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_classmethod_to_class(api, cls, name, args, return_type, self_type=None, tvar_def=None): """Adds a new classmethod 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, Decorator): cls.defs.body.remove(sym.node) self_type = self_type or fill_typevars(info) class_type = api.class_type(self_type) function_type = builtin_type(api, 'function') args = [Argument(Var('cls'), class_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.type = set_callable_name(signature, func) func._fullname = info.fullname + '.' + name func.line = info.line func.is_class = True var = Var(name) var.line = info.line var.info = info var.is_classmethod = True # should we have a NameExpr in the decorator list? dec = Decorator(func, [], var) dec.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, dec, plugin_generated=True) info.defn.defs.body.append(dec)
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_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 visit_func_def(self, func: FuncDef) -> None: sem = self.sem func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if original_sym.kind == UNBOUND_IMPORTED: # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (in case there are conditional imports). sem.function_stack.append(func) sem.errors.push_function(func.name()) sem.enter() func.body.accept(self) sem.leave() sem.errors.pop_function() sem.function_stack.pop()
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 visit_func_def(self, func: FuncDef) -> None: sem = self.sem if sem.type is not None: # Don't process methods during pass 1. return func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if (original_sym.kind == UNBOUND_IMPORTED or isinstance(original_sym.node, ImportedName)): # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (needed in case there are unreachable # conditional imports). sem.function_stack.append(func) sem.scope.enter_function(func) sem.enter() func.body.accept(self) sem.leave() sem.scope.leave() sem.function_stack.pop()
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(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_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 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 visit_func_def(self, func: FuncDef, decorated: bool = False) -> None: """Process a func def. decorated is true if we are processing a func def in a Decorator that needs a _fullname and to have its body analyzed but does not need to be added to the symbol table. """ sem = self.sem if sem.type is not None: # Don't process methods during pass 1. return func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() and not decorated if (at_module and func.name() == '__getattr__' and self.sem.cur_mod_node.is_package_init_file() and self.sem.cur_mod_node.is_stub): if isinstance(func.type, CallableType): ret = func.type.ret_type if isinstance(ret, UnboundType) and not ret.args: sym = self.sem.lookup_qualified(ret.name, func, suppress_errors=True) # We only interpret a package as partial if the __getattr__ return type # is either types.ModuleType of Any. if sym and sym.node and sym.node.fullname() in ( 'types.ModuleType', 'typing.Any'): self.sem.cur_mod_node.is_partial_stub_package = True if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if (original_sym.kind == UNBOUND_IMPORTED or isinstance(original_sym.node, ImportedName)): # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (needed in case there are unreachable # conditional imports). sem.function_stack.append(func) sem.scope.enter_function(func) sem.enter() func.body.accept(self) sem.leave() sem.scope.leave() sem.function_stack.pop()
def visit_func_def(self, func: FuncDef, decorated: bool = False) -> None: """Process a func def. decorated is true if we are processing a func def in a Decorator that needs a _fullname and to have its body analyzed but does not need to be added to the symbol table. """ sem = self.sem if sem.type is not None: # Don't process methods during pass 1. return func.is_conditional = sem.block_depth[-1] > 0 func._fullname = sem.qualified_name(func.name()) at_module = sem.is_module_scope() and not decorated if (at_module and func.name() == '__getattr__' and self.sem.cur_mod_node.is_package_init_file() and self.sem.cur_mod_node.is_stub): if isinstance(func.type, CallableType): ret = func.type.ret_type if isinstance(ret, UnboundType) and not ret.args: sym = self.sem.lookup_qualified(ret.name, func, suppress_errors=True) # We only interpret a package as partial if the __getattr__ return type # is either types.ModuleType of Any. if sym and sym.node and sym.node.fullname() in ('types.ModuleType', 'typing.Any'): self.sem.cur_mod_node.is_partial_stub_package = True if at_module and func.name() in sem.globals: # Already defined in this module. original_sym = sem.globals[func.name()] if (original_sym.kind == UNBOUND_IMPORTED or isinstance(original_sym.node, ImportedName)): # Ah this is an imported name. We can't resolve them now, so we'll postpone # this until the main phase of semantic analysis. return if not sem.set_original_def(original_sym.node, func): # Report error. sem.check_no_global(func.name(), func) else: if at_module: sem.globals[func.name()] = SymbolTableNode(GDEF, func) # Also analyze the function body (needed in case there are unreachable # conditional imports). sem.function_stack.append(func) sem.scope.enter_function(func) sem.enter() func.body.accept(self) sem.leave() sem.scope.leave() sem.function_stack.pop()
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.visit_var(var) for var in node.args], node.arg_kinds[:], [None] * len(node.init), self.block(node.body), self.optional_type(node.type)) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_property = node.is_property new.original_def = node.original_def return new
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(FunctionLike, self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.original_def = node.original_def return new
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(FunctionLike, self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.original_def = node.original_def return new
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. new = FuncDef(node.name(), [self.visit_var(var) for var in node.args], node.arg_kinds[:], [None] * len(node.init), self.block(node.body), self.optional_type(node.type)) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_property = node.is_property new.original_def = node.original_def return new
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 visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. # These contortions are needed to handle the case of recursive # references inside the function being transformed. # Set up placeholder nodes for references within this function # to other functions defined inside it. # Don't create an entry for this function itself though, # since we want self-references to point to the original # function if this is the top-level node we are transforming. init = FuncMapInitializer(self) for stmt in node.body.body: stmt.accept(init) new = FuncDef(node.name, [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(Optional[FunctionLike], self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.is_final = node.is_final new.original_def = node.original_def if node in self.func_placeholder_map: # There is a placeholder definition for this function. Replace # the attributes of the placeholder with those form the transformed # function. We know that the classes will be identical (otherwise # this wouldn't work). result = self.func_placeholder_map[node] replace_object_state(result, new) return result else: return new
def visit_func_def(self, node: FuncDef) -> FuncDef: # Note that a FuncDef must be transformed to a FuncDef. # These contortions are needed to handle the case of recursive # references inside the function being transformed. # Set up placeholder nodes for references within this function # to other functions defined inside it. # Don't create an entry for this function itself though, # since we want self-references to point to the original # function if this is the top-level node we are transforming. init = FuncMapInitializer(self) for stmt in node.body.body: stmt.accept(init) new = FuncDef(node.name(), [self.copy_argument(arg) for arg in node.arguments], self.block(node.body), cast(Optional[FunctionLike], self.optional_type(node.type))) self.copy_function_attributes(new, node) new._fullname = node._fullname new.is_decorated = node.is_decorated new.is_conditional = node.is_conditional new.is_abstract = node.is_abstract new.is_static = node.is_static new.is_class = node.is_class new.is_property = node.is_property new.is_final = node.is_final new.original_def = node.original_def if node in self.func_placeholder_map: # There is a placeholder definition for this function. Replace # the attributes of the placeholder with those form the transformed # function. We know that the classes will be identical (otherwise # this wouldn't work). result = self.func_placeholder_map[node] replace_object_state(result, new) return result else: return new
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 _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)