示例#1
0
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)
示例#2
0
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, [])
示例#3
0
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)])
示例#4
0
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
示例#5
0
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, []))
示例#6
0
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)
示例#7
0
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:],
    )
示例#8
0
 def lookup_typeinfo(self, fullname: str) -> Optional[TypeInfo]:
     return helpers.lookup_fully_qualified_typeinfo(self.api, fullname)