Ignore plugin-generated members when inferring PEP 695 variance#21564
Draft
allanlewis wants to merge 1 commit into
Draft
Ignore plugin-generated members when inferring PEP 695 variance#21564allanlewis wants to merge 1 commit into
allanlewis wants to merge 1 commit into
Conversation
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
infer_variance walked every member of a class, including methods and attributes synthesized by plugins. Those synthesized members reuse the class's own type in positions that don't reflect how the user uses the type variable, which corrupted the inferred variance: - attrs generates ordering methods (__lt__/__le__/__gt__/__ge__) whose ``other`` parameter is typed as the class's own Self[T], plus an __attrs_attrs__ tuple of the invariant Attribute[T]. This made @attrs.define/@attrs.frozen generic classes invariant (and empty ones contravariant) even when T was used only covariantly. - On Python 3.13+ the dataclass plugin generates __replace__, whose keyword parameters reuse the field types and made otherwise covariant frozen dataclasses invariant. User-written declarations are never flagged plugin_generated, so skipping plugin-generated members during variance inference leaves real fields and methods in control while ignoring synthesized ones. This generalizes the existing __mypy-replace special case. Tests: - testPEP695InferVarianceWithAttrsFrozen (check-python312.test) - testPEP695InferVarianceInFrozenDataclass (check-python313.test) Relates to python#17623. Assistant-Model: Claude Code
74e2000 to
4d6b6d1
Compare
Contributor
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
AI / LLM disclosure. This pull request was produced by an AI coding agent — Anthropic's Claude Code (model Claude Opus 4.8). The diff, the added tests, and this description were generated in their entirety by the model. I have reviewed the change and take responsibility for it, but want to be fully transparent that I did not write it by hand.
I'm aware of the
CONTRIBUTING.mdguidance on LLM-assisted contributions, in particular that mypy discourages use of LLMs by new contributors and that PRs from new contributors that are mostly LLM-generated may be closed. As a first-time contributor I didn't want to spring this on anyone unannounced, so I opened a Gitter thread to discuss it first, and I'm filing this as a draft: https://matrix.to/#/!FjKyUUyKpKFUvCNnMz:gitter.im/$NQqpiczcc57aIWYrrmkPXWl6GFA7cD4gZhgtKPslYS0?via=gitter.imIf this isn't a welcome way to contribute, please feel free to close it.
What
PEP 695 variance inference treats generic
@attrs.define/@attrs.frozenclasses as invariant — and empty ones as contravariant — even when the type variable is used only covariantly. Removing the decorator makes the same class infer covariant, as expected.Why
infer_variance(mypy/subtypes.py) iterates over every member fromall_non_object_members, including members synthesized by plugins. Two attrs-generated members reuse the class's own type and skew inference:T__lt__/__le__/__gt__/__ge__(self, other: Self[T]) -> boolT__attrs_attrs__(with aT-typed field)ClassVar[tuple[Attribute[T], ...]];Attributeis invariantTThe dataclass plugin's
__replace__(synthesized on Python 3.13+) has the same effect on frozen dataclasses with fields.This is the same class of bug as #17783, which special-cased the dataclass plugin's internal
__mypy-replace; attrs uses different member names, so that fix never covered it.Fix
Skip members flagged
plugin_generatedduring variance inference. A user's own fields and methods are neverplugin_generated, so they continue to drive the result; only synthesized members are ignored. This subsumes the existing__mypy-replacespecial case (kept in place for clarity).Tests
testAttrsPEP695InferVariance(check-plugin-attrs.test) — a covariant attrs class infers covariant; a mutable-field class stays invariant; a class with a user-writtenT-parameter method stays contravariant (confirming only synthesized members are skipped).testPEP695InferVarianceInFrozenDataclassPython313(check-python312.test) — a frozen dataclass stays covariant under--python-version 3.13.Both fail without the change and pass with it; the full
testchecksuite passes locally.Relates to #17623.