Address the #7215 review follow-ups in BaseStateMeta

Merged
YA
3h ago
+76-21

Summary

Closes #7264.

This handles the three follow-ups from the #7215 review that the issue lists: a dead statement in test_reserved_mixin_var, the getattr lookup of _reflex_state_root in BaseStateMeta.__new__, and the missing contract docs on BaseStateMeta.

What changed

  • Test: split test_reserved_mixin_var into two tests. The plain-mixin case now creates the mixin outside pytest.raises, so it shows the rejection happens when a state inherits it. The new test_reserved_state_mixin_var asserts the rejection at the state mixin's own declaration, with no second statement left to skip.
  • Typed default: BaseStateMeta now has _reflex_state_root: BaseStateMeta | None = None, and __new__ reads it directly from the EvenMoreBasicBaseState bases instead of calling getattr on every base.
  • _is_tree_state: this also used getattr, with cls as the fallback. With the new None default, root-less states would have counted as tree states, so it now checks is not None and is not cls (identity, as before). The existing suite still passed with the broken version, so I added test_only_states_below_a_root_are_tree_states.
  • _validate_state_name: now accepts None and returns early, since a state outside any hierarchy reserves no names. Without this, pyright flags the three cls._reflex_state_root calls in reflex/state.py. Covered by test_no_root_reserves_no_names.
  • Docstring: BaseStateMeta now documents that type(rx.State) is BaseStateMeta is supported, the state_root= and mixin= keywords, and the reserved _reflex_state_root name.

Notes for reviewers

  • The None branch in _validate_state_name only exists for the type checker. At runtime those three callers always have a root. If you'd rather narrow the type at the call sites instead, I can change it.
  • There's no user-facing change, so I didn't add a news fragment. Could you add the skip-changelog label?

Verification

  • uv run pytest tests/units --cov --no-cov-on-fail --cov-report=: 13,083 passed, 81.99% coverage. Two granian reload tests in tests/units/utils/test_exec.py time out on my WSL machine (they fail on main too) and pass in CI.
  • uv run ruff check ., uv run ruff format --check . and uv run pyright reflex tests: all clean.

The code changes were made with help from Claude. I reviewed the diff and confirmed the checks above pass on my machine.