Skip to content

Commit

Permalink
lookup manager type via mro (typeddjango#2276)
Browse files Browse the repository at this point in the history
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)
```
  • Loading branch information
asottile authored Jul 25, 2024
1 parent c7d734a commit 2f5a494
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 4 deletions.
8 changes: 5 additions & 3 deletions mypy_django_plugin/transformers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:

incomplete_manager_defs = set()
for manager_name, manager in model_cls._meta.managers_map.items():
manager_node = self.model_classdef.info.names.get(manager_name, None)
manager_node = self.model_classdef.info.get(manager_name)
manager_fullname = helpers.get_class_fullname(manager.__class__)
manager_info = self.lookup_manager(manager_fullname, manager)

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

manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
assert self.model_classdef.info.self_type is not None
manager_type = Instance(manager_info, [self.model_classdef.info.self_type])
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)

if incomplete_manager_defs:
Expand All @@ -359,9 +360,10 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
# setting _some_ type
fallback_manager_info = self.get_or_create_manager_with_any_fallback()
if fallback_manager_info is not None:
assert self.model_classdef.info.self_type is not None
self.add_new_node_to_model_class(
manager_name,
Instance(fallback_manager_info, [Instance(self.model_classdef.info, [])]),
Instance(fallback_manager_info, [self.model_classdef.info.self_type]),
is_classvar=True,
)

Expand Down
1 change: 0 additions & 1 deletion tests/typecheck/managers/test_managers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,6 @@
myapp/models:23: error: Could not resolve manager type for "myapp.models.TwoUnresolvable.objects" [django-manager-missing]
myapp/models:24: error: Could not resolve manager type for "myapp.models.TwoUnresolvable.second_objects" [django-manager-missing]
myapp/models:27: error: Could not resolve manager type for "myapp.models.AbstractUnresolvable.objects" [django-manager-missing]
myapp/models:32: error: Could not resolve manager type for "myapp.models.InvisibleUnresolvable.objects" [django-manager-missing]
myapp/models:36: note: Revealed type is "django.db.models.manager.Manager[myapp.models.User]"
myapp/models:37: note: Revealed type is "django.db.models.manager.Manager[myapp.models.User]"
myapp/models:39: note: Revealed type is "myapp.models.UnknownManager[myapp.models.Booking]"
Expand Down
41 changes: 41 additions & 0 deletions tests/typecheck/models/test_inheritance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,44 @@
abstract = True
class User(Mixin1, Mixin2):
name = models.TextField()
- case: test_manager_typevar_through_bounds
main: |
from myapp.models import ResultProcessorConcrete
reveal_type(ResultProcessorConcrete().f()) # N: Revealed type is "myapp.models.Concrete"
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from __future__ import annotations
from django.db.models import Model
from django.db.models.manager import Manager
from typing import TypeVar, Generic, ClassVar
from typing_extensions import Self
M = TypeVar("M", bound=Model, covariant=True)
class BaseManager(Manager[M]): ...
class Base(Model):
custom_objects: ClassVar[BaseManager[Self]] = BaseManager()
class Bound(Base): pass
T = TypeVar("T", bound=Bound)
class ResultProcessorBase(Generic[T]):
@property
def model_cls(self) -> type[T]:
raise NotImplementedError
def f(self) -> T:
return self.model_cls.custom_objects.get()
class Concrete(Bound): pass
class ResultProcessorConcrete(ResultProcessorBase[Concrete]):
pass

0 comments on commit 2f5a494

Please sign in to comment.