Fix type validation for browser storage annotations

Merged
SA
7d ago
+157-1

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?

Changes

Hydration supplies plain strings for browser-storage vars, but fields annotated with Cookie, LocalStorage, or SessionStorage currently log type errors for those values. Accept str when initializing these fields, including storage-containing unions, while preserving their annotations, defaults, persistence options, and checks for other invalid values. Assignment requires no additional runtime checks or storage-wrapper allocations.

Add unit coverage for all three storage types, Field wrappers, optional and union annotations, empty strings, subsequent writes, dirty tracking, unchanged storage options, and invalid integer values. Extend the existing browser-storage app to exercise hydration, event writes, persistence, and reloads.

Closes #7499.

Validation

Tested on Windows with Python 3.12.10. The 465-test run covers 9aaed22c318cbfb1d71f94b4fc9a839f5fddbe9d; the final head, f9b47d3a6fb65ac535b4d2629328af3402c0b207, only strengthens the browser test's error-log assertion. Production source and unit tests are byte-identical between these revisions. The final browser check and repeated applicable hooks cover that follow-up.

  • Before the corresponding fixes, 12 direct/wrapped storage cases and four optional/union cases failed with the reported type errors; the two plain-string controls passed.
  • uv run --python 3.12 --locked pytest tests/units/test_state.py tests/units/reflex_base/event/processor/test_base_state_processor.py tests/units/compiler/test_compiler_utils.py -q: 465 passed. The run emitted Pydantic deprecation and collection warnings.
  • The final-head audit reran uv run --python 3.12 --locked pytest tests/units/test_state.py -k storage_annotations_accept_browser_strings -q --tb=short: 18 passed, with the existing TestMixin collection warning. All applicable hooks on the four changed files passed again.
  • UV_PYTHON=3.12 uv run --python 3.12 --locked pre-commit run --files reflex/state.py tests/units/test_state.py tests/integration/tests_playwright/test_hydration_storage.py news/+storage-annotation-hydration.bugfix.md: all applicable hooks passed, including Ruff, codespell, stub generation, Pyright, and ty. The environment-variable prefix shown here uses POSIX shell syntax.
  • An earlier dedicated browser regression run passed in development and production (2 passed), before extending the fix to optional/union annotations.
  • After the review feedback, the browser test captures reflex_base.vars.base records directly and asserts that no errors occurred during hydration, event writes, and reloads. uv run --python 3.12 --locked pytest 'tests/integration/tests_playwright/test_hydration_storage.py::test_storage_annotations_hydrate_and_persist[AppHarness-chromium]' -q -o faulthandler_timeout=120: 1 passed on the final test. All applicable pre-commit hooks passed again for the changed browser test.
  • The development/production dedicated run after the optional/union fix, before the log-assertion follow-up: uv run --python 3.12 --locked pytest tests/integration/tests_playwright/test_hydration_storage.py -k storage_annotations_hydrate_and_persist -q -o faulthandler_timeout=120 reported 1 passed, 1 setup error. The development browser regression passed; production setup failed during React Router prerendering with Prerender: Request failed for /: The operation was aborted due to timeout. The process was interrupted after its error report. The diagnostic flag dumps stacks; it does not raise a test or prerender timeout. Final production behavior remains unverified.
  • A comparison run restored only reflex/state.py to upstream 75a08e179390bd88826b4ed0bb3912d60facf7cc while keeping the same regression app and environment. The production-only node failed during setup with the same React Router prerender timeout (1 setup error, 409 seconds). The source was restored to the PR head afterward. This comparison does not validate production behavior; it shows the timeout also occurs with the original affected implementation.
  • The expanded browser-suite run reported a failure and then stalled; it was interrupted. A fail-fast diagnostic rerun passed the previously failing case and the new regression, then stalled during the transition to the production fixture. Its stack dump shows AppHarness.stop() waiting in backend_thread.join() while the Uvicorn event loop remains running. The initial failure's cause is unresolved. The full browser suite and full unit suite are not claimed as passing.
  • A final production-only diagnostic with Node.js 24.19.0, UTF-8 console output, and no competing builds also failed during the same React Router prerender step (1 setup error, 144.12 seconds). The process was interrupted after its error report because the setup failure left the backend thread running. No production timeout was raised, and production browser behavior remains unverified.
  • GitHub Actions workflows for this head report action_required, with no jobs run yet; remote CI is unverified.

An independent automated review identified the optional/union gap, which was reproduced and fixed; the follow-up review found no remaining code issues. A separate source review of the final patch found no actionable correctness or security finding; it did not execute a full repository security audit. Production browser and expanded-suite validation remain incomplete as described above.

Implemented with assistance from OpenAI Codex. No human review or manual testing is claimed.