diff --git a/CHANGELOG.md b/CHANGELOG.md index 10f448d11..8a88912cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features +* Closed #814: The functions `reactive.Calc` and `reactive.Effect` have been changed to have lowercase names: `reactive.calc`, and `reactive.effect`. The old capitalized names are now aliases to the new lowercase names, so existing code will continue to work. Similarly, the class `reactive.Value` has a new alias, `reactive.value`, but in this case, since the original was a class, it keeps the original capitalized name as the primary name. The examples have not been changed yet, but will be changed in a future release. (#822) + ### Bug fixes * Fix support for `shiny.ui.accordion(multiple=)` (#799). diff --git a/docs/_quartodoc.yml b/docs/_quartodoc.yml index 98f38e7af..cc1dd3353 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc.yml @@ -186,6 +186,9 @@ quartodoc: - title: Reactive programming desc: "" contents: + - reactive.calc + - reactive.effect + - reactive.value - reactive.Calc - reactive.Effect - reactive.Value diff --git a/shiny/reactive/__init__.py b/shiny/reactive/__init__.py index 1de7c5bac..b76543a5e 100644 --- a/shiny/reactive/__init__.py +++ b/shiny/reactive/__init__.py @@ -8,10 +8,13 @@ ) from ._poll import poll, file_reader from ._reactives import ( # noqa: F401 + value, Value, + calc, Calc, Calc_, # pyright: ignore[reportUnusedImport] CalcAsync_, # pyright: ignore[reportUnusedImport] + effect, Effect, Effect_, # pyright: ignore[reportUnusedImport] event, @@ -26,8 +29,11 @@ "on_flushed", "poll", "file_reader", + "value", "Value", + "calc", "Calc", + "effect", "Effect", "event", ) diff --git a/shiny/reactive/_poll.py b/shiny/reactive/_poll.py index 053e17d5f..479757ae5 100644 --- a/shiny/reactive/_poll.py +++ b/shiny/reactive/_poll.py @@ -99,7 +99,7 @@ def poll( last_value: reactive.Value[Any] = reactive.Value(poll_func()) last_error: reactive.Value[Optional[Exception]] = reactive.Value(None) - @reactive.Effect(priority=priority, session=session) + @reactive.effect(priority=priority, session=session) async def _(): try: if _utils.is_async_callable(poll_func): @@ -159,7 +159,7 @@ async def _(): def wrapper(fn: Callable[[], T]) -> Callable[[], T]: if _utils.is_async_callable(fn): - @reactive.Calc(session=session) + @reactive.calc(session=session) @functools.wraps(fn) async def result_async() -> T: # If an error occurred, raise it @@ -182,7 +182,7 @@ async def result_async() -> T: else: - @reactive.Calc(session=session) + @reactive.calc(session=session) @functools.wraps(fn) def result_sync() -> T: # If an error occurred, raise it diff --git a/shiny/reactive/_reactives.py b/shiny/reactive/_reactives.py index 630a6d093..fd724b130 100644 --- a/shiny/reactive/_reactives.py +++ b/shiny/reactive/_reactives.py @@ -2,7 +2,18 @@ from __future__ import annotations -__all__ = ("Value", "Calc", "Calc_", "CalcAsync_", "Effect", "Effect_", "event") +__all__ = ( + "value", + "Value", + "calc", + "Calc", + "Calc_", + "CalcAsync_", + "effect", + "Effect", + "Effect_", + "event", +) import functools import traceback @@ -204,6 +215,8 @@ def freeze(self) -> None: self._value = MISSING +value = Value + # ============================================================================== # Calc # ============================================================================== @@ -343,12 +356,12 @@ async def __call__(self) -> T: # pyright: ignore[reportIncompatibleMethodOverri @overload -def Calc(fn: CalcFunctionAsync[T]) -> CalcAsync_[T]: +def calc(fn: CalcFunctionAsync[T]) -> CalcAsync_[T]: ... @overload -def Calc(fn: CalcFunction[T]) -> Calc_[T]: +def calc(fn: CalcFunction[T]) -> Calc_[T]: ... @@ -373,14 +386,14 @@ def Calc(fn: CalcFunction[T]) -> Calc_[T]: # __call__ method) or CalcAsync object (which has an async __call__ method), and it # works out. @overload -def Calc( +def calc( *, session: "MISSING_TYPE | Session | None" = MISSING ) -> Callable[[CalcFunction[T]], Calc_[T]]: ... @add_example() -def Calc( +def calc( fn: Optional[CalcFunction[T] | CalcFunctionAsync[T]] = None, *, session: "MISSING_TYPE | Session | None" = MISSING, @@ -434,6 +447,9 @@ def create_calc(fn: CalcFunction[T] | CalcFunctionAsync[T]) -> Calc_[T]: return create_calc(fn) +# Alias for backward compatibility +Calc = calc + # ============================================================================== # Effect # ============================================================================== @@ -630,12 +646,12 @@ def _on_session_ended_cb(self) -> None: @overload -def Effect(fn: EffectFunction | EffectFunctionAsync) -> Effect_: +def effect(fn: EffectFunction | EffectFunctionAsync) -> Effect_: ... @overload -def Effect( +def effect( *, suspended: bool = False, priority: int = 0, @@ -645,7 +661,7 @@ def Effect( @add_example() -def Effect( +def effect( fn: Optional[EffectFunction | EffectFunctionAsync] = None, *, suspended: bool = False, @@ -706,6 +722,9 @@ def create_effect(fn: EffectFunction | EffectFunctionAsync) -> Effect_: return create_effect(fn) +Effect = effect + + # ============================================================================== # event decorator # ============================================================================== diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 359dd6ce4..b46eec44c 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: from .._app import App -from .. import _utils, render +from .. import _utils, reactive, render from .._connection import Connection, ConnectionClosed from .._docstring import add_example from .._fileupload import FileInfo, FileUploadManager @@ -46,7 +46,7 @@ from .._utils import wrap_async from ..http_staticfiles import FileResponse from ..input_handler import input_handlers -from ..reactive import Effect, Effect_, Value, flush, isolate +from ..reactive import Effect_, Value, effect, flush, isolate from ..reactive._core import lock, on_flushed from ..render.transformer import OutputRenderer from ..types import SafeException, SilentCancelOutputException, SilentException @@ -904,7 +904,7 @@ def __init__( self._ns = ns def __setitem__(self, key: str, value: Value[Any]) -> None: - if not isinstance(value, Value): + if not isinstance(value, reactive.Value): raise TypeError("`value` must be a reactive.Value object.") self._map[self._ns(key)] = value @@ -1008,7 +1008,7 @@ def set_renderer(renderer_fn: OutputRenderer[OT]) -> OutputRenderer[OT]: self._suspend_when_hidden[output_name] = suspend_when_hidden - @Effect( + @effect( suspended=suspend_when_hidden and self._session._is_hidden(output_name), priority=priority, ) diff --git a/tests/deploys/apps/plotly_app/app.py b/tests/deploys/apps/plotly_app/app.py index cf780ae39..ea775b1cf 100644 --- a/tests/deploys/apps/plotly_app/app.py +++ b/tests/deploys/apps/plotly_app/app.py @@ -61,7 +61,7 @@ def summary_data(): height="100%", ) - @reactive.Calc + @reactive.calc def filtered_df(): selected_idx = list(req(input.summary_data_selected_rows())) countries = summary_df["country"][selected_idx] @@ -116,7 +116,7 @@ def synchronize_size(output_id): def wrapper(func): input = session.get_current_session().input - @reactive.Effect + @reactive.effect def size_updater(): func( input[f".clientdata_output_{output_id}_width"](), diff --git a/tests/e2e/TODO/sidebar/app.py b/tests/e2e/TODO/sidebar/app.py index 8e71a34ce..387b1ca80 100644 --- a/tests/e2e/TODO/sidebar/app.py +++ b/tests/e2e/TODO/sidebar/app.py @@ -62,24 +62,24 @@ def server(input: Inputs, output: Outputs, session: Session) -> None: def ui_content(): return f"Hello, {input.adjective()} {input.animal()}!" - @reactive.Effect + @reactive.effect @reactive.event(input.open_all) def _(): ui.update_sidebar("sidebar_inner", show=True) ui.update_sidebar("sidebar_outer", show=True) - @reactive.Effect + @reactive.effect @reactive.event(input.close_all) def _(): ui.update_sidebar("sidebar_inner", show=False) ui.update_sidebar("sidebar_outer", show=False) - @reactive.Effect + @reactive.effect @reactive.event(input.toggle_inner) def _(): ui.update_sidebar("sidebar_inner", show=not input.sidebar_inner()) - @reactive.Effect + @reactive.effect @reactive.event(input.toggle_outer) def _(): ui.update_sidebar("sidebar_outer", show=not input.sidebar_outer()) diff --git a/tests/e2e/TODO/update_popover/app.py b/tests/e2e/TODO/update_popover/app.py index 55b6d1c2b..639987639 100644 --- a/tests/e2e/TODO/update_popover/app.py +++ b/tests/e2e/TODO/update_popover/app.py @@ -14,12 +14,12 @@ def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect + @reactive.effect def _(): # Immediately display popover ui.update_popover("popover_id", show=True) - @reactive.Effect + @reactive.effect @reactive.event(input.btn_update) def _(): content = ( @@ -33,7 +33,7 @@ def _(): # show=True ) - @reactive.Effect + @reactive.effect def _(): req(input.btn_w_popover()) ui.notification_show("Button clicked!", duration=3, type="message") diff --git a/tests/e2e/async/app.py b/tests/e2e/async/app.py index 324291f29..0f1dcf63f 100644 --- a/tests/e2e/async/app.py +++ b/tests/e2e/async/app.py @@ -31,7 +31,7 @@ async def hash_output(): content = await hash_result() return content - @reactive.Calc() + @reactive.calc() async def hash_result() -> str: with ui.Progress() as p: p.set(message="Calculating...") diff --git a/tests/e2e/bugs/0648-update-slider-datetime-value/app.py b/tests/e2e/bugs/0648-update-slider-datetime-value/app.py index 15f5e62bd..ec5343723 100644 --- a/tests/e2e/bugs/0648-update-slider-datetime-value/app.py +++ b/tests/e2e/bugs/0648-update-slider-datetime-value/app.py @@ -51,7 +51,7 @@ def txt(): else: return input.times() - @reactive.Effect + @reactive.effect @reactive.event(input.reset) def reset_time(): ui.update_slider("times", min=min, max=max, value=value) diff --git a/tests/e2e/bugs/0696-resolve-id/app.py b/tests/e2e/bugs/0696-resolve-id/app.py index f4d6e4fd7..0365ae5b7 100644 --- a/tests/e2e/bugs/0696-resolve-id/app.py +++ b/tests/e2e/bugs/0696-resolve-id/app.py @@ -217,7 +217,7 @@ def mod_x_server( ): session_dict[session.ns] = session - @reactive.Calc + @reactive.calc def n(): return int(letters.index(input.input_radio_buttons())) @@ -394,7 +394,7 @@ def server(input: Inputs, output: Outputs, session: Session): ui.update_popover("explanation", show=True) # On button clicks, hide the explanation popover - @reactive.Effect(suspended=True) + @reactive.effect(suspended=True) def _(): input.reset() input.update_global() @@ -482,7 +482,7 @@ def update_session( for _input_key, session_key in module_keys: offsets[session_key] = 0 - @reactive.Effect + @reactive.effect def _(): # trigger reactivity input.reset() @@ -492,7 +492,7 @@ def _(): update_session(session_dict[session_key], count=0) def update_session_effect(*, input_key: str, session_key: str) -> None: - @reactive.Effect + @reactive.effect @reactive.event(input[input_key]) def _(): update_session( diff --git a/tests/e2e/inputs/input_file/app.py b/tests/e2e/inputs/input_file/app.py index 225f2bde8..eea183e86 100644 --- a/tests/e2e/inputs/input_file/app.py +++ b/tests/e2e/inputs/input_file/app.py @@ -20,7 +20,7 @@ def server(input: Inputs, output: Outputs, session: Session): - @reactive.Calc + @reactive.calc def parsed_file(): file: typing.Union[typing.List["FileInfo"], None] = input.file1() if file is None: diff --git a/tests/e2e/navs/navset_hidden/app.py b/tests/e2e/navs/navset_hidden/app.py index 09ff8d859..82771e41e 100644 --- a/tests/e2e/navs/navset_hidden/app.py +++ b/tests/e2e/navs/navset_hidden/app.py @@ -20,7 +20,7 @@ def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect + @reactive.effect @reactive.event(input.controller) def _(): ui.update_navs("hidden_tabs", selected="panel" + str(input.controller())) diff --git a/tests/e2e/session/flush/app.py b/tests/e2e/session/flush/app.py index 8cc62011a..38e6643c9 100644 --- a/tests/e2e/session/flush/app.py +++ b/tests/e2e/session/flush/app.py @@ -125,11 +125,11 @@ def _(): # Continuously trigger the reactive graph to ensure that the flush / flushed # callbacks are called. If this Effect is not called, then the click counter will # always be one higher than the flush/flushed values displayed. - @reactive.Effect + @reactive.effect def _(): reactive.invalidate_later(0.25) - @reactive.Effect + @reactive.effect @reactive.event(input.btn) def _(): btn_count = input.btn() diff --git a/tests/e2e/ui/accordion/app.py b/tests/e2e/ui/accordion/app.py index ff07c5d8f..ddea80b5d 100644 --- a/tests/e2e/ui/accordion/app.py +++ b/tests/e2e/ui/accordion/app.py @@ -29,14 +29,14 @@ def make_panel(letter: str) -> ui.AccordionPanel: def server(input: Inputs, output: Outputs, session: Session) -> None: - @reactive.Calc + @reactive.calc def acc() -> list[str]: acc_val: list[str] | None = input.acc() if acc_val is None: acc_val = [] return acc_val - @reactive.Effect + @reactive.effect def _(): req(input.toggle_b()) @@ -46,12 +46,12 @@ def _(): else: ui.update_accordion_panel("acc", "Section B", show=True) - @reactive.Effect + @reactive.effect def _(): req(input.open_all()) ui.update_accordion("acc", show=True) - @reactive.Effect + @reactive.effect def _(): req(input.close_all()) ui.update_accordion("acc", show=False) @@ -60,7 +60,7 @@ def _(): has_alternate = True has_updates = False - @reactive.Effect + @reactive.effect def _(): req(input.alternate()) @@ -79,7 +79,7 @@ def _(): ui.update_accordion("acc", show=sections) has_alternate = not has_alternate - @reactive.Effect + @reactive.effect def _(): req(input.toggle_efg()) @@ -93,7 +93,7 @@ def _(): has_efg = not has_efg - @reactive.Effect + @reactive.effect def _(): req(input.toggle_updates()) diff --git a/tests/e2e/ui/popover/app.py b/tests/e2e/ui/popover/app.py index 45ea2191b..c7add2786 100644 --- a/tests/e2e/ui/popover/app.py +++ b/tests/e2e/ui/popover/app.py @@ -15,19 +15,19 @@ def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect + @reactive.effect def _(): req(input.btn_show()) ui.update_popover("popover_id", show=True) - @reactive.Effect + @reactive.effect def _(): req(input.btn_close()) ui.update_popover("popover_id", show=False) - @reactive.Effect + @reactive.effect def _(): req(input.btn_w_popover()) ui.notification_show("Button clicked!", duration=3, type="message") diff --git a/tests/e2e/ui/tooltip/app.py b/tests/e2e/ui/tooltip/app.py index fcf6cb208..375d32eb4 100644 --- a/tests/e2e/ui/tooltip/app.py +++ b/tests/e2e/ui/tooltip/app.py @@ -15,19 +15,19 @@ def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect + @reactive.effect def _(): req(input.btn_show()) ui.update_tooltip("tooltip_id", show=True) - @reactive.Effect + @reactive.effect def _(): req(input.btn_close()) ui.update_tooltip("tooltip_id", show=False) - @reactive.Effect + @reactive.effect def _(): req(input.btn_w_tooltip()) ui.notification_show("Button clicked!", duration=3, type="message") diff --git a/tests/pytest/test_modules.py b/tests/pytest/test_modules.py index 5c9e6e2cc..1b91fc7d7 100644 --- a/tests/pytest/test_modules.py +++ b/tests/pytest/test_modules.py @@ -45,11 +45,11 @@ async def test_session_scoping(): @module.server def inner_server(input: Inputs, output: Outputs, session: Session): - @reactive.Calc + @reactive.calc def out(): return get_current_session() - @reactive.Effect + @reactive.effect def _(): sessions["inner"] = session sessions["inner_current"] = get_current_session() @@ -59,11 +59,11 @@ def _(): @module.server def outer_server(input: Inputs, output: Outputs, session: Session): - @reactive.Calc + @reactive.calc def out(): return get_current_session() - @reactive.Effect + @reactive.effect def _(): inner_server("mod_inner") sessions["outer"] = session @@ -75,11 +75,11 @@ def _(): def server(input: Inputs, output: Outputs, session: Session): outer_server("mod_outer") - @reactive.Calc + @reactive.calc def out(): return get_current_session() - @reactive.Effect + @reactive.effect def _(): sessions["top"] = session sessions["top_current"] = get_current_session() diff --git a/tests/pytest/test_poll.py b/tests/pytest/test_poll.py index 4630669b4..46b5b298c 100644 --- a/tests/pytest/test_poll.py +++ b/tests/pytest/test_poll.py @@ -11,7 +11,7 @@ from shiny import Session, _utils, session from shiny._namespaces import Root -from shiny.reactive import Effect, Value, file_reader, flush, isolate, poll +from shiny.reactive import Value, effect, file_reader, flush, isolate, poll from .mocktime import MockTime @@ -83,7 +83,7 @@ def value_func(): # @poll returns a lazy Calc, so value hasn't been invoked yet. assert (poll_invocations, value_invocations) == (2, 0) - @Effect() + @effect() def _(): value_func() @@ -157,7 +157,7 @@ def bad_poll() -> Any: invocations = 0 - @Effect() + @effect() def _(): nonlocal invocations invocations += 1 diff --git a/tests/pytest/test_reactives.py b/tests/pytest/test_reactives.py index e57c056d1..45eb1eed3 100644 --- a/tests/pytest/test_reactives.py +++ b/tests/pytest/test_reactives.py @@ -7,7 +7,7 @@ from shiny import App, render, req, ui from shiny._connection import MockConnection -from shiny.reactive import Calc, Effect, Value, event, flush, invalidate_later, isolate +from shiny.reactive import Value, calc, effect, event, flush, invalidate_later, isolate from shiny.reactive._core import ReactiveWarning from shiny.types import ActionButtonValue, SilentException @@ -28,12 +28,12 @@ async def test_flush_runs_newly_invalidated(): # In practice, on the first flush, Effects run in the order that they were created. # Our test checks that o2 runs _after_ o1. - @Effect() + @effect() def o2(): nonlocal v2_result v2_result = v2() - @Effect() + @effect() def o1(): v2.set(v1()) @@ -57,12 +57,12 @@ async def test_flush_runs_newly_invalidated_async(): # In practice, on the first flush, Effects run in the order that they were # created. Our test checks that o2 runs _after_ o1. - @Effect() + @effect() async def o2(): nonlocal v2_result v2_result = v2() - @Effect() + @effect() async def o1(): v2.set(v1()) @@ -79,7 +79,7 @@ async def o1(): async def test_reactive_value_same_no_invalidate(): v = Value(1) - @Effect() + @effect() def o(): v() @@ -105,7 +105,7 @@ async def test_reactive_value_unset(): val: int = 0 - @Effect() + @effect() def o(): nonlocal val val = v() @@ -139,7 +139,7 @@ async def test_reactive_value_is_set(): v = Value[int]() v_is_set: bool = False - @Effect() + @effect() def o(): nonlocal v_is_set v_is_set = v.is_set() @@ -181,14 +181,14 @@ def o(): async def test_recursive_calc(): v = Value(5) - @Calc() + @calc() def r(): if v() == 0: return 0 v.set(v() - 1) r() - @Effect() + @effect() def o(): r() @@ -203,14 +203,14 @@ def o(): async def test_recursive_async_calc(): v = Value(5) - @Calc() + @calc() async def r() -> int: if v() == 0: return 0 v.set(v() - 1) return await r() - @Effect() + @effect() async def o(): await r() @@ -233,7 +233,7 @@ async def test_async_sequential(): exec_order: list[str] = [] async def react_chain(n: int): - @Calc() + @calc() async def r(): nonlocal exec_order exec_order.append(f"r{n}-1") @@ -241,7 +241,7 @@ async def r(): exec_order.append(f"r{n}-2") return x() + 10 - @Effect() + @effect() async def _(): nonlocal exec_order exec_order.append(f"o{n}-1") @@ -279,7 +279,7 @@ async def test_isolate_basic_without_context(): # isolate() works with calc and Value; allows executing without a reactive context. v = Value(1) - @Calc() + @calc() def r(): return v() + 10 @@ -298,14 +298,14 @@ def get_r(): async def test_isolate_prevents_dependency(): v = Value(1) - @Calc() + @calc() def r(): return v() + 10 v_dep = Value(1) # Use this only for invalidating the effect o_val = None - @Effect() + @effect() def o(): nonlocal o_val v_dep() @@ -346,7 +346,7 @@ async def test_isolate_async_basic_without_context(): # context. v = Value(1) - @Calc() + @calc() async def r(): return v() + 10 @@ -362,14 +362,14 @@ async def get_r(): async def test_isolate_async_prevents_dependency(): v = Value(1) - @Calc() + @calc() async def r(): return v() + 10 v_dep = Value(1) # Use this only for invalidating the effect o_val = None - @Effect() + @effect() async def o(): nonlocal o_val v_dep() @@ -400,19 +400,19 @@ async def test_effect_priority(): v = Value(1) results: list[int] = [] - @Effect(priority=1) + @effect(priority=1) def o1(): nonlocal results v() results.append(1) - @Effect(priority=2) + @effect(priority=2) def o2(): nonlocal results v() results.append(2) - @Effect(priority=1) + @effect(priority=1) def o3(): nonlocal results v() @@ -423,7 +423,7 @@ def o3(): # Add another effect with priority 2. Only this one will run (until we # invalidate others by changing v). - @Effect(priority=2) + @effect(priority=2) def o4(): nonlocal results v() @@ -451,19 +451,19 @@ async def test_async_effect_priority(): v = Value(1) results: list[int] = [] - @Effect(priority=1) + @effect(priority=1) async def o1(): nonlocal results v() results.append(1) - @Effect(priority=2) + @effect(priority=2) async def o2(): nonlocal results v() results.append(2) - @Effect(priority=1) + @effect(priority=1) async def o3(): nonlocal results v() @@ -474,7 +474,7 @@ async def o3(): # Add another effect with priority 2. Only this one will run (until we # invalidate others by changing v). - @Effect(priority=2) + @effect(priority=2) async def o4(): nonlocal results v() @@ -504,7 +504,7 @@ async def test_effect_destroy(): v = Value(1) results: list[int] = [] - @Effect() + @effect() def o1(): nonlocal results v() @@ -522,7 +522,7 @@ def o1(): v = Value(1) results: list[int] = [] - @Effect() + @effect() def o2(): nonlocal results v() @@ -540,17 +540,17 @@ def o2(): async def test_error_handling(): vals: List[str] = [] - @Effect() + @effect() def _(): vals.append("o1") - @Effect() + @effect() def _(): vals.append("o2-1") raise Exception("Error here!") vals.append("o2-2") - @Effect() + @effect() def _(): vals.append("o3") @@ -562,18 +562,18 @@ def _(): vals: List[str] = [] - @Calc() + @calc() def r(): vals.append("r") raise Exception("Error here!") - @Effect() + @effect() def _(): vals.append("o1-1") r() vals.append("o1-2") - @Effect() + @effect() def _(): vals.append("o2") @@ -589,19 +589,19 @@ async def test_calc_error_rethrow(): vals: List[str] = [] v = Value(1) - @Calc() + @calc() def r(): vals.append("r") raise Exception("Error here!") - @Effect() + @effect() def _(): v() vals.append("o1-1") r() vals.append("o1-2") - @Effect() + @effect() def _(): v() vals.append("o2-2") @@ -628,7 +628,7 @@ async def test_dependent_invalidation(): v = Value(0) error_occurred = False - @Effect() + @effect() def _(): trigger() @@ -641,11 +641,11 @@ def _(): nonlocal error_occurred error_occurred = True - @Effect() + @effect() def _(): r() - @Calc() + @calc() def r(): return v() @@ -667,7 +667,7 @@ def r(): async def test_req(): n_times = 0 - @Effect() + @effect() def _(): req(False) nonlocal n_times @@ -676,7 +676,7 @@ def _(): await flush() assert n_times == 0 - @Effect() + @effect() def _(): req(True) nonlocal n_times @@ -685,14 +685,14 @@ def _(): await flush() assert n_times == 1 - @Calc() + @calc() def r(): req(False) return 1 val = None - @Effect() + @effect() def _(): nonlocal val val = r() @@ -700,12 +700,12 @@ def _(): await flush() assert val is None - @Calc() + @calc() def r2(): req(True) return 1 - @Effect() + @effect() def _(): nonlocal val val = r2() @@ -719,7 +719,7 @@ async def test_invalidate_later(): mock_time = MockTime() with mock_time(): - @Effect() + @effect() def obs1(): invalidate_later(1) @@ -751,7 +751,7 @@ async def test_invalidate_later_invalidation(): with mock_time(): rv = Value(0) - @Effect() + @effect() def obs1(): if rv() == 0: invalidate_later(1) @@ -803,7 +803,7 @@ async def test_event_decorator(): n_times = 0 # By default, runs every time that event expression is _not_ None (ignore_none=True) - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0)) def _(): nonlocal n_times @@ -813,7 +813,7 @@ def _(): assert n_times == 0 # Unless ignore_none=False - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0), ignore_none=False) def _(): nonlocal n_times @@ -823,7 +823,7 @@ def _(): assert n_times == 1 # Or if one of the args is not None - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0), lambda: True) def _(): nonlocal n_times @@ -835,7 +835,7 @@ def _(): # Is invalidated properly by reactive values v = Value(1) - @Effect() + @effect() @event(v) def _(): nonlocal n_times @@ -855,7 +855,7 @@ def _(): # Doesn't run on init v = Value(1) - @Effect() + @effect() @event(v, ignore_init=True) def _(): nonlocal n_times @@ -872,7 +872,7 @@ def _(): v = Value(1) v2 = Value(1) - @Effect() + @effect() @event(v) def _(): nonlocal n_times @@ -888,12 +888,12 @@ def _(): # works with @calc() v2 = Value(1) - @Calc() + @calc() @event(lambda: v2(), ignore_init=True) def r2b(): return 1 - @Effect() + @effect() def _(): nonlocal n_times n_times += r2b() @@ -914,7 +914,7 @@ async def test_event_async_decorator(): n_times = 0 # By default, runs every time that event expression is _not_ None (ignore_none=True) - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0)) async def _(): nonlocal n_times @@ -924,7 +924,7 @@ async def _(): assert n_times == 0 # Unless ignore_none=False - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0), ignore_none=False) async def _(): nonlocal n_times @@ -934,7 +934,7 @@ async def _(): assert n_times == 1 # Or if one of the args is not None - @Effect() + @effect() @event(lambda: None, lambda: ActionButtonValue(0), lambda: True) async def _(): nonlocal n_times @@ -946,7 +946,7 @@ async def _(): # Is invalidated properly by reactive values v = Value(1) - @Effect() + @effect() @event(v) async def _(): nonlocal n_times @@ -966,7 +966,7 @@ async def _(): # Doesn't run on init v = Value(1) - @Effect() + @effect() @event(v, ignore_init=True) async def _(): nonlocal n_times @@ -983,7 +983,7 @@ async def _(): v = Value(1) v2 = Value(1) - @Effect() + @effect() @event(v) async def _(): nonlocal n_times @@ -999,18 +999,18 @@ async def _(): # works with @calc() v2 = Value(1) - @Calc() + @calc() async def r_a(): await asyncio.sleep(0) # Make sure the async function yields control return 1 - @Calc() + @calc() @event(lambda: v2(), r_a, ignore_init=True) async def r2b(): await asyncio.sleep(0) # Make sure the async function yields control return 1 - @Effect() + @effect() async def _(): nonlocal n_times await asyncio.sleep(0) @@ -1032,7 +1032,7 @@ async def test_event_silent_exception(): n_times = 0 x = Value[bool]() - @Effect() + @effect() @event(x) def _(): nonlocal n_times @@ -1067,7 +1067,7 @@ async def req_fn() -> int: x() return 1234 - @Effect() + @effect() @event(req_fn) async def _(): await asyncio.sleep(0) @@ -1109,14 +1109,14 @@ async def _(): # Should complain that @event() can't take the result of @Effect (which returns # None). @event(lambda: 1) # type: ignore - @Effect() + @effect() async def _(): ... with pytest.raises(TypeError): # Should complain that @event must be applied before @Calc. @event(lambda: 1) - @Calc() + @calc() async def _(): ... @@ -1141,12 +1141,12 @@ async def _(): async def _(): ... - @Effect() + @effect() @event(lambda: 1) async def _(): ... - @Calc() + @calc() @event(lambda: 1) async def _(): ... @@ -1191,14 +1191,14 @@ def _(): with pytest.raises(TypeError): # Should complain about @Calc @output # type: ignore - @Calc + @calc def _(): ... with pytest.raises(TypeError): # Should complain about @Effet @output # type: ignore - @Effect + @effect def _(): ... @@ -1221,11 +1221,11 @@ def _(): async def test_effect_pausing(): a = Value(float(1)) - @Calc() + @calc() def funcA(): return a() - @Effect() + @effect() def obsB(): funcA() @@ -1303,11 +1303,11 @@ def _(): async def test_effect_async_pausing(): a = Value(float(1)) - @Calc() + @calc() async def funcA(): return a() - @Effect() + @effect() async def obsB(): await funcA() @@ -1382,7 +1382,7 @@ def _(): async def test_observer_async_suspended_resumed_observers_run_at_most_once(): a = Value(1) - @Effect() + @effect() async def obs(): print(a()) diff --git a/tests/pytest/test_shinysession.py b/tests/pytest/test_shinysession.py index 8ef2149a9..18017ba44 100644 --- a/tests/pytest/test_shinysession.py +++ b/tests/pytest/test_shinysession.py @@ -3,7 +3,7 @@ import pytest from shiny import ui -from shiny.reactive import Effect, flush, isolate +from shiny.reactive import effect, flush, isolate from shiny.session import Inputs from shiny.types import SilentException @@ -55,7 +55,7 @@ async def test_input_nonexistent_deps(): input = Inputs({}) result = None - @Effect() + @effect() def o1(): nonlocal result result = "x" in input