Repository navigation
Treat writable callable attributes as invariant in PEP 695 variance inference - #22438
Closed
OBOneProtocol wants to merge 1 commit into
Closed
OBOneProtocol wants to merge 1 commit into
OBOneProtocol wants to merge 1 commit into
Conversation
…nference infer_variance() only applied the extra "settable" constraint in one direction: when the getter type was not a supertype of its object-expanded form, a settable member also disabled covariance. The symmetric case was missing, so a writable attribute such as `cb: Callable[[T], None]` (where T appears only in an input position) was inferred contravariant, even though writes make it an input position for the reversed direction as well. This allowed Handler[Animal] where Handler[Dog] was expected, leading to runtime AttributeErrors. Now a settable member that rules out covariance also rules out contravariance, making such attributes invariant. Final, underscore-prefixed and read-only property members are unaffected and stay contravariant. Fixes python#22339. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #22339
infer_variance()uses thesettableflag to make writable attributes invariant, but only in one direction. WhenTreplaced byobjectgives a supertype of the member's type, it clears bothcontraandco; when it gives a non-subtype, it clears onlyco. A writableCallable[[T], None]attribute falls into the second case, soTstayed contravariant andHandler[Animal]was accepted whereHandler[Dog]was expected, which fails at runtime once the callback is reassigned.This adds the symmetric
if settable: contra = Falseto that branch. A writable attribute is read and written, so it should constrain the type variable in both directions, which is what mypy already does for writableTandCallable[[], T]attributes and what the protocol checker requires for the same attribute shape.The change is limited to the settable-member branch; it doesn't touch the
find_member(...)call that #19466 changes.Tests:
testPEP695InferVarianceWritableCallableAttributeincheck-python312.testcovers the issue's example (now anarg-typeerror), a writableCallable[[T], None]attribute in both directions, a writableCallable[[], T]attribute (already invariant), andFinal, underscore-prefixed and read-only property versions plus a methods-only class, which all stay contravariant. The new case fails on master and passes with the change. I also ran check-python312/313/314, protocols, generic-subtyping, dataclasses, typevar-defaults, final, generics, classes, selftype and incremental (2,000+ cases, no other changes), the self-check onmypy/subtypes.py, and black/ruff/codespell at the pre-commit versions.One note: classes defining
__setattr__treat members as settable, so they may now infer invariant in more cases. No existing test changed; mypy_primer should show whether that matters in practice.This change was investigated, implemented and tested with help from Claude Code.