def _add_attrs_magic_attribute( ctx: 'mypy.plugin.ClassDefContext', attrs: 'List[Tuple[str, Optional[Type]]]') -> None: any_type = AnyType(TypeOfAny.explicit) attributes_types: 'List[Type]' = [ ctx.api.named_type_or_none('attr.Attribute', [attr_type or any_type]) or any_type for _, attr_type in attrs ] fallback_type = ctx.api.named_type('builtins.tuple', [ ctx.api.named_type_or_none('attr.Attribute', [any_type]) or any_type, ]) ti = ctx.api.basic_new_typeinfo(MAGIC_ATTR_CLS_NAME, fallback_type, 0) ti.is_named_tuple = True for (name, _), attr_type in zip(attrs, attributes_types): var = Var(name, attr_type) var.is_property = True proper_type = get_proper_type(attr_type) if isinstance(proper_type, Instance): var.info = proper_type.type ti.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True) attributes_type = Instance(ti, []) # TODO: refactor using `add_attribute_to_class` var = Var(name=MAGIC_ATTR_NAME, type=TupleType(attributes_types, fallback=attributes_type)) var.info = ctx.cls.info var.is_classvar = True var._fullname = f"{ctx.cls.fullname}.{MAGIC_ATTR_CLS_NAME}" ctx.cls.info.names[MAGIC_ATTR_NAME] = SymbolTableNode( kind=MDEF, node=var, plugin_generated=True, no_serialize=True, )
def datafile_class_maker_callback(ctx: ClassDefContext) -> None: # Inherit all type definitions from dataclasses DataclassTransformer(ctx).transform() # Define 'objects' as a class propery var = Var("objects", AnyType(TypeOfAny.unannotated)) var.info = ctx.cls.info var.is_property = True ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var) # Define 'datafile' as an instance property var = Var("datafile", AnyType(TypeOfAny.unannotated)) var.info = ctx.cls.info var.is_property = True ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var)
def add_magic_hook(ctx) -> None: info = ctx.cls.info str_type = ctx.api.named_type_or_none('builtins.str', []) assert str_type is not None var = Var('__magic__', str_type) var.info = info info.names['__magic__'] = SymbolTableNode(MDEF, var)
def create_new_model_parametrized_manager(self, name: str, base_manager_info: TypeInfo) -> Instance: bases = [] for original_base in base_manager_info.bases: if self.is_any_parametrized_manager(original_base): if original_base.type is None: raise helpers.IncompleteDefnException() original_base = helpers.reparametrize_instance(original_base, [Instance(self.model_classdef.info, [])]) bases.append(original_base) new_manager_info = self.add_new_class_for_current_module(name, bases) # copy fields to a new manager new_cls_def_context = ClassDefContext(cls=new_manager_info.defn, reason=self.ctx.reason, api=self.api) custom_manager_type = Instance(new_manager_info, [Instance(self.model_classdef.info, [])]) for name, sym in base_manager_info.names.items(): # replace self type with new class, if copying method if isinstance(sym.node, FuncDef): helpers.copy_method_to_another_class( new_cls_def_context, self_type=custom_manager_type, new_method_name=name, method_node=sym.node ) continue new_sym = sym.copy() if isinstance(new_sym.node, Var): new_var = Var(name, type=sym.type) new_var.info = new_manager_info new_var._fullname = new_manager_info.fullname + "." + name new_sym.node = new_var new_manager_info.names[name] = new_sym return custom_manager_type
def add_attribute_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, name: str, typ: Type, final: bool = False, ) -> None: """ Adds a new attribute to a class definition. This currently only generates the symbol table entry and no corresponding AssignmentStatement """ info = cls.info # 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] node = Var(name, typ) node.info = info node.is_final = final node._fullname = info.fullname + '.' + name info.names[name] = SymbolTableNode(MDEF, node, plugin_generated=True)
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_field(var: Var, is_initialized_in_class: bool = False, is_property: bool = False) -> None: var.info = info var.is_initialized_in_class = is_initialized_in_class var.is_property = is_property var._fullname = '%s.%s' % (info.fullname(), var.name()) info.names[var.name()] = SymbolTableNode(MDEF, var)
def add_new_class_for_module( module: MypyFile, name: str, bases: List[Instance], fields: Optional[Dict[str, MypyType]] = None, no_serialize: bool = False, ) -> TypeInfo: new_class_unique_name = checker.gen_unique_name(name, module.names) # make new class expression classdef = ClassDef(new_class_unique_name, Block([])) classdef.fullname = module.fullname + "." + new_class_unique_name # make new TypeInfo new_typeinfo = TypeInfo(SymbolTable(), classdef, module.fullname) new_typeinfo.bases = bases calculate_mro(new_typeinfo) new_typeinfo.calculate_metaclass_type() # add fields if fields: for field_name, field_type in fields.items(): var = Var(field_name, type=field_type) var.info = new_typeinfo var._fullname = new_typeinfo.fullname + "." + field_name new_typeinfo.names[field_name] = SymbolTableNode( MDEF, var, plugin_generated=True, no_serialize=no_serialize) classdef.info = new_typeinfo module.names[new_class_unique_name] = SymbolTableNode( GDEF, new_typeinfo, plugin_generated=True, no_serialize=no_serialize) return new_typeinfo
def add_new_node_to_model_class(self, name: str, typ: Instance) -> None: var = Var(name=name, type=typ) var.info = typ.type var._fullname = self.model_classdef.info.fullname() + '.' + name var.is_inferred = True var.is_initialized_in_class = True self.model_classdef.info.names[name] = SymbolTableNode(MDEF, var)
def _make_name_lvalue_var(lvalue: NameExpr, type: "mypy.types.Type", ctx: ClassDefContext) -> Var: var = Var(lvalue.name, type) var.set_line(lvalue.line) var.is_ready = True var.info = ctx.cls.info return var
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_field(var: Var, is_initialized_in_class: bool = False, is_property: bool = False) -> None: var.info = info var.is_initialized_in_class = is_initialized_in_class var.is_property = is_property var._fullname = '%s.%s' % (info.fullname, var.name) info.names[var.name] = SymbolTableNode(MDEF, var)
def add_property(cls_node: TypeInfo, ans_cls_node: TypeInfo, prop_node: Expression, api: SemanticAnalyzerPluginInterface) -> None: """Add a property.""" if not isinstance(prop_node, StrExpr): api.fail('Keyword must be a string literal', prop_node, code=VALID_TYPE) return prop_name = prop_node.value try: ans_type = ans_cls_node[prop_name].type except KeyError: api.fail( f'Attribute `{prop_name}` does not exist in ' f'{ans_cls_node.name} or its parents', prop_node, code=ATTR_DEFINED) return prop_type = get_transformed_type(cls_node, ans_type, prop_node, api) if prop_type is None: return if not has_default(cls_node, prop_node, api) and prop_type is not None: prop_type = make_optional(prop_type) new_prop = Var(prop_name, api.anal_type(prop_type)) new_prop.info = cls_node new_prop.is_initialized_in_class = True new_prop.is_property = True cls_node.names[prop_name] = SymbolTableNode(MDEF, new_prop)
def query_callback(ctx: DynamicClassDefContext) -> TypeInfo: # todo be defensive--ctx.type is Schema[Literal[fname]] #fname = ctx.arg_types[0].value query = ctx.call.args[1].value defn = ClassDef( ctx.name, defs=Block([ mpn.AssignmentStmt( lvalues=mpn.NameExpr, rvalue=None, type=ctx.api.lookup_fully_qualified_or_none('builtins.str'), new_syntax=True) ])) defn.fullname = ctx.api.qualified_name(ctx.name) names = SymbolTable() var = Var('me', ctx.api.builtin_type('builtins.str')) var.info = var.type.type var.is_property = True names['me'] = SymbolTableNode(MDEF, var, plugin_generated=True) info = TypeInfo(names=names, defn=defn, module_name=ctx.api.cur_mod_id) obj = ctx.api.builtin_type('builtins.object') info.mro = [info, obj.type] info.bases = [obj] print(ctx.name, info) ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info))
def add_field_to_new_typeinfo(var: Var, is_initialized_in_class: bool = False, is_property: bool = False) -> None: var.info = new_typeinfo var.is_initialized_in_class = is_initialized_in_class var.is_property = is_property var._fullname = new_typeinfo.fullname() + '.' + var.name() new_typeinfo.names[var.name()] = SymbolTableNode(MDEF, var)
def create_new_var(self, name: str, typ: Instance) -> Var: # type=: type of the variable itself var = Var(name=name, type=typ) # var.info: type of the object variable is bound to var.info = self.model_classdef.info var._fullname = self.model_classdef.info.fullname() + '.' + name var.is_initialized_in_class = True var.is_inferred = True return var
def add_new_sym_for_info(info: TypeInfo, *, name: str, sym_type: MypyType) -> None: # type=: type of the variable itself var = Var(name=name, type=sym_type) # var.info: type of the object variable is bound to var.info = info var._fullname = info.fullname + "." + name var.is_initialized_in_class = True var.is_inferred = True info.names[name] = SymbolTableNode(MDEF, var, plugin_generated=True)
def add_var_to_class(name: str, typ: Type, info: TypeInfo) -> None: """Add a variable with given name and type to the symbol table of a class. This also takes care about setting necessary attributes on the variable node. """ var = Var(name) var.info = info var._fullname = fullname(info) + '.' + name var.type = typ info.names[name] = SymbolTableNode(MDEF, var)
def add_new_node_to_model_class(self, name: str, typ: Instance) -> None: # type=: type of the variable itself var = Var(name=name, type=typ) # var.info: type of the object variable is bound to var.info = self.model_classdef.info var._fullname = self.model_classdef.info.fullname() + '.' + name var.is_inferred = True var.is_initialized_in_class = True self.model_classdef.info.names[name] = SymbolTableNode( MDEF, var, plugin_generated=True)
def _add_var_to_class(name: str, typ: Type, info: TypeInfo) -> None: """ Add a variable with given name and type to the symbol table of a class. This also takes care about setting necessary attributes on the variable node. """ var = Var(name) var.info = info var._fullname = f'{info.fullname}.{name}' # pylint: disable=protected-access var.type = typ info.names[name] = SymbolTableNode(MDEF, var)
def analyse_member_lvalue(self, lval): lval.accept(self) if self.is_init_method and isinstance(lval.expr, NameExpr): node = (lval.expr).node if isinstance(node, Var) and (node).is_self and lval.name not in self.type.vars: lval.is_def = True v = Var(lval.name) v.info = self.type v.is_ready = False lval.def_var = v self.type.vars[lval.name] = v
def build_enum_call_typeinfo(self, name: str, items: List[str], fullname: str) -> TypeInfo: base = self.api.named_type_or_none(fullname) assert base is not None info = self.api.basic_new_typeinfo(name, base) info.metaclass_type = info.calculate_metaclass_type() info.is_enum = True for item in items: var = Var(item) var.info = info var.is_property = True var._fullname = '{}.{}'.format(self.api.qualified_name(name), item) info.names[item] = SymbolTableNode(MDEF, var) return info
def run_with_model_cls(self, model_cls: Type[Model]) -> None: for manager_name, manager in model_cls._meta.managers_map.items(): manager_fullname = helpers.get_class_fullname(manager.__class__) manager_info = self.lookup_typeinfo_or_incomplete_defn_error( manager_fullname) if manager_name not in self.model_classdef.info.names: manager_type = Instance( manager_info, [Instance(self.model_classdef.info, [])]) self.add_new_node_to_model_class(manager_name, manager_type) else: # creates new MODELNAME_MANAGERCLASSNAME class that represents manager parametrized with current model has_manager_any_base = any( self._is_manager_any(base) for base in manager_info.bases) if has_manager_any_base: custom_model_manager_name = manager.model.__name__ + '_' + manager.__class__.__name__ bases = [] for original_base in manager_info.bases: if self._is_manager_any(original_base): if original_base.type is None: raise helpers.IncompleteDefnException() original_base = helpers.reparametrize_instance( original_base, [Instance(self.model_classdef.info, [])]) bases.append(original_base) current_module = self.api.modules[ self.model_classdef.info.module_name] custom_manager_info = helpers.add_new_class_for_module( current_module, custom_model_manager_name, bases=bases, fields=OrderedDict()) # copy fields to a new manager for name, sym in manager_info.names.items(): new_sym = sym.copy() if isinstance(new_sym.node, Var): new_var = Var(name, type=sym.type) new_var.info = custom_manager_info new_var._fullname = custom_manager_info.fullname( ) + '.' + name new_sym.node = new_var custom_manager_info.names[name] = new_sym custom_manager_type = Instance( custom_manager_info, [Instance(self.model_classdef.info, [])]) self.add_new_node_to_model_class(manager_name, custom_manager_type)
def analyse_lvalue(self, lval, nested=False, add_defs=False): if isinstance(lval, NameExpr): n = lval nested_global = not self.locals and self.block_depth > 0 and not self.type if (add_defs or nested_global) and n.name not in self.globals: # Define new global name. v = Var(n.name) v._full_name = self.qualified_name(n.name) v.is_ready = False # Type not inferred yet n.node = v n.is_def = True n.kind = GDEF n.full_name = v._full_name self.globals[n.name] = SymbolTableNode(GDEF, v, self.cur_mod_id) elif isinstance(n.node, Var) and n.is_def: v = n.node self.module_names[v.name()] = SymbolTableNode(GDEF, v, self.cur_mod_id) elif self.locals and n.name not in self.locals[-1] and n.name not in self.global_decls[-1]: # Define new local name. v = Var(n.name) n.node = v n.is_def = True n.kind = LDEF self.add_local(v, n) elif not self.locals and (self.type and n.name not in self.type.vars): # Define a new attribute. v = Var(n.name) v.info = self.type v.is_initialized_in_class = True n.node = v n.is_def = True self.type.vars[n.name] = v else: # Bind to an existing name. lval.accept(self) elif isinstance(lval, MemberExpr): if not add_defs: self.analyse_member_lvalue(lval) elif isinstance(lval, IndexExpr): if not add_defs: lval.accept(self) elif isinstance(lval, ParenExpr): self.analyse_lvalue((lval).expr, nested, add_defs) elif (isinstance(lval, TupleExpr) or isinstance(lval, ListExpr)) and not nested: items = (lval).items for i in items: self.analyse_lvalue(i, True, add_defs) else: self.fail("Invalid assignment target", lval)
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
def _make_frozen(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute]) -> None: """Turn all the attributes into properties to simulate frozen classes.""" for attribute in attributes: if attribute.name in ctx.cls.info.names: # This variable belongs to this class so we can modify it. node = ctx.cls.info.names[attribute.name].node assert isinstance(node, Var) node.is_property = True else: # This variable belongs to a super class so create new Var so we # can modify it. var = Var(attribute.name, ctx.cls.info[attribute.name].type) var.info = ctx.cls.info var._fullname = '%s.%s' % (ctx.cls.info.fullname, var.name) ctx.cls.info.names[var.name] = SymbolTableNode(MDEF, var) var.is_property = True
def _add_dataclass_fields_magic_attribute(self) -> None: attr_name = '__dataclass_fields__' any_type = AnyType(TypeOfAny.explicit) field_type = self._ctx.api.named_type_or_none('dataclasses.Field', [any_type]) or any_type attr_type = self._ctx.api.named_type('builtins.dict', [ self._ctx.api.named_type('builtins.str'), field_type, ]) var = Var(name=attr_name, type=attr_type) var.info = self._ctx.cls.info var._fullname = self._ctx.cls.info.fullname + '.' + attr_name self._ctx.cls.info.names[attr_name] = SymbolTableNode( kind=MDEF, node=var, plugin_generated=True, )
def _apply_placeholder_attr_to_class( api: SemanticAnalyzerPluginInterface, cls: ClassDef, qualified_name: str, attrname: str, ): sym = api.lookup_fully_qualified_or_none(qualified_name) if sym: assert isinstance(sym.node, TypeInfo) type_ = Instance(sym.node, []) else: type_ = AnyType(TypeOfAny.special_form) var = Var(attrname) var.info = cls.info var.type = type_ cls.info.names[attrname] = SymbolTableNode(MDEF, var)
def _make_frozen(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute]) -> None: """Turn all the attributes into properties to simulate frozen classes.""" for attribute in attributes: if attribute.name in ctx.cls.info.names: # This variable belongs to this class so we can modify it. node = ctx.cls.info.names[attribute.name].node assert isinstance(node, Var) node.is_property = True else: # This variable belongs to a super class so create new Var so we # can modify it. var = Var(attribute.name, ctx.cls.info[attribute.name].type) var.info = ctx.cls.info var._fullname = '%s.%s' % (ctx.cls.info.fullname(), var.name()) ctx.cls.info.names[var.name()] = SymbolTableNode(MDEF, var) var.is_property = True
def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext', raw_attr_types: 'List[Optional[Type]]') -> None: attr_name = '__attrs_attrs__' any_type = AnyType(TypeOfAny.explicit) attributes_types: 'List[Type]' = [ ctx.api.named_type_or_none('attr.Attribute', [attr_type or any_type]) or any_type for attr_type in raw_attr_types ] fallback_type = ctx.api.named_type('builtins.tuple', [ ctx.api.named_type_or_none('attr.Attribute', [any_type]) or any_type, ]) var = Var(name=attr_name, type=TupleType(attributes_types, fallback=fallback_type)) var.info = ctx.cls.info var._fullname = ctx.cls.info.fullname + '.' + attr_name ctx.cls.info.names[attr_name] = SymbolTableNode( kind=MDEF, node=var, plugin_generated=True, )
def load_settings_from_names(settings_classdef: ClassDef, modules: Iterable[MypyFile], api: SemanticAnalyzerPass2) -> None: settings_metadata = get_settings_metadata(settings_classdef.info) for module in modules: for name, sym in module.names.items(): if name.isupper() and isinstance(sym.node, Var): if sym.type is not None: copied = make_sym_copy_of_setting(sym) if copied is None: continue settings_classdef.info.names[name] = copied else: var = Var(name, AnyType(TypeOfAny.unannotated)) var.info = api.named_type('__builtins__.object').type settings_classdef.info.names[name] = SymbolTableNode( sym.kind, var) settings_metadata[name] = module.fullname()
def _adjust_interface_function( self, api: SemanticAnalyzerPluginInterface, class_info: TypeInfo, func_def: FuncDef, ) -> Statement: if func_def.arg_names and func_def.arg_names[0] == "self": # reveal the common mistake of leaving "self" arguments in the # interface api.fail("Interface methods should not have 'self' argument", func_def) else: selftype = Instance(class_info, [], line=class_info.line, column=class_info.column) selfarg = Argument(Var("self", None), selftype, None, ARG_POS) if isinstance(func_def.type, CallableType): func_def.type.arg_names.insert(0, "self") func_def.type.arg_kinds.insert(0, ARG_POS) func_def.type.arg_types.insert(0, selftype) func_def.arg_names.insert(0, "self") func_def.arg_kinds.insert(0, ARG_POS) func_def.arguments.insert(0, selfarg) func_def.is_abstract = True if func_def.name() in ("__getattr__", "__getattribute__"): # These special methods cannot be decorated. Likely a mypy bug. return func_def func_def.is_decorated = True var = Var(func_def.name(), func_def.type) var.is_initialized_in_class = True var.info = func_def.info var.set_line(func_def.line) decor = Decorator(func_def, [], var) return decor
def visit_var(self, v: Var) -> None: if self.current_info is not None: v.info = self.current_info if v.type is not None: v.type.accept(self.type_fixer)
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, mx: MemberContext) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(vv, TypeInfo): # If the associated variable is a TypeInfo synthesize a Var node for # the purposes of type checking. This enables us to type check things # like accessing class attributes on an inner class. v = Var(name, type=type_object_type(vv, mx.builtin_type)) v.info = info if isinstance(vv, TypeAlias) and isinstance(vv.target, Instance): # Similar to the above TypeInfo case, we allow using # qualified type aliases in runtime context if it refers to an # instance type. For example: # class C: # A = List[int] # x = C.A() <- this is OK typ = instance_alias_type(vv, mx.builtin_type) v = Var(name, type=typ) v.info = info if isinstance(v, Var): implicit = info[name].implicit # An assignment to final attribute is always an error, # independently of types. if mx.is_lvalue and not mx.chk.get_final_context(): check_final_member(name, info, mx.msg, mx.context) return analyze_var(name, v, itype, info, mx, implicit=implicit) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in [ '__getattr__', '__setattr__', '__getattribute__' ]: if not mx.is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type( method, mx.builtin_type('builtins.function')) bound_method = bind_self(function, mx.original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): result = getattr_type.ret_type # Call the attribute hook before returning. fullname = '{}.{}'.format(method.info.fullname(), name) hook = mx.chk.plugin.get_attribute_hook(fullname) if hook: result = hook( AttributeContext(mx.original_type, result, mx.context, mx.chk)) return result else: setattr_meth = info.get_method('__setattr__') if setattr_meth and setattr_meth.info.fullname( ) != 'builtins.object': setattr_func = function_type( setattr_meth, mx.builtin_type('builtins.function')) bound_type = bind_self(setattr_func, mx.original_type) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = expand_type_by_instance(bound_type, typ) if isinstance( setattr_type, CallableType) and len(setattr_type.arg_types) > 0: return setattr_type.arg_types[-1] if itype.type.fallback_to_any: return AnyType(TypeOfAny.special_form) # Could not find the member. if mx.is_super: mx.msg.undefined_in_superclass(name, mx.context) return AnyType(TypeOfAny.from_error) else: if mx.chk and mx.chk.should_suppress_optional_error([itype]): return AnyType(TypeOfAny.from_error) return mx.msg.has_no_attr(mx.original_type, itype, name, mx.context)
def visit_var(self, node: Var) -> None: node.info = self.fixup(node.info) self.fixup_type(node.type) super().visit_var(node)
def fixup_var(node: Var, replacements: Dict[SymbolNode, SymbolNode]) -> None: if node.type: node.type.accept(TypeReplaceVisitor(replacements)) node.info = cast(TypeInfo, replacements.get(node.info, node.info))
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, node: Context, is_lvalue: bool, is_super: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, original_type: Type, chk: 'mypy.checker.TypeChecker') -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(vv, TypeInfo): # If the associated variable is a TypeInfo synthesize a Var node for # the purposes of type checking. This enables us to type check things # like accessing class attributes on an inner class. v = Var(name, type=type_object_type(vv, builtin_type)) v.info = info if isinstance(v, Var): return analyze_var(name, v, itype, info, node, is_lvalue, msg, original_type, not_ready_callback, chk=chk) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif not v and name not in ['__getattr__', '__setattr__', '__getattribute__']: if not is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type(method, builtin_type('builtins.function')) bound_method = bind_self(function, original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): return getattr_type.ret_type else: setattr_meth = info.get_method('__setattr__') if setattr_meth and setattr_meth.info.fullname() != 'builtins.object': setattr_func = function_type(setattr_meth, builtin_type('builtins.function')) bound_type = bind_self(setattr_func, original_type) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = expand_type_by_instance(bound_type, typ) if isinstance(setattr_type, CallableType) and len(setattr_type.arg_types) > 0: return setattr_type.arg_types[-1] if itype.type.fallback_to_any: return AnyType(TypeOfAny.special_form) # Could not find the member. if is_super: msg.undefined_in_superclass(name, node) return AnyType(TypeOfAny.from_error) else: if chk and chk.should_suppress_optional_error([itype]): return AnyType(TypeOfAny.from_error) return msg.has_no_attr(original_type, itype, name, node)
def analyze_member_var_access(name: str, itype: Instance, info: TypeInfo, mx: MemberContext) -> Type: """Analyse attribute access that does not target a method. This is logically part of analyze_member_access and the arguments are similar. original_type is the type of E in the expression E.var """ # It was not a method. Try looking up a variable. v = lookup_member_var_or_accessor(info, name, mx.is_lvalue) vv = v if isinstance(vv, Decorator): # The associated Var node of a decorator contains the type. v = vv.var if isinstance(vv, TypeInfo): # If the associated variable is a TypeInfo synthesize a Var node for # the purposes of type checking. This enables us to type check things # like accessing class attributes on an inner class. v = Var(name, type=type_object_type(vv, mx.builtin_type)) v.info = info if isinstance(vv, TypeAlias) and isinstance(vv.target, Instance): # Similar to the above TypeInfo case, we allow using # qualified type aliases in runtime context if it refers to an # instance type. For example: # class C: # A = List[int] # x = C.A() <- this is OK typ = instance_alias_type(vv, mx.builtin_type) v = Var(name, type=typ) v.info = info if isinstance(v, Var): implicit = info[name].implicit # An assignment to final attribute is always an error, # independently of types. if mx.is_lvalue and not mx.chk.get_final_context(): check_final_member(name, info, mx.msg, mx.context) return analyze_var(name, v, itype, info, mx, implicit=implicit) elif isinstance(v, FuncDef): assert False, "Did not expect a function" elif (not v and name not in ['__getattr__', '__setattr__', '__getattribute__'] and not mx.is_operator): if not mx.is_lvalue: for method_name in ('__getattribute__', '__getattr__'): method = info.get_method(method_name) # __getattribute__ is defined on builtins.object and returns Any, so without # the guard this search will always find object.__getattribute__ and conclude # that the attribute exists if method and method.info.fullname() != 'builtins.object': function = function_type(method, mx.builtin_type('builtins.function')) bound_method = bind_self(function, mx.original_type) typ = map_instance_to_supertype(itype, method.info) getattr_type = expand_type_by_instance(bound_method, typ) if isinstance(getattr_type, CallableType): result = getattr_type.ret_type # Call the attribute hook before returning. fullname = '{}.{}'.format(method.info.fullname(), name) hook = mx.chk.plugin.get_attribute_hook(fullname) if hook: result = hook(AttributeContext(mx.original_type, result, mx.context, mx.chk)) return result else: setattr_meth = info.get_method('__setattr__') if setattr_meth and setattr_meth.info.fullname() != 'builtins.object': setattr_func = function_type(setattr_meth, mx.builtin_type('builtins.function')) bound_type = bind_self(setattr_func, mx.original_type) typ = map_instance_to_supertype(itype, setattr_meth.info) setattr_type = expand_type_by_instance(bound_type, typ) if isinstance(setattr_type, CallableType) and len(setattr_type.arg_types) > 0: return setattr_type.arg_types[-1] if itype.type.fallback_to_any: return AnyType(TypeOfAny.special_form) # Could not find the member. if mx.is_super: mx.msg.undefined_in_superclass(name, mx.context) return AnyType(TypeOfAny.from_error) else: if mx.chk and mx.chk.should_suppress_optional_error([itype]): return AnyType(TypeOfAny.from_error) return mx.msg.has_no_attr(mx.original_type, itype, name, mx.context)