Repository navigation
Fix RecursionError when checking an Instance with a self-referential __call__ attribute against a Callable type - #22400
Closed
Shanmukh-l33t wants to merge 1 commit into
Conversation
…attribute is checked against a Callable type (python#22398) visit_instance() special-cases Instance <: Callable/Overloaded by following the __call__ member. When that member (transitively) refers back to its own class (e.g. "class C: __call__: C"), the check recurses forever: h(C()) crashes mypy with RecursionError (or a segfault in compiled builds). Track the TypeInfos whose __call__ is currently being followed in SubtypeContext (shared across a single subtype query) and return False when the chain loops back: such an instance cannot be called at runtime either, so it is simply not a subtype of the Callable type, and the caller reports the usual arg-type error instead of crashing. Complements python#22362, which guards the check_call() path in checkexpr.py.
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 #22398.
visit_instance()special-casesInstance <: Callable/Overloadedby following the__call__member (mypy/subtypes.py). When that member (transitively) refers back to its own class —class C: __call__: C— the check recurses forever, socrashes mypy with
RecursionErroron pure-Python builds and segfaults (exit 139) on compiled wheels, exactly as reported in the issue.Fix
Track the
TypeInfos whose__call__is currently being followed inSubtypeContext(shared down the whole subtype query, since each recursion level constructs a freshSubtypeVisitorbut keeps the context). When the chain loops back to a class already being followed, returnFalse: such an instance cannot be called at runtime either, so it is simply not a subtype of thatCallabletype. The caller then reports the normal error the issue asks for:This complements #22362 (which guards the
check_call()path incheckexpr.py); this path issubtypes.pyonly, as noted in the issue. The guard is scoped to theFunctionLikebranch and popped in afinally, so it cannot leak into unrelated checks; sibling branches (protocol__call__, nominal checks) are untouched.Covered variants: direct (
__call__: C),Optional[C],Self, genericG[list[T]](type args grow each round, so a pair-based guard would not fire), mutually-referencingX/Y, plus assignment-position checks, and positive controls (def __call__classes still check clean).Testing
testSubtypeOfCallableWithSelfReferentialCallAttributeintest-data/unit/check-classes.test: fails at parent5dc2840withRecursionError/INTERNAL ERROR(the exact reported crash) and passes with the fix.pytest mypy/test -q -n 2):12574 passed, 362 skipped, 10 xfailed; the 3 failures (test_find_sources_exclude, 2×testoutputJSON-parallel) are environment-only — they spawnpython -m mypy.build_workerin a venv where mypy is not pip-installed — and reproduce identically with this change stashed.check-classes.test619 passed / 1 xfailed;check-callable,check-expressions,check-protocols,check-generic-subtyping499 passed.black --check,ruff check, andmypy --config-file mypy_self_check.ini mypy/subtypes.pyall clean.Note: I built this against
master 5dc2840(the revision in the issue) before #22362 landed; the two changes touch different files, so they merge independently.