def add_method(funcname: str, ret: Type, args: List[Argument], name: Optional[str] = None, is_classmethod: bool = False, is_new: bool = False, ) -> None: if is_classmethod or is_new: first = [Argument(Var('cls'), TypeType.make_normalized(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] items = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) signature.variables = [tvd] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + funcname if is_classmethod: v = Var(funcname, func.type) v.is_classmethod = True v.info = info v._fullname = func._fullname dec = Decorator(func, [NameExpr('classmethod')], v) info.names[funcname] = SymbolTableNode(MDEF, dec) else: info.names[funcname] = SymbolTableNode(MDEF, func)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info self_type = self_type or fill_typevars(info) function_type = ctx.api.named_type('__builtins__.function') args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name()) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + name func.line = info.line info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def make_setter_wrapper(self, name, typ): """Create a setter wrapper for a data attribute. The setter will be of this form: . void set$name(C self, typ name): . 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 analyze_interface_base(classdef_ctx: ClassDefContext) -> None: # Create fake constructor to mimic adaptation signature info = classdef_ctx.cls.info api = classdef_ctx.api if "__init__" in info.names: # already patched return # Create a method: # # def __init__(self, obj, alternate=None) -> None # # This will make interfaces selftp = Instance(info, []) anytp = AnyType(TypeOfAny.implementation_artifact) init_fn = CallableType( arg_types=[selftp, anytp, anytp], arg_kinds=[ARG_POS, ARG_POS, ARG_OPT], arg_names=["self", "obj", "alternate"], ret_type=NoneTyp(), fallback=api.named_type("function"), ) newinit = FuncDef("__init__", [], Block([]), init_fn) newinit.info = info info.names["__init__"] = SymbolTableNode(MDEF, newinit, plugin_generated=True)
def visit_func_def(self, node: FuncDef) -> None: node = self.fixup(node) if node.type: self.fixup_type(node.type) if node.info: node.info = self.fixup(node.info) super().visit_func_def(node)
def add_static_method( ctx, function_name: str, args: ty.List[Argument], return_type: Type ) -> None: """Mostly copied from mypy.plugins.common, with changes to make it work for a static method.""" info = ctx.cls.info function_type = ctx.api.named_type("__builtins__.function") arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(nameit(arg.variable)) arg_kinds.append(arg.kind) signature = CallableType( arg_types, arg_kinds, arg_names, return_type, function_type ) func = FuncDef(function_name, args, Block([PassStmt()])) func.is_static = True func.info = info func.type = set_callable_name(signature, func) func._fullname = fullname(info) + "." + function_name func.line = info.line info.names[function_name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def add_method(funcname: str, ret: Type, args: List[Argument], name: Optional[str] = None, is_classmethod: bool = False, is_new: bool = False, ) -> None: if is_classmethod or is_new: first = [Argument(Var('cls'), TypeType.make_normalized(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] items = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) signature.variables = [tvd] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + funcname if is_classmethod: v = Var(funcname, func.type) v.is_classmethod = True v.info = info v._fullname = func._fullname dec = Decorator(func, [NameExpr('classmethod')], v) info.names[funcname] = SymbolTableNode(MDEF, dec) else: info.names[funcname] = SymbolTableNode(MDEF, func)
def add_method(self, method_name: str, args: List[Argument], ret_type: Type, self_type: Optional[Type] = None, tvd: Optional[TypeVarDef] = None) -> None: """Add a method: def <method_name>(self, <args>) -> <ret_type>): ... to info. self_type: The type to use for the self argument or None to use the inferred self type. tvd: If the method is generic these should be the type variables. """ from mypy.semanal import set_callable_name self_type = self_type if self_type is not None else self.self_type args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types = [arg.type_annotation for arg in args] arg_names = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in arg_types signature = CallableType(cast(List[Type], arg_types), arg_kinds, arg_names, ret_type, self.function_type) if tvd: signature.variables = [tvd] func = FuncDef(method_name, args, Block([PassStmt()])) func.info = self.info func.type = set_callable_name(signature, func) func._fullname = self.info.fullname() + '.' + method_name func.line = self.info.line self.info.names[method_name] = SymbolTableNode(MDEF, func) # Add the created methods to the body so that they can get further semantic analysis. # e.g. Forward Reference Resolution. self.info.defn.defs.body.append(func)
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 add_method(self, method_name: str, args: List[Argument], ret_type: Type, self_type: Optional[Type] = None, tvd: Optional[TypeVarDef] = None) -> None: """Add a method: def <method_name>(self, <args>) -> <ret_type>): ... to info. self_type: The type to use for the self argument or None to use the inferred self type. tvd: If the method is generic these should be the type variables. """ from mypy.semanal import set_callable_name self_type = self_type if self_type is not None else self.self_type args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types = [arg.type_annotation for arg in args] arg_names = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in arg_types signature = CallableType(cast(List[Type], arg_types), arg_kinds, arg_names, ret_type, self.function_type) if tvd: signature.variables = [tvd] func = FuncDef(method_name, args, Block([PassStmt()])) func.info = self.info func.type = set_callable_name(signature, func) func._fullname = self.info.fullname() + '.' + method_name func.line = self.info.line self.info.names[method_name] = SymbolTableNode(MDEF, func) # Add the created methods to the body so that they can get further semantic analysis. # e.g. Forward Reference Resolution. self.info.defn.defs.body.append(func)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info self_type = self_type or fill_typevars(info) function_type = ctx.api.named_type('__builtins__.function') args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name()) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + name func.line = info.line info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def add_static_method_to_class( api: Union[SemanticAnalyzerPluginInterface, CheckerPluginInterface], cls: ClassDef, name: str, args: List[Argument], return_type: Type, tvar_def: Optional[TypeVarType] = None, ) -> None: """Adds a static method Edited add_method_to_class to incorporate static method logic https://github.com/python/mypy/blob/9c05d3d19/mypy/plugins/common.py """ info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) # For compat with mypy < 0.93 if MypyVersion.VERSION < Decimal("0.93"): function_type = api.named_type("__builtins__.function") # type: ignore else: if isinstance(api, SemanticAnalyzerPluginInterface): function_type = api.named_type("builtins.function") else: function_type = api.named_generic_type("builtins.function", []) arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.is_static = True func.info = info func.type = set_callable_name(signature, func) func._fullname = f"{info.fullname}.{name}" func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer) for arg in func.arguments: if arg.type_annotation is not None: arg.type_annotation.accept(self.type_fixer)
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer) for arg in func.arguments: if arg.type_annotation is not None: arg.type_annotation.accept(self.type_fixer)
def add_method_to_class( api: Union[SemanticAnalyzerPluginInterface, CheckerPluginInterface], cls: ClassDef, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarType] = None, ) -> None: """Adds a new method to a class definition.""" info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) # TODO: semanal.py and checker.py seem to have subtly different implementations of # named_type/named_generic_type (starting with the fact that we have to use different names # for builtins), so it's easier to just check which one we're dealing with here and pick the # correct function to use than to try to add a named_type method that behaves the same for # both. We should probably combine those implementations at some point. if isinstance(api, SemanticAnalyzerPluginInterface): function_type = api.named_type('__builtins__.function') else: function_type = api.named_generic_type('builtins.function', []) args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def add_method_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new method to a class definition. """ info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) function_type = api.named_type('__builtins__.function') args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
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 transform_method_implementation(self, fdef, name): """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds typ = function_type(fdef) init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2
def transform_method_implementation(self, fdef: FuncDef, name: str) -> FuncDef: """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds typ = function_type(fdef) # type: Type init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2
def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo: info = self.api.basic_new_typeinfo(name, base_type) info.is_newtype = True # Add __init__ method args = [Argument(Var('self'), NoneType(), None, ARG_POS), self.make_argument('item', old_type)] signature = CallableType( arg_types=[Instance(info, []), old_type], arg_kinds=[arg.kind for arg in args], arg_names=['self', 'item'], ret_type=NoneType(), fallback=self.api.named_type('__builtins__.function'), name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info init_func._fullname = info.fullname + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info
def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo: info = self.api.basic_new_typeinfo(name, base_type) info.is_newtype = True # Add __init__ method args = [Argument(Var('self'), NoneTyp(), None, ARG_POS), self.make_argument('item', old_type)] signature = CallableType( arg_types=[Instance(info, []), old_type], arg_kinds=[arg.kind for arg in args], arg_names=['self', 'item'], ret_type=NoneTyp(), fallback=self.api.named_type('__builtins__.function'), name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info init_func._fullname = self.api.qualified_name(name) + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info
def make_getter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a getter wrapper for a data attribute. The getter will be of this form: . def $name*(self: C) -> type: . return self.name! """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) member_expr = MemberExpr(scope.name_expr('self'), name, direct=True) ret = ReturnStmt(member_expr) wrapper_name = '$' + name sig = Callable([selft], [nodes.ARG_POS], [None], typ, False) fdef = FuncDef(wrapper_name, [selfv], [nodes.ARG_POS], [None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
def make_getter_wrapper(self, name, typ): """Create a getter wrapper for a data attribute. The getter will be of this form: . int $name*(C self): . return self.name! """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) member_expr = MemberExpr(scope.name_expr('self'), name, direct=True) ret = ReturnStmt(member_expr) wrapper_name = '$' + name sig = Callable([selft], [nodes.ARG_POS], [None], typ, False) fdef = FuncDef(wrapper_name, [selfv], [nodes.ARG_POS], [None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer)
def visit_func_def(self, func: FuncDef) -> None: if self.current_info is not None: func.info = self.current_info if func.type is not None: func.type.accept(self.type_fixer)
return res FuncDef transform_method_implementation(self, FuncDef fdef, str name): """Transform the implementation of a method (i.e. unwrapped).""" args = fdef.args arg_kinds = fdef.arg_kinds Type typ = function_type(fdef) init = fdef.init_expressions() if fdef.name() == '__init__' and is_generic(fdef): args, arg_kinds, init, typ = self.add_constructor_tvar_args( fdef, typ, args, arg_kinds, init) fdef2 = FuncDef(name, args, arg_kinds, init, fdef.body, typ) fdef2.info = fdef.info self.tf.prepend_generic_function_tvar_args(fdef2) return fdef2 tuple<Var[], int[], Node[], Type> \ add_constructor_tvar_args( self, FuncDef fdef, Type typ, Var[] args, int[] arg_kinds, Node[] init): """Add type variable arguments for __init__ of a generic type. Return tuple (new args, new kinds, new inits). """ Var[] tv = []
def _add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: mypy.types.Type, self_type: Optional[mypy.types.Type] = None, tvar_def: Optional[mypy.types.TypeVarDef] = None, is_classmethod: bool = False, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in new semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): ctx.cls.defs.body.remove(sym.node) if is_classmethod: first = Argument( Var('cls'), # Working around python/mypy#5416. # This should be: mypy.types.TypeType.make_normalized(self_type) mypy.types.AnyType(mypy.types.TypeOfAny.implementation_artifact), None, ARG_POS) else: self_type = self_type or fill_typevars(info) first = Argument(Var('self'), self_type, None, ARG_POS) args = [first] + args function_type = ctx.api.named_type('__builtins__.function') arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(get_name(arg.variable)) arg_kinds.append(arg.kind) signature = mypy.types.CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = get_fullname(info) + '.' + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] info.defn.defs.body.append(func) info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, is_classmethod: bool = False, is_new: bool = False, # is_staticmethod: bool = False, ) -> None: """ Adds a new method to a class. This can be dropped if/when https://github.com/python/mypy/issues/7301 is merged """ info = ctx.cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): ctx.cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) if is_classmethod or is_new: first = [ Argument(Var("_cls"), TypeType.make_normalized(self_type), None, ARG_POS) ] # elif is_staticmethod: # first = [] else: self_type = self_type or fill_typevars(info) first = [Argument(Var("self"), self_type, None, ARG_POS)] args = first + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(get_name(arg.variable)) arg_kinds.append(arg.kind) function_type = ctx.api.named_type("__builtins__.function") signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func.is_class = is_classmethod # func.is_static = is_staticmethod func._fullname = get_fullname(info) + "." + name func.line = info.line # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] if is_classmethod: # or is_staticmethod: func.is_decorated = True v = Var(name, func.type) v.info = info v._fullname = func._fullname # if is_classmethod: v.is_classmethod = True dec = Decorator(func, [NameExpr("classmethod")], v) # else: # v.is_staticmethod = True # dec = Decorator(func, [NameExpr('staticmethod')], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) else: sym = SymbolTableNode(MDEF, func) sym.plugin_generated = True info.names[name] = sym info.defn.defs.body.append(func)
super_init = (FuncDef)info.base.get_method('__init__') # Build argument list. args = [Var('self')] for i in range(1, len(super_init.args)): args.append(Var(super_init.args[i].name())) args[-1].type = callee_type.arg_types[i - 1] selft = self_type(self.tf.type_context()) callee_type = prepend_arg_type(callee_type, selft) creat = FuncDef('__init__', args, super_init.arg_kinds, <Node> [None] * len(args), Block([])) creat.info = tdef.info creat.type = callee_type creat.is_implicit = False tdef.info.methods['__init__'] = creat # Insert a call to superclass constructor. If the # superclass is object, the constructor does nothing => # omit the call. if tdef.info.base.full_name() != 'builtins.object': creat.body.body.append( self.make_superclass_constructor_call(tdef.info, callee_type)) # Implicit cast from FuncDef[] to Node[] is safe below. return (any)self.func_tf.transform_method(creat) else:
def make_init_wrapper(self, tdef): """Make and return an implicit __init__ if class needs it. Otherwise, return an empty list. We include an implicit __init__ if the class is generic or if it extends a generic class and if it does not define __init__. The __init__ of a generic class requires one or more extra type variable arguments. The inherited __init__ may not accept these. For example, assume these definitions: . class A<T>: pass . class B(A<int>): pass The constructor for B will be (equivalent to) . void __init__(B self): . self.__tv = <int> . super().__init__(<int>) """ # FIX overloading, default args / varargs, keyword args info = tdef.info if '__init__' not in info.methods and ( tdef.is_generic() or (info.base and info.base.is_generic())): # Generic class with no explicit __init__ method # (i.e. __init__ inherited from superclass). Generate a # wrapper that initializes type variable slots and calls # the superclass __init__ method. selftype = self_type(info) callee_type = analyse_member_access( '__init__', selftype, None, False, True, None, None, info.base) # Now the callee type may contain the type variables of a # grandparent as bound type variables, but we want the # type variables of the parent class. Explicitly set the # bound type variables. callee_type = self.fix_bound_init_tvars(callee_type, map_instance_to_supertype(selftype, info.base)) super_init = info.base.get_method('__init__') # Build argument list. args = [Var('self')] for i in range(1, len(super_init.args)): args.append(Var(super_init.args[i].name())) args[-1].type = callee_type.arg_types[i - 1] selft = self_type(self.tf.type_context()) callee_type = prepend_arg_type(callee_type, selft) creat = FuncDef('__init__', args, super_init.arg_kinds, [None] * len(args), Block([])) creat.info = tdef.info creat.type = callee_type creat.is_implicit = False tdef.info.methods['__init__'] = creat # Insert a call to superclass constructor. If the # superclass is object, the constructor does nothing => # omit the call. if tdef.info.base.full_name() != 'builtins.object': creat.body.body.append( self.make_superclass_constructor_call(tdef.info, callee_type)) # Implicit cast from FuncDef[] to Node[] is safe below. return self.func_tf.transform_method(creat) else: return []
def add_method_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, is_classmethod: bool = False, ) -> None: """ Adds a new method to a class definition. NOTE: Copied from mypy/plugins/common.py and extended with support for adding classmethods based on https://github.com/python/mypy/pull/7796 """ info = cls.info # First remove any previously generated methods with the same name # to avoid clashes and problems in the semantic analyzer. if name in info.names: sym = info.names[name] if sym.plugin_generated and isinstance(sym.node, FuncDef): cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) # Add either self or cls as the first argument if is_classmethod: first = Argument(Var("cls"), TypeType.make_normalized(self_type), None, ARG_POS) else: first = Argument(Var("self"), self_type, None, ARG_POS) args = [first] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, "All arguments must be fully typed." arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name) arg_kinds.append(arg.kind) function_type = api.named_type("__builtins__.function") signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname + "." + name # pylint: disable=protected-access func.line = info.line func.is_class = is_classmethod # NOTE: we would like the plugin generated node to dominate, but we still # need to keep any existing definitions so they get semantically analyzed. if name in info.names: # Get a nice unique name instead. r_name = get_unique_redefinition_name(name, info.names) info.names[r_name] = info.names[name] if is_classmethod: func.is_decorated = True v = Var(name, func.type) v.info = info v._fullname = func._fullname # pylint: disable=protected-access v.is_classmethod = True dec = Decorator(func, [NameExpr("classmethod")], v) dec.line = info.line sym = SymbolTableNode(MDEF, dec) else: sym = SymbolTableNode(MDEF, func) sym.plugin_generated = True info.names[name] = sym info.defn.defs.body.append(func)
def make_init_wrapper(self, tdef: ClassDef) -> List[Node]: """Make and return an implicit __init__ if class needs it. Otherwise, return an empty list. We include an implicit __init__ if the class is generic or if it extends a generic class and if it does not define __init__. The __init__ of a generic class requires one or more extra type variable arguments. The inherited __init__ may not accept these. For example, assume these definitions: . class A(Generic[T]): pass . class B(A[int]): pass The constructor for B will be (equivalent to) . def __init__(self: B) -> None: . self.__tv = <int> . super().__init__(<int>) """ # FIX overloading, default args / varargs, keyword args info = tdef.info if '__init__' not in info.names and ( tdef.is_generic() or (info.bases and info.mro[1].is_generic())): # Generic class with no explicit __init__ method # (i.e. __init__ inherited from superclass). Generate a # wrapper that initializes type variable slots and calls # the superclass __init__ method. base = info.mro[1] selftype = self_type(info) callee_type = cast(Callable, analyse_member_access( '__init__', selftype, None, False, True, None, None, base)) # Now the callee type may contain the type variables of a # grandparent as bound type variables, but we want the # type variables of the parent class. Explicitly set the # bound type variables. callee_type = self.fix_bound_init_tvars(callee_type, map_instance_to_supertype(selftype, base)) super_init = cast(FuncDef, base.get_method('__init__')) # Build argument list. args = [Var('self')] for i in range(1, len(super_init.args)): args.append(Var(super_init.args[i].name())) args[-1].type = callee_type.arg_types[i - 1] selft = self_type(self.tf.type_context()) callee_type = prepend_arg_type(callee_type, selft) creat = FuncDef('__init__', args, super_init.arg_kinds, [None] * len(args), Block([])) creat.info = tdef.info creat.type = callee_type creat.is_implicit = False tdef.info.names['__init__'] = SymbolTableNode(MDEF, creat, typ=creat.type) # Insert a call to superclass constructor. If the # superclass is object, the constructor does nothing => # omit the call. if base.fullname() != 'builtins.object': creat.body.body.append( self.make_superclass_constructor_call(tdef.info, callee_type)) # Implicit cast from FuncDef[] to Node[] is safe below. return Any(self.func_tf.transform_method(creat)) else: return []