Skip to content

resolve type aliases - #6944

Merged
masenf merged 10 commits into
reflex-dev:mainfrom
benedikt-bartscher:allow-typevar-resolution
Aug 27, 2026
Merged

resolve type aliases#6944
masenf merged 10 commits into
reflex-dev:mainfrom
benedikt-bartscher:allow-typevar-resolution

Conversation

@benedikt-bartscher

@benedikt-bartscher benedikt-bartscher commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Review in cubic

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds support for resolving native and backported TypeAliasType annotations in Var.guess_type.

  • Resolves bare, generic, variadic, and union-nested type aliases.
  • Substitutes TypeVar, ParamSpec, and TypeVarTuple parameters.
  • Adds utility and state-variable coverage under the source-corresponding test suites.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/utils/types.py Adds recursive type-alias resolution and generic parameter substitution helpers.
packages/reflex-base/src/reflex_base/vars/base.py Resolves aliases before applying the existing Var.guess_type dispatch.
tests/units/reflex_base/utils/test_types.py Covers ParamSpec substitution alongside a variadic type parameter.
tests/units/reflex_base/vars/test_base.py Places the new alias-resolution tests in the required source-corresponding suite and covers bare, parameterized, variadic, optional, and state-field aliases.

Reviews (10): Last reviewed commit: "fix: resolve subscripted, variadic and P..." | Re-trigger Greptile

Comment thread tests/units/vars/test_base.py Outdated
@codspeed-hq

codspeed-hq Bot commented Aug 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 27 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing benedikt-bartscher:allow-typevar-resolution (1cff0a5) with main (ceff479)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@benedikt-bartscher
benedikt-bartscher marked this pull request as ready for review August 25, 2026 10:33
@benedikt-bartscher
benedikt-bartscher requested a review from a team as a code owner August 25, 2026 10:33

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/types.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/types.py Outdated
masenf
masenf previously approved these changes Aug 27, 2026
@masenf

masenf commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

welp, i marked approve before i saw the py3.10 regression; thanks for the quick fixup

@benedikt-bartscher

Copy link
Copy Markdown
Contributor Author

