Resolve PEP 695 type aliases in _isinstance and typehint_issubclass
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Type of change
- Bug fix (non-breaking change which fixes an issue)
Changes To Core Features:
- Have you added an explanation of what your changes do and why you'd like us to include them?
- Have you written new tests for your core changes, as applicable?
- Have you successfully ran tests with your changes locally?
Defect
Found in 0.9.9a1 pre-release testing as FINDING-002 (HIGH) and FINDING-006 (MEDIUM). PR #6944 added resolve_type_alias() to reflex_base/utils/types.py but wired it only into Var.guess_type, so state vars annotated with a PEP 695 alias (type Key = Literal["a", "b"], or the typing_extensions.TypeAliasType backport that 3.10/3.11 users hit) compiled while every runtime path that re-checks the raw annotation still choked on the TypeAliasType:
- FINDING-002:
State.__setattr__validates assignments through_isinstance(), which fell through to the bareisinstance()call and raisedTypeError: isinstance() arg 2 must be a type, a tuple of types, or a unionon every assignment to an alias-annotated var. The guard was designed to onlylogger.errora mismatch, but the TypeError escaped and aborted event processing โ alias vars compiled but every mutating event handler died. - FINDING-006: an event handler whose argument is alias-annotated, passed uncalled to an event trigger (
rx.input(on_change=S.choose)), crashed page compile:typehint_issubclass()reached the bareissubclass()for a plain alias (TypeError: Could not compare types <class 'str'> and Key) and failed the origin comparison for a subscripted alias (Pair[str, str]), escalating what should at most be a warning into a fatalEventHandlerArgTypeMismatchError.
Fix
Resolve TypeAliasType at the entry of _isinstance (the annotation operand) and typehint_issubclass (both operands), reusing the existing resolve_type_alias() helper, which already handles parameterized aliases (Items[str]) and aliases nested in unions (Key | None). Nested annotations (e.g. an alias inside list[...]) are covered by both functions' per-argument recursion resolving at each entry. Alias-annotated vars and handler arguments now behave exactly like their resolved annotations at runtime; the resolution is a no-op fast path for non-alias types. News fragment added in packages/reflex-base/news/.
Test plan
Regression tests written first and shown to fail on unfixed main with exactly the reported errors:
tests/units/reflex_base/utils/test_types.py::test_isinstance_resolves_type_aliasand::test_typehint_issubclass_resolves_type_aliasโ plain alias,Literalalias, parameterized generic alias, andalias | None, parameterized over thetyping_extensionsbackport and (on 3.12+) the nativetyping.TypeAliasTypethat thetypestatement produces.tests/units/test_state.py::test_setattr_alias_annotated_varโ a state with all four alias-annotated var shapes assigns them via an event handler; also pins that a mismatched value is logged, not raised.
Verified locally: uv run pytest tests/units/utils/test_types.py tests/units/reflex_base/utils/test_types.py tests/units/reflex_base/vars/test_base.py tests/units/test_state.py tests/units/test_event.py tests/units/components tests/units/test_app.py (4600+ passed), uv run ruff check ., uv run ruff format ., uv run pyright reflex tests (0 errors), uv run pyright packages/reflex-base (0 errors, 1 pre-existing warning in an untouched file). Both pre-release repro scripts (repro_alias_setattr.py, repro_alias_event_arg.py, Python 3.14 native type statements) now pass, and a parameterized-alias on_submit handler behaves identically to a plain dict[str, str] annotation.
๐ค Generated with Claude Code
https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x
Generated by Claude Code