Skip to content

Commit 0ef2a87

Browse files
authored
MutableProxy: do not rebind self for classmethods (#6045)
When the method's `__self__` is a class, then we just return the classmethod value directly with no rebinding.
1 parent 07f01f7 commit 0ef2a87

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

reflex/istate/proxy.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -560,11 +560,15 @@ def __getattr__(self, __name: str) -> Any:
560560
)
561561

562562
if (
563-
not isinstance(self.__wrapped__, Base)
564-
or __name not in NEVER_WRAP_BASE_ATTRS
565-
) and hasattr(value, "__func__"):
563+
(
564+
not isinstance(self.__wrapped__, Base)
565+
or __name not in NEVER_WRAP_BASE_ATTRS
566+
)
567+
and (func := getattr(value, "__func__", None)) is not None
568+
and not inspect.isclass(getattr(value, "__self__", None))
569+
):
566570
# Rebind `self` to the proxy on methods to capture nested mutations.
567-
return functools.partial(value.__func__, self) # pyright: ignore [reportFunctionMemberAccess, reportAttributeAccessIssue]
571+
return functools.partial(func, self)
568572

569573
if is_mutable_type(type(value)) and __name not in (
570574
"__wrapped__",

tests/units/test_state.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,18 @@ def append_to_ls(self, item: dict):
20892089
"""
20902090
self.ls.append(item)
20912091

2092+
@classmethod
2093+
def from_dict(cls, data: dict) -> ModelDC:
2094+
"""Create an instance of ModelDC from a dictionary.
2095+
2096+
Args:
2097+
data: The dictionary to create the instance from.
2098+
2099+
Returns:
2100+
An instance of ModelDC.
2101+
"""
2102+
return cls(**data)
2103+
20922104

20932105
@pytest.mark.asyncio
20942106
async def test_state_proxy(
@@ -3945,6 +3957,11 @@ def test_mutable_models():
39453957
assert state.dirty_vars == set()
39463958
state.dc.append_to_ls({"new": "item"})
39473959
assert state.dirty_vars == {"dc"}
3960+
state.dirty_vars.clear()
3961+
3962+
dc_from_dict = state.dc.from_dict({"foo": "from_dict", "ls": []})
3963+
assert dc_from_dict == ModelDC(foo="from_dict", ls=[])
3964+
assert state.dirty_vars == set()
39483965

39493966

39503967
def test_dict_and_get_delta():

0 commit comments

Comments
 (0)