def get_private_descriptor_type(type_info: TypeInfo, private_field_name: str, is_nullable: bool) -> Type: node = type_info.get(private_field_name).node if isinstance(node, Var): descriptor_type = node.type if is_nullable: descriptor_type = make_optional(descriptor_type) return descriptor_type return AnyType(TypeOfAny.unannotated)
def lookup_member_var_or_accessor(info: TypeInfo, name: str, is_lvalue: bool) -> SymbolNode: """Find the attribute/accessor node that refers to a member of a type.""" # TODO handle lvalues node = info.get(name) if node: return node.node else: return None
def lookup_member_var_or_accessor(info: TypeInfo, name: str, is_lvalue: bool) -> Optional[SymbolNode]: """Find the attribute/accessor node that refers to a member of a type.""" # TODO handle lvalues node = info.get(name) if node: return node.node else: return None
def retrieve_field_get_type(type_info: TypeInfo, private_field_name: str) -> Type: if not type_info.has_readable_member(private_field_name): return AnyType(TypeOfAny.explicit) sym = type_info.get(private_field_name) if sym and isinstance(sym.type, Instance): return sym.type return AnyType(TypeOfAny.explicit)
def get_transformed_type( cls_node: TypeInfo, ans_type: Optional[Type], prop_node: StrExpr, api: SemanticAnalyzerPluginInterface) -> Optional[Type]: transformer_name = f'_transform_{prop_node.value}' transformer = cls_node.get(transformer_name) if transformer is None: return ans_type transformer_type: Optional[Type] if isinstance(transformer.node, Decorator): transformer_type = transformer.node.func.type elif isinstance(transformer.node, FuncDef): transformer_type = transformer.node.type elif (isinstance(transformer.node, Var) and (transformer.node.is_ready or transformer.node.is_final)): if transformer.node.is_ready: transformer_type = transformer.node.type else: transformer_type = find_redef_origin(cls_node, transformer_name) if transformer_type is None: # api.fail( # f'Cannot resolve type of `{transformer_name}`', # transformer.node, code=MISC) return None else: api.fail( f'Cannot handle transformer `{transformer_name}` of type ' + transformer.node.__class__.__name__, transformer.node if transformer.node is not None else cls_node, code=MISC) return None if not isinstance(transformer_type, CallableType): api.fail(f'Cannot infer type of `{transformer_name}`', transformer.node, code=MISC) return None if len(transformer_type.arg_types) != 2: api.fail( f'Expected exactly 2 arguments for {transformer_name}:' 'self and source object', transformer.node, code=CALL_ARG) return None transformer_type = api.anal_type(transformer_type) if not isinstance(transformer_type, CallableType): return None ret_type = bind_type_var(cls_node, transformer_type.ret_type) return api.anal_type(ret_type)
def get_private_descriptor_type(type_info: TypeInfo, private_field_name: str, is_nullable: bool) -> MypyType: """ Return declared type of type_info's private_field_name (used for private Field attributes)""" sym = type_info.get(private_field_name) if sym is None: return AnyType(TypeOfAny.explicit) node = sym.node if isinstance(node, Var): descriptor_type = node.type if descriptor_type is None: return AnyType(TypeOfAny.explicit) if is_nullable: descriptor_type = make_optional(descriptor_type) return descriptor_type return AnyType(TypeOfAny.explicit)
def get_member_flags(name: str, info: TypeInfo) -> Set[int]: """Detect whether a member 'name' is settable, whether it is an instance or class variable, and whether it is class or static method. The flags are defined as following: * IS_SETTABLE: whether this attribute can be set, not set for methods and non-settable properties; * IS_CLASSVAR: set if the variable is annotated as 'x: ClassVar[t]'; * IS_CLASS_OR_STATIC: set for methods decorated with @classmethod or with @staticmethod. """ method = info.get_method(name) setattr_meth = info.get_method('__setattr__') if method: # this could be settable property if method.is_property: assert isinstance(method, OverloadedFuncDef) dec = method.items[0] assert isinstance(dec, Decorator) if dec.var.is_settable_property or setattr_meth: return {IS_SETTABLE} return set() node = info.get(name) if not node: if setattr_meth: return {IS_SETTABLE} return set() v = node.node if isinstance(v, Decorator): if v.var.is_staticmethod or v.var.is_classmethod: return {IS_CLASS_OR_STATIC} # just a variable if isinstance(v, Var): flags = {IS_SETTABLE} if v.is_classvar: flags.add(IS_CLASSVAR) return flags return set()
def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) -> ProperType: """Return the type of a type object. For a generic type G with type variables T and S the type is generally of form Callable[..., G[T, S]] where ... are argument types for the __init__/__new__ method (without the self argument). Also, the fallback type will be 'type' instead of 'function'. """ # We take the type from whichever of __init__ and __new__ is first # in the MRO, preferring __init__ if there is a tie. init_method = info.get('__init__') new_method = info.get('__new__') if not init_method or not is_valid_constructor(init_method.node): # Must be an invalid class definition. return AnyType(TypeOfAny.from_error) # There *should* always be a __new__ method except the test stubs # lack it, so just copy init_method in that situation new_method = new_method or init_method if not is_valid_constructor(new_method.node): # Must be an invalid class definition. return AnyType(TypeOfAny.from_error) # The two is_valid_constructor() checks ensure this. assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) init_index = info.mro.index(init_method.node.info) new_index = info.mro.index(new_method.node.info) fallback = info.metaclass_type or builtin_type('builtins.type') if init_index < new_index: method = init_method.node # type: Union[FuncBase, Decorator] is_new = False elif init_index > new_index: method = new_method.node is_new = True else: if init_method.node.info.fullname == 'builtins.object': # Both are defined by object. But if we've got a bogus # base class, we can't know for sure, so check for that. if info.fallback_to_any: # Construct a universal callable as the prototype. any_type = AnyType(TypeOfAny.special_form) sig = CallableType(arg_types=[any_type, any_type], arg_kinds=[ARG_STAR, ARG_STAR2], arg_names=["_args", "_kwds"], ret_type=any_type, fallback=builtin_type('builtins.function')) return class_callable(sig, info, fallback, None, is_new=False) # Otherwise prefer __init__ in a tie. It isn't clear that this # is the right thing, but __new__ caused problems with # typeshed (#5647). method = init_method.node is_new = False # Construct callable type based on signature of __init__. Adjust # return type and insert type arguments. if isinstance(method, FuncBase): t = function_type(method, fallback) else: assert isinstance(method.type, ProperType) assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this t = method.type return type_object_type_from_function(t, info, method.info, fallback, is_new)
def has_default(cls_node: TypeInfo, prop_node: StrExpr, api: SemanticAnalyzerPluginInterface) -> bool: default_name = f'_{prop_node.value}_default' return cls_node.get(default_name) is not None
def resolve_model_lookup(api: CheckerPluginInterface, model_type_info: TypeInfo, lookup: str) -> LookupNode: """Resolve a lookup on the given model.""" if lookup == 'pk': # Primary keys are special-cased primary_key_type = helpers.extract_primary_key_type_for_get( model_type_info) if primary_key_type: return FieldNode(primary_key_type) else: # No PK, use the get type for AutoField as PK type. autofield_info = api.lookup_typeinfo( 'django.db.models.fields.AutoField') pk_type = helpers.get_private_descriptor_type( autofield_info, '_pyi_private_get_type', is_nullable=False) return FieldNode(pk_type) field_name = get_actual_field_name_for_lookup_field( lookup, model_type_info) field_node = model_type_info.get(field_name) if not field_node: raise LookupException( f'When resolving lookup "{lookup}", field "{field_name}" was not found in model {model_type_info.name()}' ) if field_name.endswith('_id'): field_name_without_id = field_name.rstrip('_id') foreign_key_field = model_type_info.get(field_name_without_id) if foreign_key_field is not None and helpers.is_foreign_key( foreign_key_field.type): # Hack: If field ends with '_id' and there is a model field without the '_id' suffix, then use that field. field_node = foreign_key_field field_name = field_name_without_id field_node_type = field_node.type if field_node_type is None or not isinstance(field_node_type, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' ) if helpers.is_foreign_key(field_node_type): field_type = helpers.extract_field_getter_type(field_node_type) is_nullable = helpers.is_optional(field_type) if is_nullable: field_type = helpers.make_required(field_type) if isinstance(field_type, Instance): return RelatedModelNode(typ=field_type, is_nullable=is_nullable) else: raise LookupException( f"Not an instance for field {field_type} lookup {lookup}") field_type = helpers.extract_field_getter_type(field_node_type) if field_type: return FieldNode(typ=field_type) else: # Not a Field if field_name == 'id': # If no 'id' field was fouond, use an int return FieldNode(api.named_generic_type('builtins.int', [])) related_manager_arg = None if field_node_type.type.has_base( helpers.RELATED_MANAGER_CLASS_FULLNAME): related_manager_arg = field_node_type.args[0] if related_manager_arg is not None: # Reverse relation return RelatedModelNode(typ=related_manager_arg, is_nullable=True) raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' )
def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, mx: MemberContext, *, implicit: bool = False) -> Type: """Analyze access to an attribute via a Var node. This is conceptually part of analyze_member_access and the arguments are similar. itype is the class object in which var is defined original_type is the type of E in the expression E.var if implicit is True, the original Var was created as an assignment to self """ # Found a member variable. itype = map_instance_to_supertype(itype, var.info) typ = var.type if typ: if isinstance(typ, PartialType): return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context) if mx.is_lvalue and var.is_property and not var.is_settable_property: # TODO allow setting attributes in subclass (although it is probably an error) if info.get('__setattr__') is None: mx.msg.read_only_property(name, itype.type, mx.context) if mx.is_lvalue and var.is_classvar: mx.msg.cant_assign_to_classvar(name, mx.context) t = get_proper_type(expand_type_by_instance(typ, itype)) result = t # type: Type typ = get_proper_type(typ) if var.is_initialized_in_class and isinstance( typ, FunctionLike) and not typ.is_type_obj(): if mx.is_lvalue: if var.is_property: if not var.is_settable_property: if info.get('__setattr__') is None: mx.msg.read_only_property(name, itype.type, mx.context) else: mx.msg.cant_assign_to_method(mx.context) if not var.is_staticmethod: # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. functype = typ # Use meet to narrow original_type to the dispatched type. # For example, assume # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) # * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B) # * x: Union[A1, B1] # In `x.f`, when checking `x` against A1 we assume x is compatible with A # and similarly for B1 when checking against B dispatched_type = meet.meet_types(mx.original_type, itype) signature = freshen_function_type_vars(functype) signature = check_self_arg(signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg) signature = bind_self(signature, mx.self_type, var.is_classmethod) expanded_signature = get_proper_type( expand_type_by_instance(signature, itype)) freeze_type_vars(expanded_signature) if var.is_property: # A property cannot have an overloaded type => the cast is fine. assert isinstance(expanded_signature, CallableType) result = expanded_signature.ret_type else: result = expanded_signature else: if not var.is_ready: mx.not_ready_callback(var.name, mx.context) # Implicit 'Any' type. result = AnyType(TypeOfAny.special_form) fullname = '{}.{}'.format(var.info.fullname, name) hook = mx.chk.plugin.get_attribute_hook(fullname) if result and not mx.is_lvalue and not implicit: result = analyze_descriptor_access(mx.original_type, result, mx.builtin_type, mx.msg, mx.context, chk=mx.chk) if hook: result = hook( AttributeContext(get_proper_type(mx.original_type), result, mx.context, mx.chk)) return result
def resolve_model_lookup(api: CheckerPluginInterface, model_type_info: TypeInfo, lookup: str) -> LookupNode: """Resolve a lookup on the given model.""" if lookup == 'pk': return resolve_model_pk_lookup(api, model_type_info) field_name = get_actual_field_name_for_lookup_field( lookup, model_type_info) field_node = model_type_info.get(field_name) if not field_node: raise LookupException( f'When resolving lookup "{lookup}", field "{field_name}" was not found in model {model_type_info.name()}' ) if field_name.endswith('_id'): field_name_without_id = field_name.rstrip('_id') foreign_key_field = model_type_info.get(field_name_without_id) if foreign_key_field is not None and helpers.is_foreign_key_like( foreign_key_field.type): # Hack: If field ends with '_id' and there is a model field without the '_id' suffix, then use that field. field_node = foreign_key_field field_name = field_name_without_id field_node_type = field_node.type if field_node_type is None or not isinstance(field_node_type, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type for {model_type_info.name()}.{field_name}' ) if field_node_type.type.fullname() == 'builtins.object': # could be related manager related_manager_type = helpers.get_related_manager_type_from_metadata( model_type_info, field_name, api) if related_manager_type: model_arg = related_manager_type.args[0] if not isinstance(model_arg, Instance): raise LookupException( f'When resolving lookup "{lookup}", could not determine type ' f'for {model_type_info.name()}.{field_name}') return RelatedModelNode(typ=model_arg, is_nullable=False) if helpers.is_foreign_key_like(field_node_type): field_type = helpers.extract_field_getter_type(field_node_type) is_nullable = helpers.is_optional(field_type) if is_nullable: # type is always non-optional field_type = helpers.make_required(field_type) if isinstance(field_type, Instance): return RelatedModelNode(typ=field_type, is_nullable=is_nullable) else: raise LookupException( f"Not an instance for field {field_type} lookup {lookup}") field_type = helpers.extract_field_getter_type(field_node_type) if field_type: return FieldNode(typ=field_type) # Not a Field if field_name == 'id': # If no 'id' field was found, use an int return FieldNode(api.named_generic_type('builtins.int', [])) raise LookupException( f'When resolving lookup {lookup!r}, could not determine type for {model_type_info.name()}.{field_name}' )