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"], )
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
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)
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())