Skip to content

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

Closed
@andersk

Description

@andersk
Contributor

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",
]

Activity

asottile

asottile commented on Aug 1, 2024

@asottile
Contributor

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

flaeppe commented on Aug 1, 2024

@flaeppe
Member

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

flaeppe commented on Aug 1, 2024

@flaeppe
Member

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.

added a commit that references this issue on Aug 3, 2024
flaeppe

flaeppe commented on Aug 6, 2024

@flaeppe
Member

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @andersk@asottile@flaeppe

      Issue actions

        [5.0.3 regression] error: "type[RealmFilter]" has no attribute "objects" [attr-defined] · Issue #2304 · typeddjango/django-stubs