Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions mypy_django_plugin/transformers/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,12 @@ 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))
# Only add PlaceholderNode if it doesn't already exist to prevent
# infinite semantic analysis iterations (fixes #2373)
if not (manager_sym and isinstance(manager_sym.node, PlaceholderNode)):
# 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

Expand Down
43 changes: 43 additions & 0 deletions tests/typecheck/managers/querysets/test_from_queryset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -999,3 +999,46 @@
field = models.CharField()
b = models.ForeignKey(B, on_delete=models.CASCADE)
objects = Manager()

# Regression test for #2373: Ensure from_queryset handles repeated deferrals
# correctly. When create_manager_info_from_from_queryset_call returns None due
# to forward references, PlaceholderNode should only be added once, not on
# every iteration.
- case: test_from_queryset_repeated_deferral_with_forward_metaclass
main: |
from typing_extensions import reveal_type
from myapp.models import MyModel
reveal_type(MyModel.objects.custom_method())
out: |
main:3: error: Access to generic instance variables via class is ambiguous [misc]
main:3: note: 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
from django.db import models
from django.db.models.manager import Manager

M = TypeVar("M", bound=models.Model, covariant=True)

# Forward reference to metaclass triggers deferral, causing
# create_manager_info_from_from_queryset_call to return None
# on initial passes. The fix ensures PlaceholderNode is not
# re-added on each iteration, preventing progress flag from
# being set repeatedly.
class CustomQuerySet(models.QuerySet[M], metaclass=ForwardMCS):
def custom_method(self) -> str:
return "test"

CustomManager = Manager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
objects = CustomManager()

# Forward-referenced metaclass defined after use
class ForwardMCS(type):
pass
Loading