diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c3c330a..cab7c544a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features * `shiny run` now takes `reload-includes` and `reload-excludes` to allow you to define which files trigger a reload (#780). * `shiny.run` now passes keyword arguments to `uvicorn.run` (#780). -* The `@output` decorator is no longer required for rendering functions; `@render.xxx` decorators now register themselves automatically. You can still use `@output` explicitly if you need to set specific output options (#747). +* The `@output` decorator is no longer required for rendering functions; `@render.xxx` decorators now register themselves automatically. You can still use `@output` explicitly if you need to set specific output options (#747, #790). * Added support for integration with Quarto (#746). * Added `shiny.render.renderer_components` decorator to help create new output renderers (#621). * Added `shiny.experimental.ui.popover()`, `update_popover()`, and `toggle_popover()` for easy creation (and server-side updating) of [Bootstrap popovers](https://getbootstrap.com/docs/5.3/components/popovers/). Popovers are similar to tooltips, but are more persistent, and should primarily be used with button-like UI elements (e.g. `input_action_button()` or icons) (#680). diff --git a/examples/airmass/app.py b/examples/airmass/app.py index f353d835f..005b19a73 100644 --- a/examples/airmass/app.py +++ b/examples/airmass/app.py @@ -119,7 +119,6 @@ def df() -> Dict[str, pd.DataFrame]: for (altaz, obj) in zip(altaz_list, obj_names()) } - @output @render.plot def plot(): fig, [ax1, ax2] = plt.subplots(nrows=2) @@ -160,12 +159,10 @@ def add_boundary(ax, xval): return fig - @output @render.table def table() -> pd.DataFrame: return pd.concat(df()) - @output @render.ui def timeinfo(): start_utc, end_utc = times_utc() diff --git a/examples/annotation-export/app.py b/examples/annotation-export/app.py index 6f06f4d89..601b4116c 100644 --- a/examples/annotation-export/app.py +++ b/examples/annotation-export/app.py @@ -62,7 +62,6 @@ def _(): df = df.loc[:, ["date", "temp_c", "annotation"]] annotated_data.set(df) - @output @render.plot def time_series(): fig, ax = plt.subplots() @@ -76,7 +75,6 @@ def time_series(): out.tick_params(axis="x", rotation=30) return out.get_figure() - @output @render.ui def annotator(): if input.time_series_brush() is not None: @@ -104,7 +102,6 @@ def annotator(): ) return out - @output @render.data_frame def annotations(): df = annotated_data().copy() diff --git a/examples/brownian/app.py b/examples/brownian/app.py index 9d4827081..47859ea79 100644 --- a/examples/brownian/app.py +++ b/examples/brownian/app.py @@ -85,22 +85,18 @@ def update_plotly_camera(): # DEBUGGING ==== - @output @render.text def x_debug(): return camera_eye()["x"] - @output @render.text def y_debug(): return camera_eye()["y"] - @output @render.text def z_debug(): return camera_eye()["z"] - @output @render.text def mag_debug(): eye = camera_eye() diff --git a/examples/cpuinfo/app.py b/examples/cpuinfo/app.py index 7fd9329d8..c06ca9ab9 100644 --- a/examples/cpuinfo/app.py +++ b/examples/cpuinfo/app.py @@ -145,7 +145,6 @@ def collect_cpu_samples(): def reset_history(): cpu_history.set(None) - @output @render.plot def plot(): history = cpu_history_with_hold() @@ -205,7 +204,6 @@ def plot(): return fig - @output @render.table def table(): history = cpu_history_with_hold() diff --git a/examples/dataframe/app.py b/examples/dataframe/app.py index 5581c6e8c..b53be4f78 100644 --- a/examples/dataframe/app.py +++ b/examples/dataframe/app.py @@ -61,7 +61,6 @@ def server(input: Inputs, output: Outputs, session: Session): def update_df(): return df.set(sns.load_dataset(req(input.dataset()))) - @output @render.data_frame def grid(): height = 350 @@ -91,7 +90,6 @@ def handle_edit(): df_copy.iat[edit["row"], edit["col"]] = edit["new_value"] df.set(df_copy) - @output @render.text def detail(): if ( diff --git a/examples/duckdb/query.py b/examples/duckdb/query.py index 3395b7499..95a5b768d 100644 --- a/examples/duckdb/query.py +++ b/examples/duckdb/query.py @@ -44,7 +44,6 @@ def query_output_ui(remove_id, qry="SELECT * from weather LIMIT 10"): def query_output_server( input, output, session, con: duckdb.DuckDBPyConnection, remove_id ): - @output @render.data_frame def results(): # In order to avoid the query re-running with each keystroke we diff --git a/examples/event/app.py b/examples/event/app.py index 31ee8eb26..ade6105a3 100644 --- a/examples/event/app.py +++ b/examples/event/app.py @@ -66,7 +66,6 @@ async def _(): val = await btn_async_r() print("async @calc() event: ", str(val)) - @output @render.ui @reactive.event(btn_async_r) async def btn_async_value(): diff --git a/examples/global_pyplot/app.py b/examples/global_pyplot/app.py index e9de2f7f0..a43d503cf 100644 --- a/examples/global_pyplot/app.py +++ b/examples/global_pyplot/app.py @@ -19,13 +19,11 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot def mpl(): if input.render(): plt.hist([1, 1, 2, 3, 5]) - @output @render.plot async def mpl_bad(): if input.render(): diff --git a/examples/headers/app.py b/examples/headers/app.py index 02eabd776..32d8d7ed9 100644 --- a/examples/headers/app.py +++ b/examples/headers/app.py @@ -9,7 +9,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def headers(): s = "" @@ -18,7 +17,6 @@ def headers(): return s - @output @render.text def user_groups(): return f"session.user: {session.user}\nsession.groups: {session.groups}" diff --git a/examples/load_balance/app.py b/examples/load_balance/app.py index 09b7c5d93..cd9ac1427 100644 --- a/examples/load_balance/app.py +++ b/examples/load_balance/app.py @@ -30,7 +30,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def out(): # Register a dynamic route for the client to try to connect to. diff --git a/examples/model-score/app.py b/examples/model-score/app.py index 5f140fc34..99a77c80c 100644 --- a/examples/model-score/app.py +++ b/examples/model-score/app.py @@ -178,7 +178,6 @@ def filtered_df(): def filtered_model_names(): return filtered_df()["model"].unique() - @output @render.ui def value_boxes(): data = filtered_df() @@ -208,7 +207,6 @@ def value_boxes(): fixed_width=True, ) - @output @render_plotly_streaming(recreate_key=filtered_model_names, update="data") def plot_timeseries(): """ @@ -249,7 +247,6 @@ def plot_timeseries(): return fig - @output @render_plotly_streaming(recreate_key=filtered_model_names, update="data") def plot_dist(): fig = px.histogram( diff --git a/examples/moduleapp/app.py b/examples/moduleapp/app.py index 666c6584a..3cb185638 100644 --- a/examples/moduleapp/app.py +++ b/examples/moduleapp/app.py @@ -25,7 +25,6 @@ def counter_server( def _(): count.set(count() + 1) - @output @render.text def out() -> str: return f"Click count is {count()}" @@ -44,7 +43,6 @@ def counter_wrapper_ui() -> ui.TagChild: def counter_wrapper_server( input: Inputs, output: Outputs, session: Session, label: str = "Increment counter" ): - @output() @render.ui() def dynamic_counter(): return counter_ui("counter", label) @@ -66,7 +64,6 @@ def server(input: Inputs, output: Outputs, session: Session): counter_server("counter1") counter_wrapper_server("counter2_wrapper", "Counter 2") - @output() @render.ui() def counter3_ui(): counter_server("counter3") diff --git a/examples/penguins/app.py b/examples/penguins/app.py index d10b7e7aa..9ab78d6b5 100644 --- a/examples/penguins/app.py +++ b/examples/penguins/app.py @@ -63,7 +63,6 @@ def filtered_df() -> pd.DataFrame: # Filter the rows so we only include the desired species return df[df["Species"].isin(input.species())] - @output @render.plot def scatter(): """Generates a plot for Shiny to display to the user""" @@ -81,7 +80,6 @@ def scatter(): legend=False, ) - @output @render.ui def value_boxes(): df = filtered_df() @@ -124,7 +122,7 @@ def penguin_value_box(title: str, count: int, bgcol: str, showcase_img: str): if name in input.species() ] - return ui.layout_column_wrap(1 / len(value_boxes), *value_boxes) + return ui.layout_column_wrap(*value_boxes, width=1 / len(value_boxes)) app = App( diff --git a/examples/req/app.py b/examples/req/app.py index 1cdf47659..80d70ebb0 100644 --- a/examples/req/app.py +++ b/examples/req/app.py @@ -21,12 +21,10 @@ def safe_click(): req(input.safe()) return input.safe() - @output @render.ui def safe(): raise SafeException(f"You've clicked {str(safe_click())} times") - @output @render.ui def unsafe(): req(input.unsafe()) @@ -38,7 +36,6 @@ def _(): print("unsafe clicks:", input.unsafe()) # raise Exception("Observer exception: this should cause a crash") - @output @render.ui def txt_out(): req(input.txt(), cancel_output=True) diff --git a/examples/static_plots/app.py b/examples/static_plots/app.py index cc99f20c0..52736f56e 100644 --- a/examples/static_plots/app.py +++ b/examples/static_plots/app.py @@ -62,7 +62,6 @@ def fake_data(): cov = [(input.var(), input.cov()), (input.cov(), 1 / input.var())] return rng.multivariate_normal(mean, cov, n).T - @output @render.plot def seaborn(): x, y = fake_data() @@ -72,7 +71,6 @@ def seaborn(): sns.kdeplot(x=x, y=y, levels=5, color="w", linewidths=1) return f - @output @render.plot def plotnine(): from plotnine import ( @@ -97,7 +95,6 @@ def plotnine(): + theme(legend_position="top") ) - @output @render.plot def pandas(): ts = pd.Series( @@ -106,7 +103,6 @@ def pandas(): ts = ts.cumsum() return ts.plot() - @output @render.plot def holoviews(): import holoviews as hv @@ -115,7 +111,6 @@ def holoviews(): links = pd.DataFrame(les_mis["links"]) return hv.render(hv.Chord(links), backend="matplotlib") - @output @render.plot def xarray(): import xarray as xr @@ -126,7 +121,6 @@ def xarray(): air.attrs["units"] = "deg C" return air.isel(lon=10, lat=[19, 21, 22]).plot.line(x="time") - @output @render.plot def geopandas(): import geodatasets @@ -138,7 +132,6 @@ def geopandas(): boros.sort_index(inplace=True) return boros.plot() - @output @render.plot def missingno(): import matplotlib.pyplot as plt diff --git a/examples/typed_inputs/app.py b/examples/typed_inputs/app.py index 8f583140e..1356fef51 100644 --- a/examples/typed_inputs/app.py +++ b/examples/typed_inputs/app.py @@ -42,21 +42,18 @@ def r(): # thinks the return type of input.n() is Any, so we don't get type checking here. # The function is returning the wrong value here: it returns an int instead of a # string, but this error is not flagged. - @output @render.text async def txt(): return input.n() * 2 # In contrast, input.n2() is declared to return an int, so the type check does flag # this error -- the `render.text()` is underlined in red. - @output @render.text async def txt2(): return input.n2() * 2 # This is a corrected version of the function above. It returns a string, and is not # marked in red. - @output @render.text async def txt3(): return str(input.n2() * 2) diff --git a/examples/ui-func/app.py b/examples/ui-func/app.py index 6cc149b84..08357fbde 100644 --- a/examples/ui-func/app.py +++ b/examples/ui-func/app.py @@ -13,7 +13,6 @@ def app_ui(request: Request): def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def now(): reactive.invalidate_later(0.1) diff --git a/shiny/api-examples/Calc/app.py b/shiny/api-examples/Calc/app.py index fcaae7002..603313593 100644 --- a/shiny/api-examples/Calc/app.py +++ b/shiny/api-examples/Calc/app.py @@ -28,7 +28,6 @@ def second(): input.second() return random.randint(1, 1000) - @output @render.ui def result(): return first() + second() diff --git a/shiny/api-examples/Module/app.py b/shiny/api-examples/Module/app.py index 806f02a25..12f27078f 100644 --- a/shiny/api-examples/Module/app.py +++ b/shiny/api-examples/Module/app.py @@ -25,7 +25,6 @@ def counter_server( def _(): count.set(count() + 1) - @output @render.text def out() -> str: return f"Click count is {count()}" diff --git a/shiny/api-examples/Progress/app.py b/shiny/api-examples/Progress/app.py index 1b2d48cb8..5cb790251 100644 --- a/shiny/api-examples/Progress/app.py +++ b/shiny/api-examples/Progress/app.py @@ -9,7 +9,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text @reactive.event(input.button) async def compute(): diff --git a/shiny/api-examples/SafeException/app.py b/shiny/api-examples/SafeException/app.py index 600c9627d..00c261b0d 100644 --- a/shiny/api-examples/SafeException/app.py +++ b/shiny/api-examples/SafeException/app.py @@ -5,12 +5,10 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def safe(): raise SafeException("This is a safe exception") - @output @render.ui def unsafe(): raise Exception("This is an unsafe exception") diff --git a/shiny/api-examples/SilentCancelOutputException/app.py b/shiny/api-examples/SilentCancelOutputException/app.py index a8c9a2ef7..4008f583c 100644 --- a/shiny/api-examples/SilentCancelOutputException/app.py +++ b/shiny/api-examples/SilentCancelOutputException/app.py @@ -13,7 +13,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def txt_out(): if not input.txt(): diff --git a/shiny/api-examples/SilentException/app.py b/shiny/api-examples/SilentException/app.py index e36739731..0fb3d7060 100644 --- a/shiny/api-examples/SilentException/app.py +++ b/shiny/api-examples/SilentException/app.py @@ -12,7 +12,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def txt_out(): if not input.txt(): diff --git a/shiny/api-examples/Value/app.py b/shiny/api-examples/Value/app.py index 2cec75a56..fe0112954 100644 --- a/shiny/api-examples/Value/app.py +++ b/shiny/api-examples/Value/app.py @@ -24,7 +24,6 @@ def _(): newVal = val.get() + 1 val.set(newVal) - @output @render.text def value(): return str(val.get()) diff --git a/shiny/api-examples/accordion/app.py b/shiny/api-examples/accordion/app.py index 1cbb062b3..5f278f6d6 100644 --- a/shiny/api-examples/accordion/app.py +++ b/shiny/api-examples/accordion/app.py @@ -38,7 +38,6 @@ def server(input: Inputs, output: Outputs, session: Session): def _(): print(input.acc()) - @output @render.text def acc_multiple_val(): return "input.acc_multiple(): " + str(input.acc_multiple()) diff --git a/shiny/api-examples/accordion_panel/app.py b/shiny/api-examples/accordion_panel/app.py index fde86ef3e..b4d7d2bf8 100644 --- a/shiny/api-examples/accordion_panel/app.py +++ b/shiny/api-examples/accordion_panel/app.py @@ -18,7 +18,6 @@ def server(input: Inputs, output: Outputs, session: Session): def _(): print(input.acc()) - @output @render.text def acc_val(): return "input.acc(): " + str(input.acc()) diff --git a/shiny/api-examples/data_frame/app.py b/shiny/api-examples/data_frame/app.py index 504343acd..6cb007801 100644 --- a/shiny/api-examples/data_frame/app.py +++ b/shiny/api-examples/data_frame/app.py @@ -49,7 +49,6 @@ def server(input, output, session): - @output @render.data_frame def summary_data(): return render.DataGrid( @@ -68,7 +67,6 @@ def filtered_df(): # Filter data for selected countries return df[df["country"].isin(countries)] - @output @render_widget def country_detail_pop(): # Create the plot @@ -88,7 +86,6 @@ def on_size_changed(width, height): return widget - @output @render_widget def country_detail_percap(): # Create the plot diff --git a/shiny/api-examples/event/app.py b/shiny/api-examples/event/app.py index 062c6f4cf..d49010fc0 100644 --- a/shiny/api-examples/event/app.py +++ b/shiny/api-examples/event/app.py @@ -44,7 +44,6 @@ def _(): val.set(random.randint(0, 1000)) # Always update this output when the number is updated - @output @render.ui def number(): return val.get() @@ -52,7 +51,6 @@ def number(): # Since ignore_none=False, the function executes before clicking the button. # (input.btn_out() is 0 on page load, but @@reactive.event() treats 0 as None for # action buttons.) - @output @render.text @reactive.event(input.btn_out, ignore_none=False) def out_out(): @@ -63,7 +61,6 @@ def out_out(): def calc(): return 1 / val.get() - @output @render.text def out_calc(): return str(calc()) diff --git a/shiny/api-examples/file_reader/app.py b/shiny/api-examples/file_reader/app.py index 6d518b1ac..7df04d271 100644 --- a/shiny/api-examples/file_reader/app.py +++ b/shiny/api-examples/file_reader/app.py @@ -15,7 +15,6 @@ def read_file(): def server(input: Inputs, output: Outputs, session: Session): - @output @render.table def result(): return read_file() diff --git a/shiny/api-examples/input_action_button/app.py b/shiny/api-examples/input_action_button/app.py index 1f0b7d819..66f6abe13 100644 --- a/shiny/api-examples/input_action_button/app.py +++ b/shiny/api-examples/input_action_button/app.py @@ -11,7 +11,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") # Use reactive.event() to invalidate the plot only when the button is pressed # (not when the slider is changed) diff --git a/shiny/api-examples/input_action_link/app.py b/shiny/api-examples/input_action_link/app.py index a27a52ea6..054ebacaa 100644 --- a/shiny/api-examples/input_action_link/app.py +++ b/shiny/api-examples/input_action_link/app.py @@ -11,7 +11,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") # reactive.event() to invalidate the plot when the button is pressed but not when # the slider is changed diff --git a/shiny/api-examples/input_checkbox/app.py b/shiny/api-examples/input_checkbox/app.py index 6ee8c6030..b84f92de3 100644 --- a/shiny/api-examples/input_checkbox/app.py +++ b/shiny/api-examples/input_checkbox/app.py @@ -7,7 +7,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def value(): return input.somevalue() diff --git a/shiny/api-examples/input_checkbox_group/app.py b/shiny/api-examples/input_checkbox_group/app.py index 2abc269b3..638eee52b 100644 --- a/shiny/api-examples/input_checkbox_group/app.py +++ b/shiny/api-examples/input_checkbox_group/app.py @@ -15,7 +15,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def val(): req(input.colors()) diff --git a/shiny/api-examples/input_file/app.py b/shiny/api-examples/input_file/app.py index 0b754bafa..fa34f804c 100644 --- a/shiny/api-examples/input_file/app.py +++ b/shiny/api-examples/input_file/app.py @@ -25,7 +25,6 @@ def parsed_file(): file[0]["datapath"] ) - @output @render.table def summary(): df = parsed_file() diff --git a/shiny/api-examples/input_numeric/app.py b/shiny/api-examples/input_numeric/app.py index 513fa207a..3c6f2b0dc 100644 --- a/shiny/api-examples/input_numeric/app.py +++ b/shiny/api-examples/input_numeric/app.py @@ -7,7 +7,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def value(): return input.obs() diff --git a/shiny/api-examples/input_password/app.py b/shiny/api-examples/input_password/app.py index 36c421c8b..4d56c7024 100644 --- a/shiny/api-examples/input_password/app.py +++ b/shiny/api-examples/input_password/app.py @@ -8,7 +8,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text @reactive.event(input.go) def value(): diff --git a/shiny/api-examples/input_radio_buttons/app.py b/shiny/api-examples/input_radio_buttons/app.py index 5d8444ea6..5b6129fa4 100644 --- a/shiny/api-examples/input_radio_buttons/app.py +++ b/shiny/api-examples/input_radio_buttons/app.py @@ -14,7 +14,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def val(): return "You chose " + input.rb() diff --git a/shiny/api-examples/input_select/app.py b/shiny/api-examples/input_select/app.py index 4df16e8c9..d7a668430 100644 --- a/shiny/api-examples/input_select/app.py +++ b/shiny/api-examples/input_select/app.py @@ -15,7 +15,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def value(): return "You choose: " + str(input.state()) diff --git a/shiny/api-examples/input_selectize/app.py b/shiny/api-examples/input_selectize/app.py index 8180bc772..3d43570fd 100644 --- a/shiny/api-examples/input_selectize/app.py +++ b/shiny/api-examples/input_selectize/app.py @@ -16,7 +16,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def value(): return "You choose: " + str(input.state()) diff --git a/shiny/api-examples/input_slider/app.py b/shiny/api-examples/input_slider/app.py index b3a11ddd1..c8a238506 100644 --- a/shiny/api-examples/input_slider/app.py +++ b/shiny/api-examples/input_slider/app.py @@ -10,7 +10,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot def distPlot(): np.random.seed(19680801) diff --git a/shiny/api-examples/input_switch/app.py b/shiny/api-examples/input_switch/app.py index 09c119ac0..a438d8195 100644 --- a/shiny/api-examples/input_switch/app.py +++ b/shiny/api-examples/input_switch/app.py @@ -7,7 +7,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui def value(): return input.somevalue() diff --git a/shiny/api-examples/input_text/app.py b/shiny/api-examples/input_text/app.py index 531c766a9..bf6ddd859 100644 --- a/shiny/api-examples/input_text/app.py +++ b/shiny/api-examples/input_text/app.py @@ -7,7 +7,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def value(): return input.caption() diff --git a/shiny/api-examples/input_text_area/app.py b/shiny/api-examples/input_text_area/app.py index cd59703c1..3c708bc4e 100644 --- a/shiny/api-examples/input_text_area/app.py +++ b/shiny/api-examples/input_text_area/app.py @@ -18,12 +18,10 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def value_regular(): return input.caption_regular() - @output @render.text def value_autoresize(): return input.caption_autoresize() diff --git a/shiny/api-examples/invalidate_later/app.py b/shiny/api-examples/invalidate_later/app.py index 83c76befe..7966d01f0 100644 --- a/shiny/api-examples/invalidate_later/app.py +++ b/shiny/api-examples/invalidate_later/app.py @@ -11,7 +11,6 @@ def _(): reactive.invalidate_later(0.5) print("Random int: ", random.randint(0, 10000)) - @output @render.ui def value(): reactive.invalidate_later(0.5) diff --git a/shiny/api-examples/isolate/app.py b/shiny/api-examples/isolate/app.py index 22bfac217..f6a12559d 100644 --- a/shiny/api-examples/isolate/app.py +++ b/shiny/api-examples/isolate/app.py @@ -11,7 +11,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot(): # Take a reactive dependency on the action button... diff --git a/shiny/api-examples/layout_sidebar/app.py b/shiny/api-examples/layout_sidebar/app.py index 081343c7c..78e36e744 100644 --- a/shiny/api-examples/layout_sidebar/app.py +++ b/shiny/api-examples/layout_sidebar/app.py @@ -14,7 +14,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/on_flush/app.py b/shiny/api-examples/on_flush/app.py index 7b949d7eb..5d8db3c8a 100644 --- a/shiny/api-examples/on_flush/app.py +++ b/shiny/api-examples/on_flush/app.py @@ -20,7 +20,6 @@ def log(): session.on_flush(log, once=False) - @output @render.ui def n_clicks(): return "Number of clicks: " + str(input.flush()) diff --git a/shiny/api-examples/on_flushed/app.py b/shiny/api-examples/on_flushed/app.py index fbfcf494e..d03e269ac 100644 --- a/shiny/api-examples/on_flushed/app.py +++ b/shiny/api-examples/on_flushed/app.py @@ -20,7 +20,6 @@ def log(): session.on_flushed(log, once=False) - @output @render.ui def n_clicks(): return "Number of clicks: " + str(input.flush()) diff --git a/shiny/api-examples/output_image/app.py b/shiny/api-examples/output_image/app.py index bfef5bc28..09452566a 100644 --- a/shiny/api-examples/output_image/app.py +++ b/shiny/api-examples/output_image/app.py @@ -5,7 +5,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.image def image(): from pathlib import Path diff --git a/shiny/api-examples/output_plot/app.py b/shiny/api-examples/output_plot/app.py index fee73dbd5..339a1a6b7 100644 --- a/shiny/api-examples/output_plot/app.py +++ b/shiny/api-examples/output_plot/app.py @@ -12,7 +12,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot def p(): np.random.seed(19680801) diff --git a/shiny/api-examples/output_table/app.py b/shiny/api-examples/output_table/app.py index db9a8ac9b..1dac0bab9 100644 --- a/shiny/api-examples/output_table/app.py +++ b/shiny/api-examples/output_table/app.py @@ -26,7 +26,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.table def result(): if not input.highlight(): diff --git a/shiny/api-examples/output_text/app.py b/shiny/api-examples/output_text/app.py index f41594267..cf0d8e3d9 100644 --- a/shiny/api-examples/output_text/app.py +++ b/shiny/api-examples/output_text/app.py @@ -22,17 +22,14 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def text(): return input.txt() - @output @render.text def verb(): return input.txt() - @output @render.text def verb_no_placeholder(): return input.txt() diff --git a/shiny/api-examples/output_transformer/app.py b/shiny/api-examples/output_transformer/app.py index 09bc1b51e..568b102b2 100644 --- a/shiny/api-examples/output_transformer/app.py +++ b/shiny/api-examples/output_transformer/app.py @@ -53,7 +53,6 @@ async def CapitalizeTransformer( # First, create an overload where users can supply the extra parameters. # Example of usage: # ``` -# @output # @render_capitalize(to="upper") # def value(): # return input.caption() @@ -71,7 +70,6 @@ def render_capitalize( # While it doesn't look necessary, it is needed for the type checker. # Example of usage: # ``` -# @output # @render_capitalize # def value(): # return input.caption() @@ -118,19 +116,16 @@ def render_capitalize( def server(input: Inputs, output: Outputs, session: Session): - @output - # Called without parentheses + # Without parentheses @render_capitalize def no_parens(): return input.caption() - @output - # Called with parentheses. Equivalent to `@render_capitalize()` + # With parentheses. Equivalent to `@render_capitalize()` @render_capitalize(to="upper") def to_upper(): return input.caption() - @output @render_capitalize(to="lower") # Works with async output value functions async def to_lower(): diff --git a/shiny/api-examples/output_ui/app.py b/shiny/api-examples/output_ui/app.py index fa358746f..cc074e377 100644 --- a/shiny/api-examples/output_ui/app.py +++ b/shiny/api-examples/output_ui/app.py @@ -7,7 +7,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.ui @reactive.event(input.add) def moreControls(): diff --git a/shiny/api-examples/page_fixed/app.py b/shiny/api-examples/page_fixed/app.py index 3677fb490..80521e92c 100644 --- a/shiny/api-examples/page_fixed/app.py +++ b/shiny/api-examples/page_fixed/app.py @@ -16,7 +16,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/page_fluid/app.py b/shiny/api-examples/page_fluid/app.py index 30a724a50..99a7ef9f8 100644 --- a/shiny/api-examples/page_fluid/app.py +++ b/shiny/api-examples/page_fluid/app.py @@ -16,7 +16,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/poll/app.py b/shiny/api-examples/poll/app.py index dbd0e7720..c9411d28a 100644 --- a/shiny/api-examples/poll/app.py +++ b/shiny/api-examples/poll/app.py @@ -115,7 +115,6 @@ def filtered_quotes(): df = df[df["symbol"].isin(input.symbols())] return df - @output @render.ui def table(): return ui.HTML( diff --git a/shiny/api-examples/popover/app.py b/shiny/api-examples/popover/app.py index 69292e778..474821028 100644 --- a/shiny/api-examples/popover/app.py +++ b/shiny/api-examples/popover/app.py @@ -31,7 +31,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def plot_txt(): return f"" diff --git a/shiny/api-examples/render_image/app.py b/shiny/api-examples/render_image/app.py index bdea200dd..60d35d90a 100644 --- a/shiny/api-examples/render_image/app.py +++ b/shiny/api-examples/render_image/app.py @@ -5,7 +5,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.image def image(): from pathlib import Path diff --git a/shiny/api-examples/req/app.py b/shiny/api-examples/req/app.py index b01fca7b5..adf764625 100644 --- a/shiny/api-examples/req/app.py +++ b/shiny/api-examples/req/app.py @@ -20,12 +20,10 @@ def safe_click(): req(input.safe()) return input.safe() - @output @render.ui def safe(): raise SafeException(f"You've clicked {str(safe_click())} times") - @output @render.ui def unsafe(): req(input.unsafe()) @@ -37,7 +35,6 @@ def _(): print("unsafe clicks:", input.unsafe()) # raise Exception("Observer exception: this should cause a crash") - @output @render.ui def txt_out(): req(input.txt(), cancel_output=True) diff --git a/shiny/api-examples/row/app.py b/shiny/api-examples/row/app.py index 8e3c482fc..be69085a7 100644 --- a/shiny/api-examples/row/app.py +++ b/shiny/api-examples/row/app.py @@ -12,7 +12,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/sidebar/app.py b/shiny/api-examples/sidebar/app.py index 29d000d7b..48e4c53d7 100644 --- a/shiny/api-examples/sidebar/app.py +++ b/shiny/api-examples/sidebar/app.py @@ -29,22 +29,18 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def state_left(): return f"input.sidebar_left(): {input.sidebar_left()}" - @output @render.text def state_right(): return f"input.sidebar_right(): {input.sidebar_right()}" - @output @render.text def state_closed(): return f"input.sidebar_closed(): {input.sidebar_closed()}" - @output @render.text def state_always(): return f"input.sidebar_always(): {input.sidebar_always()}" diff --git a/shiny/api-examples/todo_list/app.py b/shiny/api-examples/todo_list/app.py index 4640a06fa..9ee61fd39 100644 --- a/shiny/api-examples/todo_list/app.py +++ b/shiny/api-examples/todo_list/app.py @@ -24,7 +24,6 @@ def server(input, output, session): finished_tasks = reactive.Value(0) task_counter = reactive.Value(0) - @output @render.text def cleared_tasks(): return f"Finished tasks: {finished_tasks()}" @@ -68,7 +67,6 @@ def task_ui(): def task_server(input, output, session, text): finished = reactive.Value(False) - @output @render.ui def button_row(): button = None diff --git a/shiny/api-examples/update_sidebar/app.py b/shiny/api-examples/update_sidebar/app.py index b0849155a..4c27ae57a 100644 --- a/shiny/api-examples/update_sidebar/app.py +++ b/shiny/api-examples/update_sidebar/app.py @@ -22,7 +22,6 @@ def _(): def _(): ui.update_sidebar("sidebar", show=False) - @output @render.text def state(): return f"input.sidebar(): {input.sidebar()}" diff --git a/shiny/reactive/_reactives.py b/shiny/reactive/_reactives.py index 249654b4b..c2da6c232 100644 --- a/shiny/reactive/_reactives.py +++ b/shiny/reactive/_reactives.py @@ -799,7 +799,7 @@ def decorator(user_fn: Callable[[], T]) -> Callable[[], T]: if not callable(user_fn): raise TypeError( "`@reactive.event()` must be applied to a function or Callable object.\n" - + "It should usually be applied before `@Calc`,` @Effect`, `@output`, or `@render.xx` function.\n" + + "It should usually be applied before `@Calc`,` @Effect`, or `@render.xx` function.\n" + "In other words, `@reactive.event()` goes below the other decorators." ) diff --git a/shiny/render/_dataframe.py b/shiny/render/_dataframe.py index 2e3af17ce..dc351588e 100644 --- a/shiny/render/_dataframe.py +++ b/shiny/render/_dataframe.py @@ -288,10 +288,10 @@ def data_frame( Tip ---- - This decorator should be applied **before** the ``@output`` decorator. Also, the - name of the decorated function (or ``@output(id=...)``) should match the ``id`` of a - :func:`~shiny.ui.output_table` container (see :func:`~shiny.ui.output_table` for - example usage). + This decorator should be applied **before** the ``@output`` decorator (if that + decorator is used). Also, the name of the decorated function (or + ``@output(id=...)``) should match the ``id`` of a :func:`~shiny.ui.output_table` + container (see :func:`~shiny.ui.output_table` for example usage). See Also -------- diff --git a/tests/playwright/deploys/apps/plotly_app/app.py b/tests/playwright/deploys/apps/plotly_app/app.py index ea775b1cf..434036711 100644 --- a/tests/playwright/deploys/apps/plotly_app/app.py +++ b/tests/playwright/deploys/apps/plotly_app/app.py @@ -51,7 +51,6 @@ def server(input, output, session): - @output @render.data_frame def summary_data(): return render.DataGrid( @@ -68,7 +67,6 @@ def filtered_df(): # Filter data for selected countries return df[df["country"].isin(countries)] - @output @render_widget def country_detail_pop(): # Create the plot @@ -88,7 +86,6 @@ def on_size_changed(width, height): return widget - @output @render_widget def country_detail_percap(): # Create the plot diff --git a/tests/playwright/shiny/TODO/sidebar/app.py b/tests/playwright/shiny/TODO/sidebar/app.py index 387b1ca80..21b18ce62 100644 --- a/tests/playwright/shiny/TODO/sidebar/app.py +++ b/tests/playwright/shiny/TODO/sidebar/app.py @@ -57,7 +57,6 @@ def server(input: Inputs, output: Outputs, session: Session) -> None: - @output @render.ui def ui_content(): return f"Hello, {input.adjective()} {input.animal()}!" diff --git a/tests/playwright/shiny/async/app.py b/tests/playwright/shiny/async/app.py index 0f1dcf63f..c129abc86 100644 --- a/tests/playwright/shiny/async/app.py +++ b/tests/playwright/shiny/async/app.py @@ -24,7 +24,6 @@ def calc(value: str) -> str: def server(input: s.Inputs, output: s.Outputs, session: s.Session): - @output() @s.render.text() @reactive.event(input.go) async def hash_output(): diff --git a/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/app.py b/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/app.py index ec5343723..b9025647a 100644 --- a/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/app.py +++ b/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/app.py @@ -43,7 +43,6 @@ def slider_with_reset_server( max: Optional[datetime.datetime] = None, value: Any = None, ): - @output @render.text def txt(): if isinstance(input.times(), (tuple, list)): diff --git a/tests/playwright/shiny/bugs/0676-row-selection/app.py b/tests/playwright/shiny/bugs/0676-row-selection/app.py index 48771e63a..ab6c11255 100644 --- a/tests/playwright/shiny/bugs/0676-row-selection/app.py +++ b/tests/playwright/shiny/bugs/0676-row-selection/app.py @@ -23,7 +23,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.data_frame def grid(): return render.DataGrid( @@ -32,7 +31,6 @@ def grid(): height=350, ) - @output @render.table def detail(): if ( @@ -42,7 +40,6 @@ def detail(): # "split", "records", "index", "columns", "values", "table" return df.iloc[list(input.grid_selected_rows())] - @output @render.text def debug(): return input.grid_selected_rows() diff --git a/tests/playwright/shiny/bugs/0696-resolve-id/app.py b/tests/playwright/shiny/bugs/0696-resolve-id/app.py index d1117b688..179e26a7b 100644 --- a/tests/playwright/shiny/bugs/0696-resolve-id/app.py +++ b/tests/playwright/shiny/bugs/0696-resolve-id/app.py @@ -221,12 +221,10 @@ def mod_x_server( def n(): return int(letters.index(input.input_radio_buttons())) - @output @render.image def out_image() -> ImgData: return {"src": penguin_imgs[n()]} - @output @render.plot def out_plot(): dt = [1, 2, 3, 4, 5] @@ -234,27 +232,22 @@ def out_plot(): dt[: ((n() + 1) % len(dt))], dt[: ((n() + 1) % len(dt))] # pyright: ignore ) - @output @render.table def out_table(): return pandas_df.head(n() + 1) - @output @render.data_frame def out_data_frame(): return pandas_df.head(n() + 1) - @output @render.text def out_text(): return f"Output text content. `input.radio_buttons()`: `{input.input_radio_buttons()}`" - @output @render.text def out_text_verbatim(): return f"Output text verbatim content. `input.radio_buttons()`: `{input.input_radio_buttons()}`" - @output @render.ui def out_ui(): return ui.p( diff --git a/tests/playwright/shiny/components/accordion/app.py b/tests/playwright/shiny/components/accordion/app.py index ddea80b5d..5ee4d2b7a 100644 --- a/tests/playwright/shiny/components/accordion/app.py +++ b/tests/playwright/shiny/components/accordion/app.py @@ -134,7 +134,6 @@ def _(): has_updates = not has_updates - @output @render.text def acc_txt(): return f"input.acc(): {input.acc()}" diff --git a/tests/playwright/shiny/inputs/input_file/app.py b/tests/playwright/shiny/inputs/input_file/app.py index d2c67b2d8..5b91212ff 100644 --- a/tests/playwright/shiny/inputs/input_file/app.py +++ b/tests/playwright/shiny/inputs/input_file/app.py @@ -29,7 +29,6 @@ def parsed_file(): file[0]["datapath"] ) - @output @render.table def summary(): df = parsed_file() @@ -58,7 +57,6 @@ def summary(): # checkboxes return info_df.loc[:, input.stats()] - @output @render.text def file2_info(): file2: typing.Union[typing.List["FileInfo"], None] = input.file2() diff --git a/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py b/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py index 1a1274fbb..898124cf1 100644 --- a/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py +++ b/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py @@ -65,22 +65,18 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.text def radio1_out(): return input.radio1() - @output @render.text def radio2_out(): return input.radio2() - @output @render.text def check1_out(): return input.check1() - @output @render.text def check2_out(): return input.check2() diff --git a/tests/playwright/shiny/server/output_transformer/app.py b/tests/playwright/shiny/server/output_transformer/app.py index 0c39cb48b..7548312e8 100644 --- a/tests/playwright/shiny/server/output_transformer/app.py +++ b/tests/playwright/shiny/server/output_transformer/app.py @@ -70,33 +70,27 @@ def render_test_text( def server(input: Inputs, output: Outputs, session: Session): - @output @render_test_text def t1(): return "t1; no call" # return "hello" - @output @render_test_text async def t2(): return "t2; no call" - @output @render_test_text() def t3(): return "t3; call" - @output @render_test_text() async def t4(): return "t4; call" - @output @render_test_text(extra_txt="w/ extra_txt") def t5(): return "t5; call" - @output @render_test_text(extra_txt="w/ extra_txt") async def t6(): return "t6; call" diff --git a/tests/playwright/shiny/session/flush/app.py b/tests/playwright/shiny/session/flush/app.py index 38e6643c9..a6591e31d 100644 --- a/tests/playwright/shiny/session/flush/app.py +++ b/tests/playwright/shiny/session/flush/app.py @@ -165,22 +165,18 @@ def reset(): cancel_on_ended_sync() cancel_on_ended_async() - @output @render.text def btn_txt(): return str(input.btn()) - @output @render.text def all_txt(): return str(all_vals.get()) - @output @render.text def flush_txt(): return str(flush_vals.get()) - @output @render.text def flushed_txt(): return str(flushed_vals.get()) diff --git a/tests/pytest/test_reactives.py b/tests/pytest/test_reactives.py index 45eb1eed3..b9439fd3b 100644 --- a/tests/pytest/test_reactives.py +++ b/tests/pytest/test_reactives.py @@ -1095,10 +1095,6 @@ async def _(): # ------------------------------------------------------------ @pytest.mark.asyncio async def test_event_type_check(): - conn = MockConnection() - session = App(ui.TagList(), None)._create_session(conn) - output = session.output - with pytest.raises(TypeError): # Should complain about missing argument to @event(). @event() @@ -1131,7 +1127,6 @@ async def _(): with pytest.raises(TypeError): # Should complain that @event must be applied before @output. @event(lambda: 1) # type: ignore - @output @render.text async def _(): ... @@ -1202,12 +1197,10 @@ def _(): def _(): ... - @output @render.text def _(): ... - @output @render.plot @event(lambda: 1) def _():