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 copy_method_to_another_class( ctx: ClassDefContext, self_type: Instance, new_method_name: str, method_node: FuncDef ) -> None: semanal_api = get_semanal_api(ctx) if method_node.type is None: if not semanal_api.final_iteration: semanal_api.defer() return arguments, return_type = build_unannotated_method_args(method_node) add_method(ctx, new_method_name, args=arguments, return_type=return_type, self_type=self_type) return method_type = method_node.type if not isinstance(method_type, CallableType): if not semanal_api.final_iteration: semanal_api.defer() return arguments = [] bound_return_type = semanal_api.anal_type(method_type.ret_type, allow_placeholder=True) assert bound_return_type is not None if isinstance(bound_return_type, PlaceholderNode): return try: original_arguments = method_node.arguments[1:] except AttributeError: original_arguments = [] for arg_name, arg_type, original_argument in zip( method_type.arg_names[1:], method_type.arg_types[1:], original_arguments ): bound_arg_type = semanal_api.anal_type(arg_type, allow_placeholder=True) if bound_arg_type is None and not semanal_api.final_iteration: semanal_api.defer() return assert bound_arg_type is not None if isinstance(bound_arg_type, PlaceholderNode): return var = Var(name=original_argument.variable.name, type=arg_type) var.line = original_argument.variable.line var.column = original_argument.variable.column argument = Argument( variable=var, type_annotation=bound_arg_type, initializer=original_argument.initializer, kind=original_argument.kind, ) argument.set_line(original_argument) arguments.append(argument) add_method(ctx, new_method_name, args=arguments, return_type=bound_return_type, self_type=self_type)
def visit_var(self, node: Var) -> Var: # Note that a Var must be transformed to a Var. if node in self.var_map: return self.var_map[node] new = Var(node.name(), self.optional_type(node.type)) new.line = node.line new._fullname = node._fullname new.info = node.info new.is_self = node.is_self new.is_ready = node.is_ready new.is_initialized_in_class = node.is_initialized_in_class new.is_staticmethod = node.is_staticmethod new.is_property = node.is_property new.set_line(node.line) self.var_map[node] = new return new