def get_method_type_from_reverse_manager( api: TypeChecker, method_name: str, manager_type_info: TypeInfo) -> Optional[ProperType]: """ Attempts to resolve a reverse manager's method via the '_default_manager' manager on the related model From Django docs: "By default the RelatedManager used for reverse relations is a subclass of the default manager for that model." Ref: https://docs.djangoproject.com/en/dev/topics/db/queries/#using-a-custom-reverse-manager """ is_reverse_manager = ("django" in manager_type_info.metadata and "related_manager_to_model" in manager_type_info.metadata["django"]) if not is_reverse_manager: return None related_model_fullname = manager_type_info.metadata["django"][ "related_manager_to_model"] assert isinstance(related_model_fullname, str) model_info = helpers.lookup_fully_qualified_typeinfo( api, related_model_fullname) if model_info is None: return None # We should _always_ have a '_default_manager' on a model assert "_default_manager" in model_info.names assert isinstance(model_info.names["_default_manager"].node, Var) manager_instance = model_info.names["_default_manager"].node.type return (get_method_type_from_dynamic_manager(api, method_name, manager_instance.type) # TODO: Can we assert on None and Instance? if manager_instance is not None and isinstance(manager_instance, Instance) else None)
def get_type_of_settings_attribute(ctx: AttributeContext, django_context: DjangoContext) -> MypyType: assert isinstance(ctx.context, MemberExpr) setting_name = ctx.context.name if not hasattr(django_context.settings, setting_name): ctx.api.fail(f"'Settings' object has no attribute {setting_name!r}", ctx.context) return ctx.default_attr_type typechecker_api = helpers.get_typechecker_api(ctx) # first look for the setting in the project settings file, then global settings settings_module = typechecker_api.modules.get( django_context.django_settings_module) global_settings_module = typechecker_api.modules.get( 'django.conf.global_settings') for module in [settings_module, global_settings_module]: if module is not None: sym = module.names.get(setting_name) if sym is not None and sym.type is not None: return sym.type # if by any reason it isn't present there, get type from django settings value = getattr(django_context.settings, setting_name) value_fullname = helpers.get_class_fullname(value.__class__) value_info = helpers.lookup_fully_qualified_typeinfo( typechecker_api, value_fullname) if value_info is None: return ctx.default_attr_type return Instance(value_info, [])
def _get_field_instance(ctx: MethodContext, field_fullname: str) -> MypyType: field_info = helpers.lookup_fully_qualified_typeinfo( helpers.get_typechecker_api(ctx), field_fullname) if field_info is None: return AnyType(TypeOfAny.unannotated) return Instance(field_info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)])
def get_or_create_annotated_type( api: Union[SemanticAnalyzer, CheckerPluginInterface], model_type: Instance, fields_dict: Optional[TypedDictType]) -> Instance: """ Get or create the type for a model for which you getting/setting any attr is allowed. The generated type is an subclass of the model and django._AnyAttrAllowed. The generated type is placed in the django_stubs_ext module, with the name WithAnnotations[ModelName]. If the user wanted to annotate their code using this type, then this is the annotation they would use. This is a bit of a hack to make a pretty type for error messages and which would make sense for users. """ model_module_name = "django_stubs_ext" if helpers.is_annotated_model_fullname(model_type.type.fullname): # If it's already a generated class, we want to use the original model as a base model_type = model_type.type.bases[0] if fields_dict is not None: type_name = f"WithAnnotations[{model_type.type.fullname.replace('.', '__')}, {fields_dict}]" else: type_name = f"WithAnnotations[{model_type.type.fullname.replace('.', '__')}]" annotated_typeinfo = helpers.lookup_fully_qualified_typeinfo( cast(TypeChecker, api), model_module_name + "." + type_name) if annotated_typeinfo is None: model_module_file = api.modules[model_module_name] # type: ignore if isinstance(api, SemanticAnalyzer): annotated_model_type = api.named_type_or_none( ANY_ATTR_ALLOWED_CLASS_FULLNAME, []) assert annotated_model_type is not None else: annotated_model_type = api.named_generic_type( ANY_ATTR_ALLOWED_CLASS_FULLNAME, []) annotated_typeinfo = add_new_class_for_module( model_module_file, type_name, bases=[model_type] if fields_dict is not None else [model_type, annotated_model_type], fields=fields_dict.items if fields_dict is not None else None, no_serialize=True, ) if fields_dict is not None: # To allow structural subtyping, make it a Protocol annotated_typeinfo.is_protocol = True # Save for later to easily find which field types were annotated annotated_typeinfo.metadata[ "annotated_field_types"] = fields_dict.items annotated_type = Instance(annotated_typeinfo, []) return annotated_type
def get_user_model_hook(ctx: FunctionContext, django_context: DjangoContext) -> MypyType: auth_user_model = django_context.settings.AUTH_USER_MODEL model_cls = django_context.apps_registry.get_model(auth_user_model) model_cls_fullname = helpers.get_class_fullname(model_cls) model_info = helpers.lookup_fully_qualified_typeinfo( helpers.get_typechecker_api(ctx), model_cls_fullname) if model_info is None: return AnyType(TypeOfAny.unannotated) return TypeType(Instance(model_info, []))
def set_first_generic_param_as_default_for_second(ctx: AnalyzeTypeContext, fullname: str) -> MypyType: type_analyser_api = cast(TypeAnalyser, ctx.api) info = helpers.lookup_fully_qualified_typeinfo(type_analyser_api.api, fullname) # type: ignore assert isinstance(info, TypeInfo) if not ctx.type.args: return Instance(info, [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)]) args = ctx.type.args if len(args) == 1: args = [args[0], args[0]] analyzed_args = [type_analyser_api.analyze_type(arg) for arg in args] return Instance(info, analyzed_args)
def get_method_type_from_dynamic_manager( api: TypeChecker, method_name: str, manager_type_info: TypeInfo) -> Optional[ProperType]: """ Attempt to resolve a method on a manager that was built from '.from_queryset' """ if ("django" not in manager_type_info.metadata or "from_queryset_manager" not in manager_type_info.metadata["django"]): # Manager isn't dynamically added return None queryset_fullname = manager_type_info.metadata["django"][ "from_queryset_manager"] assert isinstance(queryset_fullname, str) queryset_info = helpers.lookup_fully_qualified_typeinfo( api, queryset_fullname) assert queryset_info is not None def get_funcdef_type( definition: Union[FuncBase, Decorator, None]) -> Optional[ProperType]: # TODO: Handle @overload? if isinstance(definition, FuncBase) and not isinstance( definition, OverloadedFuncDef): return definition.type elif isinstance(definition, Decorator): return definition.func.type return None method_type = get_funcdef_type(queryset_info.get_method(method_name)) if method_type is None: return None assert isinstance(method_type, CallableType) # Drop any 'self' argument as our manager is already initialized return method_type.copy_modified( arg_types=method_type.arg_types[1:], arg_kinds=method_type.arg_kinds[1:], arg_names=method_type.arg_names[1:], )
def lookup_typeinfo(self, fullname: str) -> Optional[TypeInfo]: return helpers.lookup_fully_qualified_typeinfo(self.api, fullname)