diff --git a/examples/cpuinfo/app.py b/examples/cpuinfo/app.py index c06ca9ab9..75607657b 100644 --- a/examples/cpuinfo/app.py +++ b/examples/cpuinfo/app.py @@ -140,7 +140,7 @@ def collect_cpu_samples(): combined_data = combined_data[:, -MAX_SAMPLES:] cpu_history.set(combined_data) - @reactive.Effect(priority=100) + @reactive.effect(priority=100) @reactive.event(input.reset) def reset_history(): cpu_history.set(None) diff --git a/shiny/reactive/_reactives.py b/shiny/reactive/_reactives.py index 54c855579..59b9c7e51 100644 --- a/shiny/reactive/_reactives.py +++ b/shiny/reactive/_reactives.py @@ -480,6 +480,14 @@ def __init__( self.__name__ = fn.__name__ self.__doc__ = fn.__doc__ + from ..render.renderer import RendererBase + + if isinstance(fn, RendererBase): + raise TypeError( + "`@reactive.effect` can not be combined with `@render.xx`.\n" + + "Please remove your call of `@reactive.effect`." + ) + # The EffectAsync subclass will pass in an async function, but it tells the # static type checker that it's synchronous. wrap_async() is smart -- if is # passed an async function, it will not change it. diff --git a/tests/pytest/test_renderer.py b/tests/pytest/test_renderer.py index 18af6a093..15a5417a5 100644 --- a/tests/pytest/test_renderer.py +++ b/tests/pytest/test_renderer.py @@ -4,6 +4,7 @@ import pytest +from shiny import reactive, render from shiny.render.renderer import Renderer, ValueFn @@ -53,3 +54,12 @@ def txt4() -> str: assert val == "42 42" val = await txt4.render() assert val == "42 42 42 42" + + +def test_effect(): + with pytest.raises(TypeError): + + @reactive.effect # pyright: ignore[reportGeneralTypeIssues] + @render.text + def my_output(): + return "42"