-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REF-1356] Track changes applied to Base
subclass via helper method.
#2242
Conversation
When a helper method manipulates fields on a Base instance that is associated with a state, recursively wrap the helper method with the MutableProxy so it also tracks changes. If the helper method returns a mutable object, this object will also be tied back to the same field on the state to allow for "get_*" type functions that still track changes. Fix #2239
tests/test_state.py
Outdated
def test_set_base_field_via_setter(): | ||
"""When calling a setter on a Base instance, also track changes.""" | ||
|
||
class BaseFieldSetterState(BaseState): | ||
c1: Custom1 = Custom1(foo="") | ||
|
||
bfss = BaseFieldSetterState() | ||
assert "c1" not in bfss.dirty_vars | ||
bfss.c1.double_foo() | ||
assert "c1" not in bfss.dirty_vars | ||
bfss.c1.set_foo("bar") | ||
assert "c1" in bfss.dirty_vars | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, this covers the issue -- thanks!
I'm now wondering if this will work if using Custom2
to do a nested call to Custom1
(something like a hypothetical c2r.set_c1_foo()
, which calls c1r.set_foo()
). I do see some terminology around recursion in the function wrapper, so if I had to guess it seems like this is the intent; if so maybe good to also capture in a test case?
tests/test_state.py
Outdated
def test_set_base_field_via_setter(): | ||
"""When calling a setter on a Base instance, also track changes.""" | ||
|
||
class BaseFieldSetterState(BaseState): | ||
c1: Custom1 = Custom1(foo="") | ||
|
||
bfss = BaseFieldSetterState() | ||
assert "c1" not in bfss.dirty_vars | ||
bfss.c1.double_foo() | ||
assert "c1" not in bfss.dirty_vars | ||
bfss.c1.set_foo("bar") | ||
assert "c1" in bfss.dirty_vars | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also wondering how this works for shared references, too? Something like this:
class State(rx.State):
a: A()
b: B()
def __init__(self):
c = C()
a.c = c
b.c = c
def trigger_a():
self.a.update_c() # Does this trigger `b` being dirty?
Base
subclass via helper method.Base
subclass via helper method.
When a helper method manipulates fields on a Base instance that is associated with a state, recursively wrap the helper method with the MutableProxy so it also tracks changes.
If the helper method returns a mutable object, this object will also be tied back to the same field on the state to allow for "get_*" type functions that still track changes.
Fix #2239