Skip to content

Stop recursive type alias expansion on Python 3.10 (0.2.4) - #18

Merged
trissim merged 1 commit into
mainfrom
fix/recursive-type-alias-py310
Oct 10, 2026
Merged

trissim merged 1 commit into
mainfrom
fix/recursive-type-alias-py310

Conversation

@trissim

@trissim trissim commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Problem

On Python 3.10, zmqruntime's tests/test_progress_annotation.py::test_progress_mapping_decode_preserves_wire_and_immutable_nested_values fails with RecursionError: maximum recursion depth exceeded in __instancecheck__ once python-introspect 0.2.x is installed. Python 3.11 and later pass.

Bisected from v0.1.16 to v0.2.3 with that test: the first bad commit is 0da0ee8 ("resolve recursive sequence declarations on Python 3.10", 0.2.1). PyPI first shipped it in 0.2.2.

Root cause

_resolve_nested_type_strings evaluates the strings that 3.10's get_type_hints leaves inside PEP 585 aliases. A recursive alias (WireValue = str | ... | list["WireValue"] | dict[str, "WireValue"]) names itself. Each evaluation gives back the same strings, so expansion never ends. On 3.11 and later, get_type_hints already leaves the inner self-reference as a ForwardRef, so the resolver never re-enters it.

Fix

The resolver tracks the names it is currently expanding. When a name refers back to itself, it returns ForwardRef(name), the same form 3.11+ produces, so the decoder sees the same declaration on every version. There is no version check.

Tests

  • New test_recursive_type_alias_decodes_nested_values reproduces the zmqruntime declaration. It fails on 3.10 without the fix.
  • python-introspect suite: 170 passed on CPython 3.10.22 and on 3.11.
  • zmqruntime tests/test_progress_annotation.py: 5 passed, 2 skipped on 3.10 and on 3.11 (it failed on 3.10 before the fix).

Version 0.2.4, with a CHANGELOG entry. Not tagged.

🤖 Generated with Claude Code

Since 0.2.1 (0da0ee8), resolved_class_annotations expanded nested type
strings left by Python 3.10's get_type_hints with no stop. A recursive alias
such as WireValue = str | list["WireValue"] | dict[str, "WireValue"]
re-evaluated its own name forever and raised RecursionError in
dataclass_from_mapping (seen in zmqruntime's progress decode test).

Expansion now tracks the names being expanded and leaves a self-reference as
a ForwardRef, the form get_type_hints returns on Python 3.11+.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 10, 2026 10:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@trissim
trissim merged commit b56ea76 into main Oct 10, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants