Exemplo n.º 1
0
def _apply_type_to_mapped_statement(
    api: SemanticAnalyzerPluginInterface,
    stmt: AssignmentStmt,
    lvalue: NameExpr,
    left_hand_explicit_type: Optional[Union[Instance, UnionType]],
    python_type_for_type: Union[Instance, UnionType],
) -> None:
    """Apply the Mapped[<type>] annotation and right hand object to a
    declarative assignment statement.

    This converts a Python declarative class statement such as::

        class User(Base):
            # ...

            attrname = Column(Integer)

    To one that describes the final Python behavior to Mypy::

        class User(Base):
            # ...

            attrname : Mapped[Optional[int]] = <meaningless temp node>

    """
    descriptor = api.modules["sqlalchemy.orm.attributes"].names["Mapped"]

    left_node = lvalue.node

    inst = Instance(descriptor.node, [python_type_for_type])

    if left_hand_explicit_type is not None:
        left_node.type = Instance(descriptor.node, [left_hand_explicit_type])
    else:
        lvalue.is_inferred_def = False
        left_node.type = inst

    # so to have it skip the right side totally, we can do this:
    # stmt.rvalue = TempNode(AnyType(TypeOfAny.special_form))

    # however, if we instead manufacture a new node that uses the old
    # one, then we can still get type checking for the call itself,
    # e.g. the Column, relationship() call, etc.

    # rewrite the node as:
    # <attr> : Mapped[<typ>] =
    # _sa_Mapped._empty_constructor(<original CallExpr from rvalue>)
    # the original right-hand side is maintained so it gets type checked
    # internally
    api.add_symbol_table_node("_sa_Mapped", descriptor)
    column_descriptor = nodes.NameExpr("_sa_Mapped")
    column_descriptor.fullname = "sqlalchemy.orm.Mapped"
    mm = nodes.MemberExpr(column_descriptor, "_empty_constructor")
    orig_call_expr = stmt.rvalue
    stmt.rvalue = CallExpr(
        mm,
        [orig_call_expr],
        [nodes.ARG_POS],
        ["arg1"],
    )
Exemplo n.º 2
0
    def _field_from_field_def(
        self,
        stmt: nodes.AssignmentStmt,
        name: nodes.NameExpr,
        sym: nodes.SymbolTableNode,
    ):
        field = super()._field_from_field_def(stmt, name, sym)
        if field is None:
            return None
        else:
            assert isinstance(sym.node, nodes.Var)
            sym.node.is_initialized_in_class = False

            name.is_inferred_def = False

            rhs = stmt.rvalue
            if not isinstance(rhs, nodes.CastExpr):
                stmt.rvalue = nodes.CastExpr(
                    typ=field.type,
                    expr=rhs,
                )
                stmt.rvalue.line = rhs.line
                stmt.rvalue.column = rhs.column

            return field
Exemplo n.º 3
0
def apply_type_to_mapped_statement(
    api: SemanticAnalyzerPluginInterface,
    stmt: AssignmentStmt,
    lvalue: NameExpr,
    left_hand_explicit_type: Optional[ProperType],
    python_type_for_type: Optional[ProperType],
) -> None:
    """Apply the Mapped[<type>] annotation and right hand object to a
    declarative assignment statement.

    This converts a Python declarative class statement such as::

        class User(Base):
            # ...

            attrname = Column(Integer)

    To one that describes the final Python behavior to Mypy::

        class User(Base):
            # ...

            attrname : Mapped[Optional[int]] = <meaningless temp node>

    """
    left_node = lvalue.node
    assert isinstance(left_node, Var)

    if left_hand_explicit_type is not None:
        left_node.type = api.named_type(
            "__sa_Mapped", [left_hand_explicit_type]
        )
    else:
        lvalue.is_inferred_def = False
        left_node.type = api.named_type(
            "__sa_Mapped",
            [] if python_type_for_type is None else [python_type_for_type],
        )

    # so to have it skip the right side totally, we can do this:
    # stmt.rvalue = TempNode(AnyType(TypeOfAny.special_form))

    # however, if we instead manufacture a new node that uses the old
    # one, then we can still get type checking for the call itself,
    # e.g. the Column, relationship() call, etc.

    # rewrite the node as:
    # <attr> : Mapped[<typ>] =
    # _sa_Mapped._empty_constructor(<original CallExpr from rvalue>)
    # the original right-hand side is maintained so it gets type checked
    # internally
    stmt.rvalue = util.expr_to_mapped_constructor(stmt.rvalue)
Exemplo n.º 4
0
    def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
        super().visit_assignment_stmt(s)
        if isinstance(s.lvalues[0], IndexExpr):
            index = cast(IndexExpr, s.lvalues[0])
            method_type = index.method_type
            if self.dynamic_funcs[-1] or isinstance(method_type, AnyType):
                lvalue_type = AnyType()  # type: Type
            else:
                method_callable = cast(Callable, method_type)
                # TODO arg_types[1] may not be reliable
                lvalue_type = method_callable.arg_types[1]
        else:
            lvalue_type = self.get_type(s.lvalues[0])

        s.rvalue = self.coerce2(s.rvalue, lvalue_type, self.get_type(s.rvalue), self.type_context())
Exemplo n.º 5
0
    def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
        super().visit_assignment_stmt(s)
        if isinstance(s.lvalues[0], IndexExpr):
            index = cast(IndexExpr, s.lvalues[0])
            method_type = index.method_type
            if self.dynamic_funcs[-1] or isinstance(method_type, AnyType):
                lvalue_type = AnyType()  # type: Type
            else:
                method_callable = cast(Callable, method_type)
                # TODO arg_types[1] may not be reliable
                lvalue_type = method_callable.arg_types[1]
        else:
            lvalue_type = self.get_type(s.lvalues[0])

        s.rvalue = self.coerce2(s.rvalue, lvalue_type, self.get_type(s.rvalue),
                                self.type_context())