Reject browser storage defaults produced by a default_factory
Requested by Masen · project thread
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
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
- This change requires a documentation update
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?
What
Before: a str-annotated var whose default_factory returned a LocalStorage was never detected as browser storage. It compiled as an ordinary var and each session silently held a storage instance as a plain str. A storage-annotated var with a factory was accepted, and compile_client_storage called the factory to read the storage name and options.
After: both declarations fail at compile time with a VarTypeError naming the var and showing the supported form (theme: str = rx.LocalStorage("", name='theme')). Browser storage must be declared with a storage value as the default or with a storage type annotation. The docs say so in Browser Storage and in Changing Defaults.
This rejects rather than warns: a factory-produced storage value cannot be classified when the app compiles, so there is no working fallback to warn about, and nothing in the codebase deprecates a declaration that never worked.
How
_compile_client_storage_field raises when a storage-annotated field has no default (a declared factory, or one set later with set_default(default_factory=...)), and when a field whose annotation accepts a str (str, str | None, Any, object) has a factory that returns a ClientStorageBase. That second check calls the factory once at compile for str-compatible factory fields only; factories for other types are never called. The special case that read storage options from a factory's product is removed, along with the unit and Playwright tests that exercised factory-declared storage.
Test plan
tests/units/compiler/test_compiler_utils.py: rejection for each storage type via annotation, viaset_default(default_factory=...), and forstr/str | Nonefactories returning storage; a plainstrfactory and alistfactory compile as ordinary vars with thestrfactory called once.uv run pytest tests/units/compiler tests/units/test_state.py tests/units/utils/test_telemetry_accounting.py: 638 passed.uv run pytest tests/integration/tests_playwright/test_hydration_storage.py: 32 passed.ruff check,ruff format --check,pyright reflex tests: clean.
Closes #7498
🤖 Generated with Claude Code
https://claude.ai/code/session_014uPh1ADyPWucA2MM6zpW9T
Generated by Claude Code