Skip to content

fix: raise NameError for undefined names in state var annotations - #7547

Open
flcrom wants to merge 6 commits into
reflex-dev:mainfrom
flcrom:fix-forwardref-7488
Open

flcrom wants to merge 6 commits into
reflex-dev:mainfrom
flcrom:fix-forwardref-7488

Conversation

@flcrom

@flcrom flcrom commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Closes #7488.

With from __future__ import annotations, an undefined name in a state var annotation (for example items: list[Item] = [] where Item is not defined) raised TypeError: Unsupported type ForwardRef('list[Item]') for guess_type deep inside var typing. 0.9.12 raised NameError: name 'Item' is not defined, which names the missing symbol.

This restores the NameError, including for a var declared without a default (items: list[Item]), where the annotation is widened to Optional. is_dataframe in reflex_base/utils/types.py now also tolerates an unresolved ForwardRef, which has no __name__. Added tests in tests/units/test_state.py and tests/units/utils/test_utils.py that fail without the change.

An independent review environment ran the full unit suite in chunks, not in one uninterrupted run (12,699 passed), on Python 3.11 to 3.13; I did not run it myself. I did not run lint or the full CI matrix locally; CI will.

News fragment included.

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)

View guided diff

@flcrom
flcrom requested a review from a team as a code owner October 11, 2026 05:25

@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.

No issues found across 3 files

View guided diff | Re-trigger cubic

@greptile-apps

greptile-apps Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous loading failure is fixed and no new actionable issues were found.

Summary

Restores NameError when a state field annotation names an undefined symbol.

  • State creation names missing types in annotation errors.

No new actionable issues were found. The previous private-annotation finding is fixed.

Reviews (5) · Last reviewed commit: "fix: resolve only the annotations that d..." · Reviewed by Greptile

Comment thread reflex/state.py Outdated
@codspeed

codspeed Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 19 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 148 untouched benchmarks


Comparing flcrom:fix-forwardref-7488 (5a538dd) with main (5ae5dcd)

Open in CodSpeed

Comment thread tests/units/test_state.py Outdated
@masenf masenf added the on deck PRs lined up to review / merge next label Oct 16, 2026
Move the check from get_var_for_field into the state metaclass.
resolve_annotations gets a keyword-only strict flag. With strict set, an
annotation that names an undefined symbol re-raises the NameError, unless
it is a ClassVar. BaseStateMeta sets strict only for the state's own
namespace. Plain bases and components stay lenient.

Field never sees an unresolved ForwardRef now, so the Optional unwrap in
get_var_for_field, the BaseState._get_type_hints probe and the
is_dataframe getattr patch are removed. Each annotation is evaluated on
its own, so the error names the symbol of that var. Nested refs (the
Python 3.14 form without the __future__ import) and backend vars raise
too, as in 0.9.12.
Comment thread packages/reflex-base/src/reflex_base/vars/base.py
Strict resolution ran on every annotation before _annotated_fields
skipped the names that are not fields. A private attribute or a
descriptor annotated with a symbol imported only under TYPE_CHECKING
then raised NameError, and the state class failed to load.

_annotated_fields now resolves each annotation after the private,
descriptor and inherited-var checks, through a per-annotation
_resolve_annotation helper. resolve_annotations uses the same helper
and is lenient again, without the strict flag.

@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.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/reflex-base/src/reflex_base/utils/types.py">

<violation number="1" location="packages/reflex-base/src/reflex_base/utils/types.py:1502">
P2: Qualified ClassVar annotations are not exempt from the new strict NameError path. `typing.ClassVar[MissingItem]` remains an unresolved forward reference whose text does not start with `ClassVar`, so state class creation raises instead of skipping this non-field; recognize qualified ClassVar forward references before re-raising.</violation>
</file>

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

View guided diff | Re-trigger cubic

return _eval_type(value, base_globals, None, type_params=())
return _eval_type(value, base_globals, None)
except NameError:
if strict and not is_classvar(value):

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.

P2: Qualified ClassVar annotations are not exempt from the new strict NameError path. typing.ClassVar[MissingItem] remains an unresolved forward reference whose text does not start with ClassVar, so state class creation raises instead of skipping this non-field; recognize qualified ClassVar forward references before re-raising.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/utils/types.py, line 1502:

<comment>Qualified ClassVar annotations are not exempt from the new strict NameError path. `typing.ClassVar[MissingItem]` remains an unresolved forward reference whose text does not start with `ClassVar`, so state class creation raises instead of skipping this non-field; recognize qualified ClassVar forward references before re-raising.</comment>

<file context>
@@ -1475,11 +1475,37 @@ def typehint_issubclass(
+            return _eval_type(value, base_globals, None, type_params=())
+        return _eval_type(value, base_globals, None)
+    except NameError:
+        if strict and not is_classvar(value):
+            raise
+        return value
</file context>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

on deck PRs lined up to review / merge next

Projects

None yet

3 participants