Skip to content

Commit ef501f2

Browse files
authored
Don't populate a type arg for non generic managers (#2314)
1 parent 2f4c99a commit ef501f2

File tree

6 files changed

+118
-22
lines changed

6 files changed

+118
-22
lines changed

mypy_django_plugin/lib/helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,3 +533,7 @@ def get_model_from_expression(
533533
if model_info is not None:
534534
return Instance(model_info, [])
535535
return None
536+
537+
538+
def fill_manager(manager: TypeInfo, typ: MypyType) -> Instance:
539+
return Instance(manager, [typ] if manager.is_generic() else [])

mypy_django_plugin/transformers/models.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def reparametrize_dynamically_created_manager(self, manager_name: str, manager_i
318318

319319
assert manager_info is not None
320320
# Reparameterize dynamically created manager with model type
321-
manager_type = Instance(manager_info, [Instance(self.model_classdef.info, [])])
321+
manager_type = helpers.fill_manager(manager_info, Instance(self.model_classdef.info, []))
322322
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
323323

324324
def run_with_model_cls(self, model_cls: Type[Model]) -> None:
@@ -344,7 +344,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
344344
continue
345345

346346
assert self.model_classdef.info.self_type is not None
347-
manager_type = Instance(manager_info, [self.model_classdef.info.self_type])
347+
manager_type = helpers.fill_manager(manager_info, self.model_classdef.info.self_type)
348348
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
349349

350350
if incomplete_manager_defs:
@@ -361,11 +361,8 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
361361
fallback_manager_info = self.get_or_create_manager_with_any_fallback()
362362
if fallback_manager_info is not None:
363363
assert self.model_classdef.info.self_type is not None
364-
self.add_new_node_to_model_class(
365-
manager_name,
366-
Instance(fallback_manager_info, [self.model_classdef.info.self_type]),
367-
is_classvar=True,
368-
)
364+
manager_type = helpers.fill_manager(fallback_manager_info, self.model_classdef.info.self_type)
365+
self.add_new_node_to_model_class(manager_name, manager_type, is_classvar=True)
369366

370367
# Find expression for e.g. `objects = SomeManager()`
371368
manager_expr = self.get_manager_expression(manager_name)
@@ -445,7 +442,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
445442
return None
446443
default_manager_info = generated_manager_info
447444

448-
default_manager = Instance(default_manager_info, [Instance(self.model_classdef.info, [])])
445+
default_manager = helpers.fill_manager(default_manager_info, Instance(self.model_classdef.info, []))
449446
self.add_new_node_to_model_class("_default_manager", default_manager, is_classvar=True)
450447

451448

mypy_django_plugin/transformers/querysets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def determine_proper_manager_type(ctx: FunctionContext) -> MypyType:
4848
outer_model_info is None
4949
or not outer_model_info.has_base(fullnames.MODEL_CLASS_FULLNAME)
5050
or outer_model_info.self_type is None
51+
or not default_return_type.type.is_generic()
5152
):
5253
return default_return_type
5354

tests/typecheck/managers/querysets/test_from_queryset.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,14 +583,14 @@
583583
- case: test_queryset_in_model_class_body
584584
main: |
585585
from myapp.models import MyModel
586-
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
587-
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
586+
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
587+
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
588588
reveal_type(MyModel.objects.all) # N: Revealed type is "def () -> myapp.models.MyQuerySet"
589589
reveal_type(MyModel.objects.custom) # N: Revealed type is "def () -> myapp.models.MyQuerySet"
590590
reveal_type(MyModel.objects.all().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
591591
reveal_type(MyModel.objects.custom().filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
592-
reveal_type(MyModel.objects2) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
593-
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet[myapp.models.MyModel]"
592+
reveal_type(MyModel.objects2) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
593+
reveal_type(MyModel._default_manager) # N: Revealed type is "myapp.models.MyManagerFromMyQuerySet"
594594
installed_apps:
595595
- myapp
596596
files:
@@ -613,7 +613,10 @@
613613
- case: test_queryset_in_model_class_body_subclass
614614
main: |
615615
from myapp.models import MyModel
616-
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.BaseManagerFromBaseQuerySet[myapp.models.MyModel]"
616+
reveal_type(MyModel.objects) # N: Revealed type is "myapp.models.BaseManagerFromBaseQuerySet"
617+
reveal_type(MyModel.objects.get()) # N: Revealed type is "myapp.models.BaseModel"
618+
reveal_type(MyModel.objects.filter()) # N: Revealed type is "myapp.models.BaseQuerySet"
619+
reveal_type(MyModel.objects.filter().get()) # N: Revealed type is "myapp.models.BaseModel"
617620
installed_apps:
618621
- myapp
619622
files:

tests/typecheck/managers/test_managers.yml

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
- case: test_leave_as_is_if_objects_is_set_and_fill_typevars_with_outer_class
127127
main: |
128128
from myapp.models import MyUser
129-
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.UserManager[myapp.models.MyUser]"
129+
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.UserManager"
130130
reveal_type(MyUser.objects.get()) # N: Revealed type is "myapp.models.MyUser"
131131
reveal_type(MyUser.objects.get_or_404()) # N: Revealed type is "myapp.models.MyUser"
132132
installed_apps:
@@ -169,9 +169,9 @@
169169
main: |
170170
from myapp.models import AbstractPerson, Book
171171
reveal_type(AbstractPerson.abstract_persons) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.AbstractPerson]"
172-
reveal_type(Book.published_objects) # N: Revealed type is "myapp.models.PublishedBookManager[myapp.models.Book]"
172+
reveal_type(Book.published_objects) # N: Revealed type is "myapp.models.PublishedBookManager"
173173
Book.published_objects.create(title='hello')
174-
reveal_type(Book.annotated_objects) # N: Revealed type is "myapp.models.AnnotatedBookManager[myapp.models.Book]"
174+
reveal_type(Book.annotated_objects) # N: Revealed type is "myapp.models.AnnotatedBookManager"
175175
Book.annotated_objects.create(title='hello')
176176
installed_apps:
177177
- myapp
@@ -200,10 +200,10 @@
200200
reveal_type(AbstractBase1.manager1)
201201
reveal_type(AbstractBase2.restricted)
202202
out: |
203-
main:2: note: Revealed type is "myapp.models.CustomManager1[myapp.models.Child]"
204-
main:3: note: Revealed type is "myapp.models.CustomManager2[myapp.models.Child]"
205-
main:4: note: Revealed type is "myapp.models.CustomManager1[myapp.models.AbstractBase1]"
206-
main:5: note: Revealed type is "myapp.models.CustomManager2[myapp.models.AbstractBase2]"
203+
main:2: note: Revealed type is "myapp.models.CustomManager1"
204+
main:3: note: Revealed type is "myapp.models.CustomManager2"
205+
main:4: note: Revealed type is "myapp.models.CustomManager1"
206+
main:5: note: Revealed type is "myapp.models.CustomManager2"
207207
installed_apps:
208208
- myapp
209209
files:
@@ -229,6 +229,44 @@
229229
class Child(AbstractBase1, AbstractBase2):
230230
pass
231231
232+
- case: managers_inherited_from_abstract_classes_multiple_inheritance_with_generic
233+
main: |
234+
from myapp.models import AbstractBase1, AbstractBase2, Child
235+
reveal_type(Child.manager1) # N: Revealed type is "myapp.models.CustomManager1[myapp.models.Child]"
236+
reveal_type(Child.manager1.get()) # N: Revealed type is "myapp.models.Child"
237+
reveal_type(Child.restricted) # N: Revealed type is "myapp.models.CustomManager2[myapp.models.Child]"
238+
reveal_type(Child.restricted.get()) # N: Revealed type is "myapp.models.Child"
239+
reveal_type(AbstractBase1.manager1) # N: Revealed type is "myapp.models.CustomManager1[myapp.models.AbstractBase1]"
240+
reveal_type(AbstractBase1.manager1.get()) # N: Revealed type is "myapp.models.AbstractBase1"
241+
reveal_type(AbstractBase2.restricted) # N: Revealed type is "myapp.models.CustomManager2[myapp.models.AbstractBase2]"
242+
reveal_type(AbstractBase2.restricted.get()) # N: Revealed type is "myapp.models.AbstractBase2"
243+
installed_apps:
244+
- myapp
245+
files:
246+
- path: myapp/__init__.py
247+
- path: myapp/models.py
248+
content: |
249+
from typing import Generic, TypeVar
250+
from django.db import models
251+
252+
T1 = TypeVar("T1", bound="AbstractBase1")
253+
class CustomManager1(models.Manager[T1], Generic[T1]): ...
254+
class AbstractBase1(models.Model):
255+
class Meta:
256+
abstract = True
257+
name = models.CharField(max_length=50)
258+
manager1 = CustomManager1()
259+
260+
T2 = TypeVar("T2", bound="AbstractBase2")
261+
class CustomManager2(models.Manager[T2], Generic[T2]): ...
262+
class AbstractBase2(models.Model):
263+
class Meta:
264+
abstract = True
265+
value = models.CharField(max_length=50)
266+
restricted = CustomManager2()
267+
268+
class Child(AbstractBase1, AbstractBase2): ...
269+
232270
- case: model_has_a_manager_of_the_same_type
233271
main: |
234272
from myapp.models import UnrelatedModel, MyModel
@@ -638,10 +676,12 @@
638676
main: |
639677
from myapp.models import MyModel
640678
reveal_type(MyModel.objects)
679+
reveal_type(MyModel.objects.get())
641680
installed_apps:
642681
- myapp
643682
out: |
644-
main:2: note: Revealed type is "myapp.models.MyModel.MyManager[myapp.models.MyModel]"
683+
main:2: note: Revealed type is "myapp.models.MyModel.MyManager"
684+
main:3: note: Revealed type is "Any"
645685
files:
646686
- path: myapp/__init__.py
647687
- path: myapp/models.py
@@ -670,3 +710,54 @@
670710
from django.db import models
671711
672712
class MyModel(models.Model): ...
713+
714+
- case: test_does_not_populate_an_unexpected_type_argument
715+
main: |
716+
from myapp.models import MyModel
717+
reveal_type(MyModel.populated_manager) # N: Revealed type is "myapp.models.PopulatedManager"
718+
reveal_type(MyModel.populated_manager.get()) # N: Revealed type is "myapp.models.MyModel"
719+
reveal_type(MyModel.populated_manager.filter()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyModel, myapp.models.MyModel]"
720+
721+
reveal_type(MyModel.populated_manager_from_generic_queryset) # N: Revealed type is "myapp.models.PopulatedManagerFromQuerySet"
722+
reveal_type(MyModel.populated_manager_from_generic_queryset.get()) # N: Revealed type is "myapp.models.MyModel"
723+
reveal_type(MyModel.populated_manager_from_generic_queryset.filter()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyModel, myapp.models.MyModel]"
724+
725+
reveal_type(MyModel.populated_manager_from_populated_queryset) # N: Revealed type is "myapp.models.PopulatedManagerFromPopulatedQuerySet"
726+
reveal_type(MyModel.populated_manager_from_populated_queryset.get()) # N: Revealed type is "myapp.models.MyModel"
727+
reveal_type(MyModel.populated_manager_from_populated_queryset.filter()) # N: Revealed type is "myapp.models.PopulatedQuerySet"
728+
729+
reveal_type(MyModel.generic_manager) # N: Revealed type is "django.db.models.manager.Manager[myapp.models.MyModel]"
730+
reveal_type(MyModel.generic_manager.get()) # N: Revealed type is "myapp.models.MyModel"
731+
reveal_type(MyModel.generic_manager.filter()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyModel, myapp.models.MyModel]"
732+
733+
reveal_type(MyModel.generic_manager_from_generic_queryset) # N: Revealed type is "myapp.models.ManagerFromQuerySet[myapp.models.MyModel]"
734+
reveal_type(MyModel.generic_manager_from_generic_queryset.get()) # N: Revealed type is "myapp.models.MyModel"
735+
reveal_type(MyModel.generic_manager_from_generic_queryset.filter()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyModel, myapp.models.MyModel]"
736+
737+
reveal_type(MyModel.generic_manager_from_populated_queryset) # N: Revealed type is "myapp.models.ManagerFromPopulatedQuerySet[myapp.models.MyModel]"
738+
reveal_type(MyModel.generic_manager_from_populated_queryset.get()) # N: Revealed type is "myapp.models.MyModel"
739+
reveal_type(MyModel.generic_manager_from_populated_queryset.filter()) # N: Revealed type is "myapp.models.PopulatedQuerySet"
740+
741+
installed_apps:
742+
- myapp
743+
files:
744+
- path: myapp/__init__.py
745+
- path: myapp/models.py
746+
content: |
747+
from django.db import models
748+
749+
class PopulatedManager(models.Manager["MyModel"]): ...
750+
class PopulatedQuerySet(models.QuerySet["MyModel"]): ...
751+
752+
PopulatedManagerFromGenericQuerySet = PopulatedManager.from_queryset(models.QuerySet)
753+
PopulatedManagerFromPopulatedQuerySet = PopulatedManager.from_queryset(PopulatedQuerySet)
754+
GenericManagerFromGenericQuerySet = models.Manager.from_queryset(models.QuerySet)
755+
GenericManagerFromPopulatedQuerySet = models.Manager.from_queryset(PopulatedQuerySet)
756+
757+
class MyModel(models.Model):
758+
populated_manager = PopulatedManager()
759+
populated_manager_from_generic_queryset = PopulatedManagerFromGenericQuerySet()
760+
populated_manager_from_populated_queryset = PopulatedManagerFromPopulatedQuerySet()
761+
generic_manager = models.Manager()
762+
generic_manager_from_generic_queryset = GenericManagerFromGenericQuerySet()
763+
generic_manager_from_populated_queryset = GenericManagerFromPopulatedQuerySet()

tests/typecheck/models/test_contrib_models.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
- case: can_override_abstract_user_manager
3939
main: |
4040
from myapp.models import MyBaseUser, MyUser
41-
reveal_type(MyBaseUser.objects) # N: Revealed type is "myapp.models.MyBaseUserManager[myapp.models.MyBaseUser]"
41+
reveal_type(MyBaseUser.objects) # N: Revealed type is "myapp.models.MyBaseUserManager"
4242
reveal_type(MyBaseUser.objects.all()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyBaseUser, myapp.models.MyBaseUser]"
4343
reveal_type(MyUser.objects) # N: Revealed type is "myapp.models.MyUserManager"
4444
reveal_type(MyUser.objects.all()) # N: Revealed type is "django.db.models.query.QuerySet[myapp.models.MyUser, myapp.models.MyUser]"

0 commit comments

Comments
 (0)