File: manytoone.py

package info (click to toggle)
python-django-stubs 5.2.9-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,832 kB
  • sloc: python: 5,185; makefile: 15; sh: 8
file content (51 lines) | stat: -rw-r--r-- 2,007 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from mypy.plugin import MethodContext
from mypy.types import Instance, get_proper_type
from mypy.types import Type as MypyType

from mypy_django_plugin.lib import fullnames, helpers


def get_model_of_related_manager(ctx: MethodContext) -> Instance | None:
    """
    Returns the type parameter (_To) instance of a `RelatedManager` instance when it's a
    model. Otherwise `None` is returned.

    For example: if given a `RelatedManager[A]` where `A` is a model then `A` is
    returned.
    """
    default_return_type = get_proper_type(ctx.default_return_type)
    if (
        isinstance(default_return_type, Instance)
        and default_return_type.type.fullname == fullnames.RELATED_MANAGER_CLASS
    ):
        # This is a call to '__get__' overload with a model instance of
        # 'ReverseManyToOneDescriptor'. Returning a 'RelatedManager'. Which we want to,
        # just like Django, build from the default manager of the related model.
        related_manager = default_return_type
        # Require first type argument of 'RelatedManager' to be a model
        if (
            len(related_manager.args) >= 1
            and isinstance((_To := get_proper_type(related_manager.args[0])), Instance)
            and helpers.is_model_type(_To.type)
        ):
            return _To

    return None


def refine_many_to_one_related_manager(ctx: MethodContext) -> MypyType:
    """
    Updates the 'RelatedManager' returned by e.g. 'ReverseManyToOneDescriptor' to be a subclass
    of 'RelatedManager' and the related model's default manager.
    """
    to_model_instance = get_model_of_related_manager(ctx)
    if to_model_instance is None:
        return ctx.default_return_type

    checker = helpers.get_typechecker_api(ctx)
    related_manager_info = helpers.get_reverse_manager_info(
        checker, to_model_instance.type, derived_from="_default_manager"
    )
    if related_manager_info is None:
        return ctx.default_return_type
    return Instance(related_manager_info, [])