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

[5.0.3 regression] error: "type[RealmFilter]" has no attribute "objects" [attr-defined] #2304

Closed
andersk opened this issue Aug 1, 2024 · 4 comments · Fixed by libretime/libretime#3062 or #2322 · May be fixed by #2280
Closed

[5.0.3 regression] error: "type[RealmFilter]" has no attribute "objects" [attr-defined] #2304

andersk opened this issue Aug 1, 2024 · 4 comments · Fixed by libretime/libretime#3062 or #2322 · May be fixed by #2280
Labels
bug Something isn't working

Comments

@andersk
Copy link
Contributor

andersk commented Aug 1, 2024

When trying to upgrade to django-stubs 5.0.3 or 5.0.4 in Zulip, I get 25 unexpected errors like error: "type[RealmFilter]" has no attribute "objects" [attr-defined]. This is not #1684; RealmFilter is a normal concrete model class. The issue seems to depend on the structure of the project’s import graph.

Based on git bisect, it first occurs at

I’ve reduced the issue to this minimized test case: https://github.com/andersk/no-attribute-objects

$ mypy .
zerver/models/__init__.py:6: error: "type[RealmFilter]" has no attribute "objects"  [attr-defined]
Found 1 error in 1 file (checked 10 source files)

pyproject.toml

[tool.django-stubs]
django_settings_module = "zproject.settings"

[tool.mypy]
plugins = ["mypy_django_plugin.main"]

confirmation/__init__.py (empty)

confirmation/models.py

from django.db import models

from zerver.models import Realm


class Confirmation(models.Model):
    realm = models.ForeignKey(Realm, on_delete=models.RESTRICT)

zerver/__init__.py (empty)

zerver/models/__init__.py

from zerver.models.linkifiers import RealmFilter as RealmFilter
from zerver.models.realms import Realm as Realm
from zerver.models.streams import Stream as Stream
from zerver.models.users import UserProfile as UserProfile

RealmFilter.objects

zerver/models/linkifiers.py

from django.db import models


class RealmFilter(models.Model):
    pass

zerver/models/realms.py

from django.db import models


class Realm(models.Model):
    pass

zerver/models/streams.py

from django.db import models

from zerver.models.realms import Realm
from zerver.models.users import UserProfile


class Stream(models.Model):
    realm = models.ForeignKey(Realm, on_delete=models.RESTRICT)
    creator = models.ForeignKey(UserProfile, on_delete=models.RESTRICT)

zerver/models/users.py

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin


class UserProfile(AbstractBaseUser, PermissionsMixin):
    pass

zproject/__init__.py (empty)

zproject/settings.py

INSTALLED_APPS = [
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "confirmation",
    "zerver",
]
@andersk andersk added the bug Something isn't working label Aug 1, 2024
@asottile
Copy link
Contributor

asottile commented Aug 1, 2024

appears to be a weird ordering thing -- #2280 will fix this probably (that or the objects removal needs to happen earlier)

what's happening is the plugin does a pass over your class -- notices the correct type exists (Manager[Self]) from models.Model and leaves it -- and then later the plugin deletes models.Model.objects

I'm surprised none of the tests hits this -- and we don't hit this at sentry because we turn off deletion of objects and friends in our fork

flaeppe added a commit to flaeppe/django-stubs that referenced this issue Aug 1, 2024
flaeppe added a commit to flaeppe/django-stubs that referenced this issue Aug 1, 2024
@flaeppe
Copy link
Member

flaeppe commented Aug 1, 2024

I couldn't get the case above to reproduce in django-stubs test suite, but I was able to check out https://github.com/andersk/no-attribute-objects and run it against #2280 successfully

@flaeppe
Copy link
Member

flaeppe commented Aug 1, 2024

I just want to reference that djangorestframework-stubs also ran in to this issue for rest_framework.authtoken.models.Token.objects(some additional info in typeddjango/djangorestframework-stubs#644). Which, in some way, means that project also has a structure with the ability to make mypy process files in an order that breaks the plugin.

flaeppe added a commit to flaeppe/django-stubs that referenced this issue Aug 3, 2024
paddatrapper added a commit to paddatrapper/libretime that referenced this issue Aug 4, 2024
paddatrapper added a commit to libretime/libretime that referenced this issue Aug 4, 2024
@flaeppe
Copy link
Member

flaeppe commented Aug 6, 2024

I realised that our plugin tries to adjust the model class from the get_customize_class_mro_hook which, per its definition, is called:

get_customize_class_mro_hook() can be used to modify class MRO (for example insert some entries there) before the class body is analyzed.

Ref: https://mypy.readthedocs.io/en/stable/extending_mypy.html#current-list-of-plugin-hooks

But the code tries to expect and look through the class body...

A fix for this is instead to move to adjust the model class via the get_metaclass_hook. Which is called before get_base_class_hook: See https://github.com/python/mypy/blob/570b90a7a368f04c64f60af339d0ac1808c49c15/mypy/semanal.py#L1998-L2010


Anyways, above is some quite specific details, in the end it's just to change the plugin to use another hook. I'll add a fix for this.

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