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

Remove some unnecessary defer rounds #2252

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

flaeppe
Copy link
Member

@flaeppe flaeppe commented Jul 5, 2024

Change so that we don't trigger unnecessary defer rounds during manager creation via the from_queryset method call.

Refs:

Comment on lines +544 to +562
myapp/models:39: note: Revealed type is "django.db.models.manager.Manager[Any]"
myapp/models:40: note: Revealed type is "django.db.models.manager.Manager[myapp.models.Booking]"
myapp/models:42: note: Revealed type is "myapp.models.UnknownManager[myapp.models.TwoUnresolvable]"
myapp/models:43: note: Revealed type is "myapp.models.UnknownManager[myapp.models.TwoUnresolvable]"
myapp/models:42: note: Revealed type is "django.db.models.manager.Manager[Any]"
myapp/models:43: note: Revealed type is "django.db.models.manager.Manager[Any]"
myapp/models:44: note: Revealed type is "django.db.models.manager.Manager[myapp.models.TwoUnresolvable]"
myapp/models:46: note: Revealed type is "myapp.models.UnknownManager[myapp.models.InvisibleUnresolvable]"
myapp/models:46: note: Revealed type is "django.db.models.manager.Manager[Any]"
myapp/models:47: note: Revealed type is "django.db.models.manager.Manager[myapp.models.InvisibleUnresolvable]"
myapp/models:49: note: Revealed type is "django.db.models.fields.related_descriptors.RelatedManager[myapp.models.Booking]"
myapp/models:50: note: Revealed type is "django.db.models.fields.related_descriptors.RelatedManager[myapp.models.Booking]"
myapp/models:53: note: Revealed type is "def () -> myapp.models.UnknownQuerySet[myapp.models.Booking, myapp.models.Booking]"
myapp/models:53: note: Revealed type is "def () -> django.db.models.query.QuerySet[Any, Any]"
myapp/models:54: error: "Manager[Any]" has no attribute "custom" [attr-defined]
myapp/models:54: note: Revealed type is "Any"
myapp/models:55: note: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.UnknownQuerySet[myapp.models.Booking, myapp.models.Booking]"
myapp/models:55: note: Revealed type is "def (*args: Any, **kwargs: Any) -> django.db.models.query.QuerySet[Any, Any]"
myapp/models:56: error: "QuerySet[Any, Any]" has no attribute "custom" [attr-defined]
myapp/models:56: note: Revealed type is "Any"
myapp/models:57: note: Revealed type is "Union[myapp.models.Booking, None]"
myapp/models:58: note: Revealed type is "myapp.models.Booking"
myapp/models:59: note: Revealed type is "builtins.list[myapp.models.Booking]"
myapp/models:60: note: Revealed type is "builtins.list[myapp.models.Booking]"
myapp/models:57: note: Revealed type is "Union[Any, None]"
myapp/models:58: note: Revealed type is "Any"
myapp/models:59: note: Revealed type is "builtins.list[Any]"
myapp/models:60: note: Revealed type is "builtins.list[Any]"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to argue that the changes here improves correctness. Since the manager in the test comes from a function that specifies a manager as return type:

def LocalManager() -> models.Manager:
    """
    Returns a manager instance of an inlined manager type that can't
    be resolved.
    """
    class InnerManager(models.Manager): ...
    return InnerManager()

This (now) assigns a type models.Manager[Any], the return type of the function. I would say that the previous behaviour did too much, since it tried to express that assigned an invalid manager. But to know that it had to draw conclusions from the function body and thus "override" the return type.

The plugin should now only consider calls of the format: objects = <Manager>.from_queryset(<QuerySet>)() or objects = <QuerySet>.as_manager() in this case

Change so that we don't trigger unnecessary defer rounds during manager
creation via the `from_queryset` method call.
@flaeppe
Copy link
Member Author

flaeppe commented Jul 13, 2024

To clarify a little bit more; previously the plugin could tell mypy to defer on e.g. a case like this:

def not_a_manager() -> Any: ...

class SomeModel(models.Model):
    objects = not_a_manager()

Deferring isn't gonna make it a more of a manager. The changed code looks for a more narrow/specific case that we want and can handle that comes from from_queryset. For any other situation we now just continue without deferring.

@flaeppe
Copy link
Member Author

flaeppe commented Jul 15, 2024

@sobolevn, @intgr would any of you guys mind checking this out? I'm suspecting it could improve a couple of errors in #1023 (though I have no proof that it will)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

1 participant