ENG-9425: fixes for extensible BaseState#6418
Conversation
* For overridden methods, do not create EventHandler wrappers * For descriptor fields in a State, add a placeholder in `base_vars`, but do not intercept their access/set patterns.
Greptile SummaryThis PR introduces two fixes for extensible
Confidence Score: 3/5Not safe to merge until the incorrect A P1 test bug ( tests/units/test_state.py — line 4694 has an incorrect assertion that will fail CI. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[BaseState.__init_subclass__] --> B[Compute base_vars\nfrom pydantic fields\nnot starting with _]
A --> C[Iterate mixins + cls\nfor descriptor_vars]
C --> D{_is_user_descriptor?}
D -- No --> E[skip]
D -- Yes --> F[Create placeholder Var\nwith FIELD_MARKER]
F --> G[descriptor_vars dict]
G --> H[cls.vars merge\ndescriptor_vars + inherited_vars\n+ base_vars + computed_vars]
B --> H
A --> I[_item_is_event_handler check]
I --> J{__override_base_method__?}
J -- Yes --> K[skip — not an EventHandler]
J -- No --> L[wrap as EventHandler]
A --> M[_init_var_dependency_dicts\nregisters computed var deps\nagainst descriptor_vars entries in cls.vars]
Reviews (1): Last reviewed commit: "ENG-9425: fixes for extensible BaseState" | Re-trigger Greptile |
| # But it should be visible in base_vars/vars for dependency tracking. | ||
| assert "_desc_value" in DescriptorState.base_vars |
There was a problem hiding this comment.
Incorrect
base_vars assertion — test will fail
_desc_value starts with _, so it is explicitly excluded from base_vars by the not name.startswith("_") guard in __init_subclass__. Descriptor vars are added only to descriptor_vars, which is then merged into cls.vars. The assertion on line 4694 will therefore always be False and the test will fail.
The comment on line 4693 says "visible in base_vars/vars", but only the vars half is correct; the base_vars assertion should be inverted (or simply removed).
| # But it should be visible in base_vars/vars for dependency tracking. | |
| assert "_desc_value" in DescriptorState.base_vars | |
| # But it should be visible in vars for dependency tracking. | |
| assert "_desc_value" not in DescriptorState.base_vars | |
| assert "_desc_value" in DescriptorState.vars |
| for source_cls in (*cls._mixins(), cls): | ||
| for dname, dvalue in source_cls.__dict__.items(): | ||
| if ( | ||
| dname in cls.base_vars | ||
| or dname in descriptor_vars | ||
| or dname in cls.inherited_vars | ||
| or dname in cls.inherited_backend_vars | ||
| or dname not in hints | ||
| or not _is_user_descriptor(dvalue) |
There was a problem hiding this comment.
Child override of parent descriptor silently uses parent Var
When a subclass overrides a parent's descriptor with a different implementation, dname in cls.inherited_vars is True (because the parent's placeholder Var was merged into parent_state.vars, which becomes inherited_vars). The child's descriptor is therefore skipped and the dependency-tracking Var still refers to the parent-state field name in its VarData. Any computed var in the child that depends on the overridden descriptor would be attached to the parent's tracking dict rather than the child's, potentially missing invalidation.
This may be an accepted limitation, but it would be worth a comment or a # TODO to document the behaviour for future readers.
Allow substates to override descriptors in parent states. Fix test assertions
vars, but do not intercept their access/set patterns.