yeah, i sadly missed this yesterday, because main has been red..
thanks for the quick review!

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/reflex-base/src/reflex_base/utils/types.py Outdated
@masenf
masenf merged commit bb7ed7c into reflex-dev:main Aug 27, 2026
111 checks passed
masenf pushed a commit that referenced this pull request Aug 28, 2026
pydantic_optional (#6786): bare/pydantic/db extras and the in-place
upgrade path all verified end-to-end in the browser; no regressions vs
0.9.8. Two low-severity error-message papercuts recorded.

typing_python (#6944/#6890/#6846): PEP 695 aliases compile and render on
3.11-3.14, and Python 3.14 builtin-annotation handling passes fully.
Found: runtime assignment to an alias-annotated state var raises
TypeError in State.__setattr__ (high — mutating handlers die), and an
uncalled handler with an alias-annotated arg crashes page compile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x
masenf pushed a commit that referenced this pull request Aug 28, 2026
19 confirmed issues (1 high, 9 medium, 9 low) with repro steps, verifier
root-cause analysis, and verification notes; 2 refuted claims; and
per-cluster summaries for all 11 feature-exploration clusters (32 agents,
0 errors). Headline confirmed issues: PEP695 alias state vars cannot be
mutated at runtime (#6944 gap); background-task on_load cancelled on
navigation (undocumented behavior change); client_error no-arg
TypeError; upload sanitizer '..' traversal; react-router-dom custom
components silently install RR7 in dev; reflex run hangs after fatal
node-version error; breaking API removals give bare errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x
benedikt-bartscher pushed a commit to benedikt-bartscher/reflex that referenced this pull request Aug 28, 2026
PR reflex-dev#6944 added resolve_type_alias() but wired it only into
Var.guess_type, so alias-annotated state vars compiled while every
runtime path that re-checks the raw annotation still choked on the
TypeAliasType:

- State.__setattr__ validates assignments through _isinstance(), which
  fell through to the bare isinstance() call and raised "TypeError:
  isinstance() arg 2 must be a type" on every assignment to an
  alias-annotated var, aborting the event handler instead of at most
  logging a mismatch (0.9.9a1 pre-release FINDING-002).
- Passing an event handler with an alias-annotated argument uncalled to
  an event trigger crashed page compile: typehint_issubclass() reached
  the bare issubclass() for a plain alias and failed the origin
  comparison for a subscripted one, escalating to a fatal error
  (FINDING-006).

Resolve the alias at the entry of both functions, reusing
resolve_type_alias(), which already handles parameterized aliases
(Items[str]) and aliases nested in unions (Key | None). Alias-annotated
vars now behave exactly like their resolved annotations at runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x
masenf pushed a commit that referenced this pull request Aug 28, 2026
PR #6944 added resolve_type_alias() but wired it only into
Var.guess_type, so alias-annotated state vars compiled while every
runtime path that re-checks the raw annotation still choked on the
TypeAliasType:

- State.__setattr__ validates assignments through _isinstance(), which
  fell through to the bare isinstance() call and raised "TypeError:
  isinstance() arg 2 must be a type" on every assignment to an
  alias-annotated var, aborting the event handler instead of at most
  logging a mismatch (0.9.9a1 pre-release FINDING-002).
- Passing an event handler with an alias-annotated argument uncalled to
  an event trigger crashed page compile: typehint_issubclass() reached
  the bare issubclass() for a plain alias and failed the origin
  comparison for a subscripted one, escalating to a fatal error
  (FINDING-006).

Resolve the alias at the entry of both functions, reusing
resolve_type_alias(), which already handles parameterized aliases
(Items[str]) and aliases nested in unions (Key | None). Alias-annotated
vars now behave exactly like their resolved annotations at runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x
masenf added a commit that referenced this pull request Aug 28, 2026
…6986)

* Resolve PEP 695 type aliases in _isinstance and typehint_issubclass

PR #6944 added resolve_type_alias() but wired it only into
Var.guess_type, so alias-annotated state vars compiled while every
runtime path that re-checks the raw annotation still choked on the
TypeAliasType:

- State.__setattr__ validates assignments through _isinstance(), which
  fell through to the bare isinstance() call and raised "TypeError:
  isinstance() arg 2 must be a type" on every assignment to an
  alias-annotated var, aborting the event handler instead of at most
  logging a mismatch (0.9.9a1 pre-release FINDING-002).
- Passing an event handler with an alias-annotated argument uncalled to
  an event trigger crashed page compile: typehint_issubclass() reached
  the bare issubclass() for a plain alias and failed the origin
  comparison for a subscripted one, escalating to a fatal error
  (FINDING-006).

Resolve the alias at the entry of both functions, reusing
resolve_type_alias(), which already handles parameterized aliases
(Items[str]) and aliases nested in unions (Key | None). Alias-annotated
vars now behave exactly like their resolved annotations at runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x

* Make PEP 695 alias resolution zero-cost on alias-free type checks

The unconditional resolve_type_alias() at the entry of _isinstance and
typehint_issubclass ran on every recursive per-item and per-member call,
regressing event processing by ~28% (local pytest-benchmark mean;
CodSpeed -13.6%) even though no alias was present. Move resolution to
the exact points where an alias becomes opaque:

- _isinstance: catch the TypeError from isinstance() on a bare alias
  (zero-cost try on the hot path, mirroring safe_issubclass) and check
  the already-computed origin for subscripted aliases; union and Literal
  branches resolve alias members through their existing per-member
  recursion.
- typehint_issubclass: same try/except in the non-generic issubclass
  branch, a one-sided check before each union decomposition (so an
  alias of a union keeps union semantics), and a two-sided check before
  the final origin comparison.
- resolve_type_alias: rebuild a union only when a member actually
  resolved, instead of allocating a resolved tuple on every call (also
  helps Var.guess_type, which calls it unconditionally).

Local microbench (20k-iter timeit, best of runs) vs origin/main:
_isinstance('x', str|None) 1.61us -> 1.62us (was 4.78us),
typehint_issubclass(str, str|None) 1.81us -> 1.89us (was 5.98us),
_isinstance(list(range(20)), list[int], nested=1) 16.4us -> 16.7us
(was 26.9us). test_process_event mean back inside main's noise band
(2.78-2.96ms main, 2.87-3.09ms fixed, 3.72-3.79ms before).

Also add the review-requested regression test for uncalled
alias-annotated handlers through call_event_handler (previously the
opaque "Could not compare types" TypeError at page compile), alias-of-
union coverage locking the guard placement, and the missing docstring
on _type_alias_types() in the types tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x

* Check subscripted-alias origin lazily in _isinstance's fallback

CodSpeed still flagged test_isinstance_container[list_dict] (-7.58%):
the isinstance(origin, TypeAliasTypes) probe ran on every generic check
before the args dispatch. Move it into the final fallback's TypeError
handler — a subscripted alias matches none of the container branches
and get_base_class hands the opaque alias back, so the existing
isinstance there raises deterministically. Alias-free generic checks
now pay nothing; interleaved local A/B puts this at parity with main
(8.4ms vs 8.45ms; previous head 9.07ms) on the flagged benchmark.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x

---------

Co-authored-by: Claude <noreply@anthropic.com>
masenf pushed a commit that referenced this pull request Aug 28, 2026
A ~29s 1920x1080 motion graphic covering the end-user-facing changes in
0.9.9, sourced from the 0.9.9a1 and 0.9.9a2 changelog entries across
reflex, reflex-base, the component packages and reflex-hosting-cli.

Nine beats: the dev-mode main-thread win (#6905), call-site
auto-memoization of @rx.memo (#6949), the move to React Router 8 (#6854),
DevTools component naming (#6945), stdlib logging with --json plus the
client_error report (#6863, #6865, #6827), the cloud CLI's whoami/token
and autoscaling flags (#6918, #6884, #6948), and a grid of the smaller
fixes a user would notice (#6786, #6593, #6944, #6971, #6790, #6920).

Styled to match reflex.dev by reusing its design system rather than
approximating it: the dark violet/slate scales, wave-line motif, radial
bloom and type scale come from reflex-site-shared, and the fonts are the
same @fontsource-variable Instrument Sans and JetBrains Mono files the
site serves.

Source only — the rendered mp4 is a build artifact, regenerated by
`npm run build` and ignored. Lives outside the packaged tree (hatch only
includes /reflex), so it does not affect the published wheel or sdist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016arkVKmoogzUtZNfZZSvSo
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