Skip to content

Commit 2f5a494

Browse files
authored
lookup manager type via mro (#2276)
there's a lot going on in the test but this specifically breaks lookup through `TypeVar` in mypy 1.11 (mypy 1.10 also failed in this way as well -- but was fixed in python/mypy#17381) test originally failing with: ``` /tmp/django-stubs/tests/typecheck/models/test_inheritance.yml:114: E pytest_mypy_plugins.utils.TypecheckAssertionError: Invalid output: E Actual: E myapp/models:23: error: Incompatible return value type (got "Bound", expected "T") [return-value] (diff) E Expected: E (empty) ```
1 parent c7d734a commit 2f5a494

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

mypy_django_plugin/transformers/models.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
326326

327327
incomplete_manager_defs = set()
328328
for manager_name, manager in model_cls._meta.managers_map.items():
329-
manager_node = self.model_classdef.info.names.get(manager_name, None)
329+
manager_node = self.model_classdef.info.get(manager_name)
330330
manager_fullname = helpers.get_class_fullname(manager.__class__)
331331
manager_info = self.lookup_manager(manager_fullname, manager)
332332

@@ -343,7 +343,8 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
343343
incomplete_manager_defs.add(manager_name)
344344
continue
345345

346-
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
346+
assert self.model_classdef.info.self_type is not None
347+
manager_type = Instance(manager_info, [self.model_classdef.info.self_type])
347348
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
348349

349350
if incomplete_manager_defs:
@@ -359,9 +360,10 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
359360
# setting _some_ type
360361
fallback_manager_info = self.get_or_create_manager_with_any_fallback()
361362
if fallback_manager_info is not None:
363+
assert self.model_classdef.info.self_type is not None
362364
self.add_new_node_to_model_class(
363365
manager_name,
364-
Instance(fallback_manager_info, [Instance(self.model_classdef.info, [])]),
366+
Instance(fallback_manager_info, [self.model_classdef.info.self_type]),
365367
is_classvar=True,
366368
)
367369

tests/typecheck/managers/test_managers.yml

-1
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,6 @@
543543
myapp/models:23: error: Could not resolve manager type for "myapp.models.TwoUnresolvable.objects" [django-manager-missing]
544544
myapp/models:24: error: Could not resolve manager type for "myapp.models.TwoUnresolvable.second_objects" [django-manager-missing]
545545
myapp/models:27: error: Could not resolve manager type for "myapp.models.AbstractUnresolvable.objects" [django-manager-missing]
546-
myapp/models:32: error: Could not resolve manager type for "myapp.models.InvisibleUnresolvable.objects" [django-manager-missing]
547546
myapp/models:36: note: Revealed type is "django.db.models.manager.Manager[myapp.models.User]"
548547
myapp/models:37: note: Revealed type is "django.db.models.manager.Manager[myapp.models.User]"
549548
myapp/models:39: note: Revealed type is "myapp.models.UnknownManager[myapp.models.Booking]"

tests/typecheck/models/test_inheritance.yml

+41
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,44 @@
109109
abstract = True
110110
class User(Mixin1, Mixin2):
111111
name = models.TextField()
112+
113+
- case: test_manager_typevar_through_bounds
114+
main: |
115+
from myapp.models import ResultProcessorConcrete
116+
reveal_type(ResultProcessorConcrete().f()) # N: Revealed type is "myapp.models.Concrete"
117+
installed_apps:
118+
- myapp
119+
files:
120+
- path: myapp/__init__.py
121+
- path: myapp/models.py
122+
content: |
123+
from __future__ import annotations
124+
125+
from django.db.models import Model
126+
from django.db.models.manager import Manager
127+
from typing import TypeVar, Generic, ClassVar
128+
from typing_extensions import Self
129+
130+
M = TypeVar("M", bound=Model, covariant=True)
131+
132+
class BaseManager(Manager[M]): ...
133+
134+
class Base(Model):
135+
custom_objects: ClassVar[BaseManager[Self]] = BaseManager()
136+
137+
class Bound(Base): pass
138+
139+
T = TypeVar("T", bound=Bound)
140+
141+
class ResultProcessorBase(Generic[T]):
142+
@property
143+
def model_cls(self) -> type[T]:
144+
raise NotImplementedError
145+
146+
def f(self) -> T:
147+
return self.model_cls.custom_objects.get()
148+
149+
class Concrete(Bound): pass
150+
151+
class ResultProcessorConcrete(ResultProcessorBase[Concrete]):
152+
pass

0 commit comments

Comments
 (0)