Skip to content

fix Django: Instance vs Class access of attributes should return different types #4080 - #4336

Open
asukaminato0721 wants to merge 1 commit into
meta:mainfrom
asukaminato0721:4080
Open

asukaminato0721 wants to merge 1 commit into
meta:mainfrom
asukaminato0721:4080

Conversation

@asukaminato0721

@asukaminato0721 asukaminato0721 commented Jul 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #4080

Django fields now preserve descriptor semantics for class access while retaining inferred instance types, _ST/_GT parameters are specialized from Django stubs.

Test Plan

add test

@meta-codesync

meta-codesync Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This pull request has been imported. If you are a Meta employee, you can view this in D114011430. (Because this pull request was imported automatically, there will not be any future comments.)

@codspeed

codspeed Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 16 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing asukaminato0721:4080 (847c989) with main (8a65019)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown

Diff from mypy_primer, showing the effect of this PR on open source code:

============================================================
SUMMARY
============================================================
Total: +18 new errors, -4 fixed errors

Projects with changes (2):
  django-stubs: +2 -0
  zulip: +16 -4
============================================================

FULL DIFF DETAILS
------------------------------------------------------------

django-stubs (https://github.com/typeddjango/django-stubs)
+ ERROR django-stubs/contrib/auth/models.pyi:115:5-14: Class member `AbstractUser.is_active` overrides parent class `AbstractBaseUser` in an inconsistent manner [bad-override]
+ ERROR django-stubs/contrib/auth/models.pyi:115:5-14: Class member `AbstractUser.is_active` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]

zulip (https://github.com/zulip/zulip)
+ ERROR corporate/lib/stripe.py:1450:22-66: `-` is not supported between `int` and `None` [unsupported-operation]
+ ERROR zerver/actions/data_import.py:147:17-150:18: The type of this argument is unknown [unknown-argument-type]
- ERROR zerver/actions/data_import.py:156:17-85: The type of this argument is unknown [unknown-argument-type]
+ ERROR zerver/lib/import_realm.py:2208:25-63: The type of this argument is unknown [unknown-argument-type]
+ ERROR zerver/lib/realm_description.py:39:12-38: Returned type `str | None` is not assignable to declared return type `str` [bad-return]
- ERROR zerver/lib/send_email.py:547:5-549:15: Result of call expression is of type `tuple[int, dict[str, int]]` and is not used; assign to `_` if this is intentional [unused-call-result]
+ ERROR zerver/lib/send_email.py:554:16-78: The type of `subquery` is unknown; it is inferred as an implicit `Any` [unknown-variable-type]
+ ERROR zerver/lib/send_email.py:555:67-75: The type of this argument is unknown [unknown-argument-type]
- ERROR zerver/lib/test_classes.py:2836:28-44: `Literal['6cde5f7a-1f7e-4978-9716-49f69ebfc9fe']` is not assignable to attribute `uuid` with type `UUID` [bad-assignment]
+ ERROR zerver/models/presence.py:104:5-17: Class member `UserStatus.user_profile` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
+ ERROR zerver/models/presence.py:111:5-15: Class member `UserStatus.emoji_name` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
+ ERROR zerver/models/presence.py:112:5-15: Class member `UserStatus.emoji_code` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
+ ERROR zerver/models/users.py:492:5-7: Class member `UserProfile.id` overrides parent class `AbstractBaseUser` in an inconsistent manner [bad-override]
+ ERROR zerver/models/users.py:492:5-7: Class member `UserProfile.id` overrides parent class `PermissionsMixin` in an inconsistent manner [bad-override]
+ ERROR zerver/models/users.py:492:5-7: Class member `UserProfile.id` overrides parent class `UserBaseSettings` in an inconsistent manner [bad-override]
+ ERROR zerver/models/users.py:492:5-7: Class member `UserProfile.id` overrides a member in a parent class but is missing an `@override` decorator [missing-override-decorator]
+ ERROR zerver/tests/test_zulip_update_announcements.py:242:36-78: Object of class `NoneType` has no attribute `id` [missing-attribute]
+ ERROR zerver/tests/test_zulip_update_announcements.py:261:26-68: Object of class `NoneType` has no attribute `id` [missing-attribute]
+ ERROR zerver/tests/test_zulip_update_announcements.py:262:34-73: Argument `Any | None` is not assignable to parameter `stream` with type `Stream` in function `zerver.actions.streams.do_deactivate_stream` [bad-argument-type]
- ERROR zerver/views/registration.py:387:49-51: Cannot infer type of empty container; it will be treated as containing `Any` [implicit-any-empty-container]

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had recent activity for more than 2 weeks.

If you are still working on this this pull request, please add a comment or push new commits to keep it active. Otherwise, please unassign yourself and allow someone else to take over.

Thank you for your contributions!

@stroxler stroxler left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This has some behaviors we need to fix up before we can consider merging:

  1. Models stop satisfying field protocols, and valid field overrides fail.
    Descriptor preservation in class_field.rs changes the representation used by compatibility checks, but those checks still reject descriptor-versus-ordinary-attribute comparisons and compare descriptor classes nominally.

    • A model with name = models.CharField(...) no longer satisfies a Protocol with name: str.
    • Replacing an abstract base’s TextField with CharField now reports bad-override, although Django permits it.
      Compare the effective instance getter/setter contracts rather than the field representation.
  2. Relation assignments lose validation.
    Setter specialization in django.rs copies ForeignKey._pyi_private_set_type, which contains Any, into _ST. Assignments of an unrelated model, an integer, or None to a non-nullable foreign key become accepted; the base rejects them. Direct list assignment to ManyToManyField also becomes accepted.
    Preserve resolved relation setter constraints instead of using placeholder stub types. Django runtime confirms assigning the wrong model raises ValueError.

  3. Explicit field type arguments are overwritten.
    [This call](

    let instance_getter_type = is_django_descriptor.then(|| ty.clone());
    if let Some(get_type) = &instance_getter_type
    && let Some(field_type) =
    self.specialize_django_field_descriptor(cls.class_object(), get_type.clone())
    {
    cls = field_type;
    ) passes only cls.class_object(), dropping existing arguments. A field annotated CharField[str, str] now accepts 123, and class access exposes CharField[str | int | Combinable, str]. Preserve explicit arguments; infer defaults only where necessary. Mypy rejects the integer assignment.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Django: Instance vs Class access of attributes should return different types

4 participants