def duplicate_assignment(self, node: AssignmentStmt) -> AssignmentStmt: new = AssignmentStmt(self.expressions(node.lvalues), self.expr(node.rvalue), self.optional_type(node.type)) new.line = node.line new.is_final_def = node.is_final_def return new
def visit_assignment_stmt(self, node: AssignmentStmt) -> None: node.type = node.unanalyzed_type node.is_final_def = False if self.type and not self.is_class_body: for lvalue in node.lvalues: self.process_lvalue_in_method(lvalue) super().visit_assignment_stmt(node)
def convert_arg(index: int, arg: ast27.expr) -> Var: if isinstance(arg, ast27.Name): v = arg.id elif isinstance(arg, ast27.Tuple): v = '__tuple_arg_{}'.format(index + 1) rvalue = NameExpr(v) rvalue.set_line(line) assignment = AssignmentStmt([self.visit(arg)], rvalue) assignment.set_line(line) decompose_stmts.append(assignment) else: raise RuntimeError("'{}' is not a valid argument.".format(ast27.dump(arg))) return Var(v)
def visit_Assign(self, n: ast35.Assign) -> AssignmentStmt: typ = None if hasattr(n, 'annotation') and n.annotation is not None: # type: ignore new_syntax = True else: new_syntax = False if new_syntax and self.pyversion < (3, 6): raise TypeCommentParseError( 'Variable annotation syntax is only ' 'suppoted in Python 3.6, use type ' 'comment instead', n.lineno, n.col_offset) # typed_ast prevents having both type_comment and annotation. if n.type_comment is not None: typ = parse_type_comment(n.type_comment, n.lineno) elif new_syntax: typ = TypeConverter(line=n.lineno).visit( n.annotation) # type: ignore typ.column = n.annotation.col_offset if n.value is None: # always allow 'x: int' rvalue = TempNode(AnyType()) # type: Expression else: rvalue = self.visit(n.value) lvalues = self.translate_expr_list(n.targets) return AssignmentStmt(lvalues, rvalue, type=typ, new_syntax=new_syntax)
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 copy_argument(self, argument: Argument) -> Argument: init_stmt = None # type: AssignmentStmt if argument.initialization_statement: init_lvalue = cast( NameExpr, self.node(argument.initialization_statement.lvalues[0]), ) init_lvalue.set_line(argument.line) init_stmt = AssignmentStmt( [init_lvalue], self.node(argument.initialization_statement.rvalue), self.optional_type(argument.initialization_statement.type), ) arg = Argument( self.visit_var(argument.variable), argument.type_annotation, argument.initializer, argument.kind, init_stmt, ) # Refresh lines of the inner things arg.set_line(argument.line) return arg
def make_setter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a setter wrapper for a data attribute. The setter will be of this form: . def set$name(self: C, name: typ) -> None: . self.name! = name """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) namev = scope.add(name, typ) lvalue = MemberExpr(scope.name_expr('self'), name, direct=True) rvalue = scope.name_expr(name) ret = AssignmentStmt([lvalue], rvalue) wrapper_name = 'set$' + name sig = Callable([selft, typ], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Void(), False) fdef = FuncDef(wrapper_name, [selfv, namev], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
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 visit_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt: if n.value is None: # always allow 'x: int' rvalue = TempNode(AnyType()) # type: Expression else: rvalue = self.visit(n.value) typ = TypeConverter(self.errors, line=n.lineno).visit(n.annotation) typ.column = n.annotation.col_offset return AssignmentStmt([self.visit(n.target)], rvalue, type=typ, new_syntax=True)
def visit_Assign(self, n: ast3.Assign) -> AssignmentStmt: lvalues = self.translate_expr_list(n.targets) rvalue = self.visit(n.value) if n.type_comment is not None: typ = parse_type_comment(n.type_comment, n.lineno, self.errors) else: typ = None return AssignmentStmt(lvalues, rvalue, type=typ, new_syntax=False)
def visit_Assign(self, n: ast27.Assign) -> AssignmentStmt: typ = None if n.type_comment: typ = parse_type_comment(n.type_comment, n.lineno, self.errors) return AssignmentStmt(self.translate_expr_list(n.targets), self.visit(n.value), type=typ)
def visit_Assign(self, n: ast35.Assign) -> Node: typ = None if n.type_comment: typ = parse_type_comment(n.type_comment, n.lineno) return AssignmentStmt(self.visit_list(n.targets), self.visit(n.value), type=typ)
def visit_Assign(self, n: ast27.Assign) -> AssignmentStmt: typ = None if n.type_comment: typ = parse_type_comment(n.type_comment, n.lineno, self.errors, assume_str_is_unicode=self.unicode_literals) stmt = AssignmentStmt(self.translate_expr_list(n.targets), self.visit(n.value), type=typ) return self.set_line(stmt, n)
def visit_assignment_stmt(self, node: AssignmentStmt) -> None: node.type = node.unanalyzed_type if self.type and not self.is_class_body: # TODO: Handle multiple assignment if len(node.lvalues) == 1: lvalue = node.lvalues[0] if isinstance(lvalue, MemberExpr) and lvalue.is_new_def: # Remove defined attribute from the class symbol table. If is_new_def is # true for a MemberExpr, we know that it must be an assignment through # self, since only those can define new attributes. del self.type.names[lvalue.name] super().visit_assignment_stmt(node)
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_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt: if n.value is None: # always allow 'x: int' rvalue = TempNode(AnyType(TypeOfAny.special_form), no_rhs=True) # type: Expression else: rvalue = self.visit(n.value) typ = TypeConverter(self.errors, line=n.lineno).visit(n.annotation) assert typ is not None typ.column = n.annotation.col_offset s = AssignmentStmt([self.visit(n.target)], rvalue, type=typ, new_syntax=True) return self.set_line(s, n)
def visit_assignment_stmt(self, node: AssignmentStmt) -> None: node.type = node.unanalyzed_type if node.type and self.is_class_body: # Remove attribute defined in the class body from the class namespace to avoid # bogus "Name already defined" errors. # # TODO: Handle multiple assignment, other lvalues # TODO: What about assignments without type annotations? assert len(node.lvalues) == 1 lvalue = node.lvalues[0] assert isinstance(lvalue, NameExpr) assert self.type is not None # Because self.is_class_body is True del self.type.names[lvalue.name] super().visit_assignment_stmt(node)
def make_instance_tvar_initializer(self, creat: FuncDef) -> None: """Add type variable member initialization code to a constructor. Modify the constructor body directly. """ for n in range(num_slots(creat.info)): rvalue = self.make_tvar_init_expression(creat.info, n) init = AssignmentStmt([MemberExpr(self_expr(), tvar_slot_name(n), direct=True)], rvalue) self.tf.set_type(init.lvalues[0], AnyType()) self.tf.set_type(init.rvalue, AnyType()) creat.body.body.insert(n, init)
def make_generic_wrapper_init(self, info: TypeInfo) -> FuncDef: """Build constructor of a generic wrapper class.""" nslots = num_slots(info) cdefs = [] # type: List[Node] # Build superclass constructor call. base = info.mro[1] if base.fullname() != 'builtins.object' and self.tf.is_java: s = SuperExpr('__init__') cargs = [NameExpr('__o')] # type: List[Node] for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1))) for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1, BOUND_VAR))) c = CallExpr(s, cargs, [nodes.ARG_POS] * len(cargs)) cdefs.append(ExpressionStmt(c)) # Create initialization of the wrapped object. cdefs.append(AssignmentStmt([MemberExpr( self_expr(), self.object_member_name(info), direct=True)], NameExpr('__o'))) # Build constructor arguments. args = [Var('self'), Var('__o')] init = [None, None] # type: List[Node] for alt in [False, BOUND_VAR]: for n in range(nslots): args.append(Var(tvar_arg_name(n + 1, alt))) init.append(None) nargs = nslots * 2 + 2 fdef = FuncDef('__init__', args, [nodes.ARG_POS] * nargs, init, Block(cdefs), Callable( [AnyType()] * nargs, [nodes.ARG_POS] * nargs, [None] * nargs, Void(), is_type_obj=False)) fdef.info = info self.make_wrapper_slot_initializer(fdef) return fdef
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())
def make_wrapper_slot_initializer(self, creat: FuncDef) -> None: """Add type variable member initializations to a wrapper constructor. The function must be a constructor of a generic wrapper class. Modify the constructor body directly. """ for alt in [BOUND_VAR, False]: for n in range(num_slots(creat.info)): rvalue = TypeExpr( RuntimeTypeVar(NameExpr(tvar_slot_name(n, alt)))) init = AssignmentStmt( [MemberExpr(self_expr(), tvar_slot_name(n, alt), direct=True)], rvalue) self.tf.set_type(init.lvalues[0], AnyType()) self.tf.set_type(init.rvalue, AnyType()) creat.body.body.insert(n, init)
def make_dynamic_setter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a dynamically-typed setter wrapper for a data attribute. The setter will be of this form: . def set$name*(self: C, name; Any) -> None: . self.name! = {typ name} """ lvalue = MemberExpr(self_expr(), name, direct=True) name_expr = NameExpr(name) rvalue = coerce(name_expr, typ, AnyType(), self.tf.type_context()) ret = AssignmentStmt([lvalue], rvalue) wrapper_name = 'set$' + name + self.tf.dynamic_suffix() selft = self_type(self.tf.type_context()) sig = Callable([selft, AnyType()], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Void(), False) return FuncDef(wrapper_name, [Var('self'), Var(name)], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Block([ret]), sig)
def visit_Assign(self, n: ast27.Assign) -> AssignmentStmt: typ = self.translate_type_comment(n, n.type_comment) stmt = AssignmentStmt(self.translate_expr_list(n.targets), self.visit(n.value), type=typ) return self.set_line(stmt, n)
def duplicate_assignment(self, node: AssignmentStmt) -> AssignmentStmt: new = AssignmentStmt(self.nodes(node.lvalues), self.node(node.rvalue), self.optional_type(node.type)) new.line = node.line return new
def _scan_declarative_decorator_stmt( cls: ClassDef, api: SemanticAnalyzerPluginInterface, stmt: Decorator, cls_metadata: util.DeclClassApplied, ) -> None: """Extract mapping information from a @declared_attr in a declarative class. E.g.:: @reg.mapped class MyClass: # ... @declared_attr def updated_at(cls) -> Column[DateTime]: return Column(DateTime) Will resolve in mypy as:: @reg.mapped class MyClass: # ... updated_at: Mapped[Optional[datetime.datetime]] """ for dec in stmt.decorators: if ( isinstance(dec, (NameExpr, MemberExpr, SymbolNode)) and names._type_id_for_named_node(dec) is names.DECLARED_ATTR ): break else: return dec_index = cls.defs.body.index(stmt) left_hand_explicit_type: Optional[ProperType] = None if isinstance(stmt.func.type, CallableType): func_type = stmt.func.type.ret_type if isinstance(func_type, UnboundType): type_id = names._type_id_for_unbound_type(func_type, cls, api) else: # this does not seem to occur unless the type argument is # incorrect return if ( type_id in { names.MAPPED, names.RELATIONSHIP, names.COMPOSITE_PROPERTY, names.MAPPER_PROPERTY, names.SYNONYM_PROPERTY, names.COLUMN_PROPERTY, } and func_type.args ): left_hand_explicit_type = get_proper_type(func_type.args[0]) elif type_id is names.COLUMN and func_type.args: typeengine_arg = func_type.args[0] if isinstance(typeengine_arg, UnboundType): sym = api.lookup_qualified(typeengine_arg.name, typeengine_arg) if sym is not None and isinstance(sym.node, TypeInfo): if names._has_base_type_id(sym.node, names.TYPEENGINE): left_hand_explicit_type = UnionType( [ infer._extract_python_type_from_typeengine( api, sym.node, [] ), NoneType(), ] ) else: util.fail( api, "Column type should be a TypeEngine " "subclass not '{}'".format(sym.node.fullname), func_type, ) if left_hand_explicit_type is None: # no type on the decorated function. our option here is to # dig into the function body and get the return type, but they # should just have an annotation. msg = ( "Can't infer type from @declared_attr on function '{}'; " "please specify a return type from this function that is " "one of: Mapped[<python type>], relationship[<target class>], " "Column[<TypeEngine>], MapperProperty[<python type>]" ) util.fail(api, msg.format(stmt.var.name), stmt) left_hand_explicit_type = AnyType(TypeOfAny.special_form) left_node = NameExpr(stmt.var.name) left_node.node = stmt.var # totally feeling around in the dark here as I don't totally understand # the significance of UnboundType. It seems to be something that is # not going to do what's expected when it is applied as the type of # an AssignmentStatement. So do a feeling-around-in-the-dark version # of converting it to the regular Instance/TypeInfo/UnionType structures # we see everywhere else. if isinstance(left_hand_explicit_type, UnboundType): left_hand_explicit_type = get_proper_type( util._unbound_to_instance(api, left_hand_explicit_type) ) left_node.node.type = api.named_type( "__sa_Mapped", [left_hand_explicit_type] ) # this will ignore the rvalue entirely # rvalue = TempNode(AnyType(TypeOfAny.special_form)) # rewrite the node as: # <attr> : Mapped[<typ>] = # _sa_Mapped._empty_constructor(lambda: <function body>) # the function body is maintained so it gets type checked internally column_descriptor = nodes.NameExpr("__sa_Mapped") column_descriptor.fullname = "sqlalchemy.orm.attributes.Mapped" mm = nodes.MemberExpr(column_descriptor, "_empty_constructor") arg = nodes.LambdaExpr(stmt.func.arguments, stmt.func.body) rvalue = CallExpr( mm, [arg], [nodes.ARG_POS], ["arg1"], ) new_stmt = AssignmentStmt([left_node], rvalue) new_stmt.type = left_node.node.type cls_metadata.mapped_attr_names.append( (left_node.name, left_hand_explicit_type) ) cls.defs.body[dec_index] = new_stmt
def visit_assignment_stmt(self, node: AssignmentStmt) -> None: node.type = node.unanalyzed_type super().visit_assignment_stmt(node)