From e7bd5f98bd5b018c97d8cfb975f9af66948832b1 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 21 Jun 2024 15:45:50 -0400 Subject: [PATCH 1/2] add PlaceholderNode when deferring manager class hook --- mypy_django_plugin/transformers/managers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy_django_plugin/transformers/managers.py b/mypy_django_plugin/transformers/managers.py index 2da8e50de..e4be536a2 100644 --- a/mypy_django_plugin/transformers/managers.py +++ b/mypy_django_plugin/transformers/managers.py @@ -10,6 +10,7 @@ MemberExpr, Node, OverloadedFuncDef, + PlaceholderNode, RefExpr, StrExpr, SymbolTableNode, @@ -317,6 +318,9 @@ def create_new_manager_class_from_from_queryset_method(ctx: DynamicClassDefConte new_manager_info = create_manager_info_from_from_queryset_call(semanal_api, ctx.call, ctx.name) if new_manager_info is None: if not ctx.api.final_iteration: + # XXX: hack for python/mypy#17402 + ph = PlaceholderNode(ctx.api.qualified_name(ctx.name), ctx.call, ctx.call.line, becomes_typeinfo=True) + ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, ph)) ctx.api.defer() return From 857f98e6257aa700967bce3c4f2f26722d2a6c52 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 7 Jul 2024 21:57:22 -0400 Subject: [PATCH 2/2] add test demonstrating deferral a lot of inspiration to force deferral from https://github.com/python/mypy/blob/4e3346ee1dee83868adc2411c4a0f4050cf2f95a/test-data/unit/check-newsemanal.test#L1145 --- .../managers/querysets/test_from_queryset.yml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/typecheck/managers/querysets/test_from_queryset.yml b/tests/typecheck/managers/querysets/test_from_queryset.yml index 98e1c9b46..cb1da33b6 100644 --- a/tests/typecheck/managers/querysets/test_from_queryset.yml +++ b/tests/typecheck/managers/querysets/test_from_queryset.yml @@ -847,6 +847,41 @@ self.ttl = ttl super().__init__(*a, **k) +- case: test_from_queryset_with_deferral + main: | + from myapp.models import Concrete + reveal_type(Concrete.objects.qs_meth()) # N: Revealed type is "builtins.int" + reveal_type(Concrete.objects.manager_meth()) # N: Revealed type is "builtins.str" + mypy_config: | + [mypy.plugins.django-stubs] + django_settings_module = myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from typing import TypeVar, ClassVar + from typing_extensions import Self + from django.db import models + from django.db.models.manager import Manager + + M = TypeVar("M", bound=models.Model, covariant=True) + + # bogus forward-`metaclass=` to trigger second semanal pass similar to #2224 + class CustomQuerySet(models.QuerySet[M], metaclass=MCS): + def qs_meth(self) -> int: + return 1 + + _base = Manager.from_queryset(CustomQuerySet) + + class CustomBase(_base[M]): + def manager_meth(self) -> str: + return 'hi' + + class Concrete(models.Model): + objects: ClassVar[CustomBase[Self]] = CustomBase() + + class MCS(type): pass + - case: test_queryset_arg_as_unsupported_expressions main: | from typing import Union, Generic, TypeVar