computed var default to cache=True (#4194)

* computed var default to cache=True

* fix lifespan integration tests

* fix redis test
This commit is contained in:
Thomas Brandého 2025-01-16 12:49:54 -08:00 committed by GitHub
parent c8de356d98
commit 6e546526b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 68 additions and 93 deletions

View File

@ -1200,7 +1200,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
fget=func,
auto_deps=False,
deps=["router"],
cache=True,
_js_expr=param,
_var_data=VarData.from_state(cls),
)

View File

@ -1838,7 +1838,7 @@ class ComputedVar(Var[RETURN_TYPE]):
self,
fget: Callable[[BASE_STATE], RETURN_TYPE],
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
cache: bool = False,
cache: bool = True,
deps: Optional[List[Union[str, Var]]] = None,
auto_deps: bool = True,
interval: Optional[Union[int, datetime.timedelta]] = None,
@ -2253,7 +2253,7 @@ if TYPE_CHECKING:
def computed_var(
fget: None = None,
initial_value: Any | types.Unset = types.Unset(),
cache: bool = False,
cache: bool = True,
deps: Optional[List[Union[str, Var]]] = None,
auto_deps: bool = True,
interval: Optional[Union[datetime.timedelta, int]] = None,
@ -2266,7 +2266,7 @@ def computed_var(
def computed_var(
fget: Callable[[BASE_STATE], RETURN_TYPE],
initial_value: RETURN_TYPE | types.Unset = types.Unset(),
cache: bool = False,
cache: bool = True,
deps: Optional[List[Union[str, Var]]] = None,
auto_deps: bool = True,
interval: Optional[Union[datetime.timedelta, int]] = None,
@ -2278,7 +2278,7 @@ def computed_var(
def computed_var(
fget: Callable[[BASE_STATE], Any] | None = None,
initial_value: Any | types.Unset = types.Unset(),
cache: Optional[bool] = None,
cache: bool = True,
deps: Optional[List[Union[str, Var]]] = None,
auto_deps: bool = True,
interval: Optional[Union[datetime.timedelta, int]] = None,
@ -2304,15 +2304,6 @@ def computed_var(
ValueError: If caching is disabled and an update interval is set.
VarDependencyError: If user supplies dependencies without caching.
"""
if cache is None:
cache = False
console.deprecate(
"Default non-cached rx.var",
"the default value will be `@rx.var(cache=True)` in a future release. "
"To retain uncached var, explicitly pass `@rx.var(cache=False)`",
deprecation_version="0.6.8",
removal_version="0.7.0",
)
if cache is False and interval is not None:
raise ValueError("Cannot set update interval without caching.")

View File

@ -22,22 +22,22 @@ def ComputedVars():
count: int = 0
# cached var with dep on count
@rx.var(cache=True, interval=15)
@rx.var(interval=15)
def count1(self) -> int:
return self.count
# cached backend var with dep on count
@rx.var(cache=True, interval=15, backend=True)
@rx.var(interval=15, backend=True)
def count1_backend(self) -> int:
return self.count
# same as above but implicit backend with `_` prefix
@rx.var(cache=True, interval=15)
@rx.var(interval=15)
def _count1_backend(self) -> int:
return self.count
# explicit disabled auto_deps
@rx.var(interval=15, cache=True, auto_deps=False)
@rx.var(interval=15, auto_deps=False)
def count3(self) -> int:
# this will not add deps, because auto_deps is False
print(self.count1)
@ -45,16 +45,19 @@ def ComputedVars():
return self.count
# explicit dependency on count var
@rx.var(cache=True, deps=["count"], auto_deps=False)
@rx.var(deps=["count"], auto_deps=False)
def depends_on_count(self) -> int:
return self.count
# explicit dependency on count1 var
@rx.var(cache=True, deps=[count1], auto_deps=False)
@rx.var(deps=[count1], auto_deps=False)
def depends_on_count1(self) -> int:
return self.count
@rx.var(deps=[count3], auto_deps=False, cache=True)
@rx.var(
deps=[count3],
auto_deps=False,
)
def depends_on_count3(self) -> int:
return self.count

View File

@ -74,16 +74,16 @@ def DynamicRoute():
class ArgState(rx.State):
"""The app state."""
@rx.var
@rx.var(cache=False)
def arg(self) -> int:
return int(self.arg_str or 0)
class ArgSubState(ArgState):
@rx.var(cache=True)
@rx.var
def cached_arg(self) -> int:
return self.arg
@rx.var(cache=True)
@rx.var
def cached_arg_str(self) -> str:
return self.arg_str

View File

@ -36,7 +36,7 @@ def LifespanApp():
print("Lifespan global started.")
try:
while True:
lifespan_task_global += inc # pyright: ignore[reportUnboundVariable]
lifespan_task_global += inc # pyright: ignore[reportUnboundVariable, reportPossiblyUnboundVariable]
await asyncio.sleep(0.1)
except asyncio.CancelledError as ce:
print(f"Lifespan global cancelled: {ce}.")
@ -45,11 +45,11 @@ def LifespanApp():
class LifespanState(rx.State):
interval: int = 100
@rx.var
@rx.var(cache=False)
def task_global(self) -> int:
return lifespan_task_global
@rx.var
@rx.var(cache=False)
def context_global(self) -> int:
return lifespan_context_global

View File

@ -22,31 +22,31 @@ def MediaApp():
img.format = format # type: ignore
return img
@rx.var(cache=True)
@rx.var
def img_default(self) -> Image.Image:
return self._blue()
@rx.var(cache=True)
@rx.var
def img_bmp(self) -> Image.Image:
return self._blue(format="BMP")
@rx.var(cache=True)
@rx.var
def img_jpg(self) -> Image.Image:
return self._blue(format="JPEG")
@rx.var(cache=True)
@rx.var
def img_png(self) -> Image.Image:
return self._blue(format="PNG")
@rx.var(cache=True)
@rx.var
def img_gif(self) -> Image.Image:
return self._blue(format="GIF")
@rx.var(cache=True)
@rx.var
def img_webp(self) -> Image.Image:
return self._blue(format="WEBP")
@rx.var(cache=True)
@rx.var
def img_from_url(self) -> Image.Image:
img_url = "https://picsum.photos/id/1/200/300"
img_resp = httpx.get(img_url, follow_redirects=True)

View File

@ -908,7 +908,7 @@ class DynamicState(BaseState):
"""Increment the counter var."""
self.counter = self.counter + 1
@computed_var(cache=True)
@computed_var
def comp_dynamic(self) -> str:
"""A computed var that depends on the dynamic var.
@ -1549,11 +1549,11 @@ def test_app_with_valid_var_dependencies(compilable_app: tuple[App, Path]):
base: int = 0
_backend: int = 0
@computed_var(cache=True)
@computed_var()
def foo(self) -> str:
return "foo"
@computed_var(deps=["_backend", "base", foo], cache=True)
@computed_var(deps=["_backend", "base", foo])
def bar(self) -> str:
return "bar"
@ -1565,7 +1565,7 @@ def test_app_with_invalid_var_dependencies(compilable_app: tuple[App, Path]):
app, _ = compilable_app
class InvalidDepState(BaseState):
@computed_var(deps=["foolksjdf"], cache=True)
@computed_var(deps=["foolksjdf"])
def bar(self) -> str:
return "bar"

View File

@ -202,7 +202,7 @@ class GrandchildState(ChildState):
class GrandchildState2(ChildState2):
"""A grandchild state fixture."""
@rx.var(cache=True)
@rx.var
def cached(self) -> str:
"""A cached var.
@ -215,7 +215,7 @@ class GrandchildState2(ChildState2):
class GrandchildState3(ChildState3):
"""A great grandchild state fixture."""
@rx.var
@rx.var(cache=False)
def computed(self) -> str:
"""A computed var.
@ -796,7 +796,7 @@ async def test_process_event_simple(test_state):
# The delta should contain the changes, including computed vars.
assert update.delta == {
TestState.get_full_name(): {"num1": 69, "sum": 72.14, "upper": ""},
TestState.get_full_name(): {"num1": 69, "sum": 72.14},
GrandchildState3.get_full_name(): {"computed": ""},
}
assert update.events == []
@ -823,7 +823,7 @@ async def test_process_event_substate(test_state, child_state, grandchild_state)
assert child_state.value == "HI"
assert child_state.count == 24
assert update.delta == {
TestState.get_full_name(): {"sum": 3.14, "upper": ""},
# TestState.get_full_name(): {"sum": 3.14, "upper": ""},
ChildState.get_full_name(): {"value": "HI", "count": 24},
GrandchildState3.get_full_name(): {"computed": ""},
}
@ -839,7 +839,7 @@ async def test_process_event_substate(test_state, child_state, grandchild_state)
update = await test_state._process(event).__anext__()
assert grandchild_state.value2 == "new"
assert update.delta == {
TestState.get_full_name(): {"sum": 3.14, "upper": ""},
# TestState.get_full_name(): {"sum": 3.14, "upper": ""},
GrandchildState.get_full_name(): {"value2": "new"},
GrandchildState3.get_full_name(): {"computed": ""},
}
@ -989,7 +989,7 @@ class InterdependentState(BaseState):
v1: int = 0
_v2: int = 1
@rx.var(cache=True)
@rx.var
def v1x2(self) -> int:
"""Depends on var v1.
@ -998,7 +998,7 @@ class InterdependentState(BaseState):
"""
return self.v1 * 2
@rx.var(cache=True)
@rx.var
def v2x2(self) -> int:
"""Depends on backend var _v2.
@ -1007,7 +1007,7 @@ class InterdependentState(BaseState):
"""
return self._v2 * 2
@rx.var(cache=True, backend=True)
@rx.var(backend=True)
def v2x2_backend(self) -> int:
"""Depends on backend var _v2.
@ -1016,7 +1016,7 @@ class InterdependentState(BaseState):
"""
return self._v2 * 2
@rx.var(cache=True)
@rx.var
def v1x2x2(self) -> int:
"""Depends on ComputedVar v1x2.
@ -1025,7 +1025,7 @@ class InterdependentState(BaseState):
"""
return self.v1x2 * 2 # type: ignore
@rx.var(cache=True)
@rx.var
def _v3(self) -> int:
"""Depends on backend var _v2.
@ -1034,7 +1034,7 @@ class InterdependentState(BaseState):
"""
return self._v2
@rx.var(cache=True)
@rx.var
def v3x2(self) -> int:
"""Depends on ComputedVar _v3.
@ -1239,7 +1239,7 @@ def test_computed_var_cached():
class ComputedState(BaseState):
v: int = 0
@rx.var(cache=True)
@rx.var
def comp_v(self) -> int:
nonlocal comp_v_calls
comp_v_calls += 1
@ -1264,15 +1264,15 @@ def test_computed_var_cached_depends_on_non_cached():
class ComputedState(BaseState):
v: int = 0
@rx.var
@rx.var(cache=False)
def no_cache_v(self) -> int:
return self.v
@rx.var(cache=True)
@rx.var
def dep_v(self) -> int:
return self.no_cache_v # type: ignore
@rx.var(cache=True)
@rx.var
def comp_v(self) -> int:
return self.v
@ -1304,14 +1304,14 @@ def test_computed_var_depends_on_parent_non_cached():
counter = 0
class ParentState(BaseState):
@rx.var
@rx.var(cache=False)
def no_cache_v(self) -> int:
nonlocal counter
counter += 1
return counter
class ChildState(ParentState):
@rx.var(cache=True)
@rx.var
def dep_v(self) -> int:
return self.no_cache_v # type: ignore
@ -1357,7 +1357,7 @@ def test_cached_var_depends_on_event_handler(use_partial: bool):
def handler(self):
self.x = self.x + 1
@rx.var(cache=True)
@rx.var
def cached_x_side_effect(self) -> int:
self.handler()
nonlocal counter
@ -1393,7 +1393,7 @@ def test_computed_var_dependencies():
def testprop(self) -> int:
return self.v
@rx.var(cache=True)
@rx.var
def comp_v(self) -> int:
"""Direct access.
@ -1402,7 +1402,7 @@ def test_computed_var_dependencies():
"""
return self.v
@rx.var(cache=True, backend=True)
@rx.var(backend=True)
def comp_v_backend(self) -> int:
"""Direct access backend var.
@ -1411,7 +1411,7 @@ def test_computed_var_dependencies():
"""
return self.v
@rx.var(cache=True)
@rx.var
def comp_v_via_property(self) -> int:
"""Access v via property.
@ -1420,7 +1420,7 @@ def test_computed_var_dependencies():
"""
return self.testprop
@rx.var(cache=True)
@rx.var
def comp_w(self):
"""Nested lambda.
@ -1429,7 +1429,7 @@ def test_computed_var_dependencies():
"""
return lambda: self.w
@rx.var(cache=True)
@rx.var
def comp_x(self):
"""Nested function.
@ -1442,7 +1442,7 @@ def test_computed_var_dependencies():
return _
@rx.var(cache=True)
@rx.var
def comp_y(self) -> List[int]:
"""Comprehension iterating over attribute.
@ -1451,7 +1451,7 @@ def test_computed_var_dependencies():
"""
return [round(y) for y in self.y]
@rx.var(cache=True)
@rx.var
def comp_z(self) -> List[bool]:
"""Comprehension accesses attribute.
@ -2027,10 +2027,6 @@ async def test_state_proxy(grandchild_state: GrandchildState, mock_app: rx.App):
assert mcall.args[0] == str(SocketEvent.EVENT)
assert mcall.args[1] == StateUpdate(
delta={
parent_state.get_full_name(): {
"upper": "",
"sum": 3.14,
},
grandchild_state.get_full_name(): {
"value2": "42",
},
@ -2053,7 +2049,7 @@ class BackgroundTaskState(BaseState):
super().__init__(**kwargs)
self.router_data = {"simulate": "hydrate"}
@rx.var
@rx.var(cache=False)
def computed_order(self) -> List[str]:
"""Get the order as a computed var.
@ -3040,10 +3036,6 @@ async def test_get_state(mock_app: rx.App, token: str):
grandchild_state.value2 = "set_value"
assert test_state.get_delta() == {
TestState.get_full_name(): {
"sum": 3.14,
"upper": "",
},
GrandchildState.get_full_name(): {
"value2": "set_value",
},
@ -3081,10 +3073,6 @@ async def test_get_state(mock_app: rx.App, token: str):
child_state2.value = "set_c2_value"
assert new_test_state.get_delta() == {
TestState.get_full_name(): {
"sum": 3.14,
"upper": "",
},
ChildState2.get_full_name(): {
"value": "set_c2_value",
},
@ -3139,7 +3127,7 @@ async def test_get_state_from_sibling_not_cached(mock_app: rx.App, token: str):
child3_var: int = 0
@rx.var
@rx.var(cache=False)
def v(self):
pass
@ -3210,8 +3198,8 @@ def test_potentially_dirty_substates():
def bar(self) -> str:
return ""
assert RxState._potentially_dirty_substates() == {State}
assert State._potentially_dirty_substates() == {C1}
assert RxState._potentially_dirty_substates() == set()
assert State._potentially_dirty_substates() == set()
assert C1._potentially_dirty_substates() == set()
@ -3226,7 +3214,7 @@ def test_router_var_dep() -> None:
class RouterVarDepState(RouterVarParentState):
"""A state with a router var dependency."""
@rx.var(cache=True)
@rx.var
def foo(self) -> str:
return self.router.page.params.get("foo", "")
@ -3421,7 +3409,7 @@ class MixinState(State, mixin=True):
_backend: int = 0
_backend_no_default: dict
@rx.var(cache=True)
@rx.var
def computed(self) -> str:
"""A computed var on mixin state.

View File

@ -42,7 +42,7 @@ class SubA_A_A_A(SubA_A_A):
class SubA_A_A_B(SubA_A_A):
"""SubA_A_A_B is a child of SubA_A_A."""
@rx.var(cache=True)
@rx.var
def sub_a_a_a_cached(self) -> int:
"""A cached var.
@ -117,7 +117,7 @@ class TreeD(Root):
d: int
@rx.var
@rx.var(cache=False)
def d_var(self) -> int:
"""A computed var.
@ -156,7 +156,7 @@ class SubE_A_A_A_A(SubE_A_A_A):
sub_e_a_a_a_a: int
@rx.var
@rx.var(cache=False)
def sub_e_a_a_a_a_var(self) -> int:
"""A computed var.
@ -183,7 +183,7 @@ class SubE_A_A_A_D(SubE_A_A_A):
sub_e_a_a_a_d: int
@rx.var(cache=True)
@rx.var
def sub_e_a_a_a_d_var(self) -> int:
"""A computed var.

View File

@ -1814,10 +1814,7 @@ def cv_fget(state: BaseState) -> int:
],
)
def test_computed_var_deps(deps: List[Union[str, Var]], expected: Set[str]):
@computed_var(
deps=deps,
cache=True,
)
@computed_var(deps=deps)
def test_var(state) -> int:
return 1
@ -1835,10 +1832,7 @@ def test_computed_var_deps(deps: List[Union[str, Var]], expected: Set[str]):
def test_invalid_computed_var_deps(deps: List):
with pytest.raises(TypeError):
@computed_var(
deps=deps,
cache=True,
)
@computed_var(deps=deps)
def test_var(state) -> int:
return 1