Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Union return values with generics from queryset methods resolve wrong when the queryset is used on multiple models. #2343

Closed
jkaikkosplk opened this issue Aug 19, 2024 · 1 comment · Fixed by #2345
Labels
bug Something isn't working

Comments

@jkaikkosplk
Copy link
Contributor

Bug report

What's wrong

Returning a union type that includes a generic from a queryset method results with the generic getting resolved wrong on subsequent models.

-   case: handles_type_var_in_subclasses_of_subclasses_of_queryset
    main: |
        from myapp.models import MyModel, MyOtherModel
        reveal_type(MyModel.objects.example_other_mixin())  # N: Revealed type is "myapp.models.MyModel"
        reveal_type(MyModel.objects.example_union())  # N: Revealed type is "Union[myapp.models.MyModel, None]"
        reveal_type(MyOtherModel.objects.example_other_mixin())  # N: Revealed type is "myapp.models.MyOtherModel"
        reveal_type(MyOtherModel.objects.example_union())  # N: Revealed type is "Union[myapp.models.MyOtherModel, None]"
    installed_apps:
        - myapp
    files:
        -   path: myapp/__init__.py
        -   path: myapp/models.py
            content: |
              from typing import TypeVar, Generic, Optional
              from django.db import models
              from typing_extensions import Self

              T = TypeVar("T", bound=models.Model)
              T_2 = TypeVar("T_2", bound=models.Model)

              class SomeMixin:
                  def example_mixin(self, a: T) -> T: ...

              class OtherMixin(models.QuerySet[T]):
                  def example_other_mixin(self) -> T: ...
                  def example_union(self) -> Optional[T]: ...

              class _MyModelQuerySet(OtherMixin[T], models.QuerySet[T], Generic[T]):
                  def example(self) -> T: ...
                  def override(self) -> T: ...
                  def override2(self) -> T: ...
                  def dummy_override(self) -> int: ...
                  def test_sub_self(self) -> Self: ...

              class _MyModelQuerySet2(SomeMixin, _MyModelQuerySet[T_2]):
                  def example_2(self) -> T_2: ...
                  def override(self) -> T_2: ...
                  def override2(self) -> T_2: ...
                  def dummy_override(self) -> T_2: ...  # type: ignore[override]
                  def test_self(self) -> Self: ...

              class MyModelQuerySet(_MyModelQuerySet2["MyModel"]):
                  def override(self) -> "MyModel": ...

              class MyModel(models.Model):
                  objects = MyModelQuerySet.as_manager()

              class MyOtherModel(models.Model):
                  objects = _MyModelQuerySet2.as_manager()

Result:
both reveal_type(MyModel.objects.example_union()) and reveal_type(MyOtherModel.objects.example_union()) get resolved to "Union[myapp.models.MyModel, None]"

============================================================================================ FAILURES ============================================================================================
____________________________________________________________________ handles_type_var_in_subclasses_of_subclasses_of_queryset ____________________________________________________________________
/Users/jkaikko/Projects/django-stubs/tests/typecheck/managers/querysets/test_as_manager.yml:249:
E   pytest_mypy_plugins.utils.TypecheckAssertionError: Invalid output:
E   Actual:
E     ...
E     main:20: note: Revealed type is "Union[myapp.models.MyModel, None]" (diff)
E   Expected:
E     ...
E     main:20: note: Revealed type is "Union[myapp.models.MyOtherModel, None]" (diff)
E   Alignment of first line difference:
E     E: ...s "Union[myapp.models.MyOtherModel, None]"
E     A: ...s "Union[myapp.models.MyModel, None]"
E                                   ^

How is that should be

reveal_type(MyModel.objects.example_union()) should be resolve to Union[myapp.models.MyModel, None] and MyOtherModel.objects.example_union() should resolve to Union[myapp.models.MyOtherModel, None]

System information

  • OS:
  • python version: 3.9.9, 3.11.9
  • django version: 4.2, 5.0
  • mypy version: 1.11.1
  • django-stubs version: 5.0.4
  • django-stubs-ext version: 5.0.4
@jkaikkosplk jkaikkosplk added the bug Something isn't working label Aug 19, 2024
@jkaikkosplk
Copy link
Contributor Author

Aaaand right after submitting, I find #1917, which I think this is a duplicate of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
1 participant