diff --git a/CHANGELOG.md b/CHANGELOG.md index c01bbc04d..46d1e3423 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `@render.data_frame`'s input value `input._column_filter()` has been deprecated. Please use `.filter()` to retrieve the same information. (#1374) +* Many `shiny.ui` methods (and `reactive.invalidate_later`) have deprecated their `session` argument. This rarely used argument would pass the `Session` into the method, but it is now considered an anti-pattern. If a custom `session` value is needed, please execute your code within a session context like `with shiny.session.session_context(MY_SESSION):`. (#793) + +* `require_active_session()` will display a warning if a non-`MISSING` value is provided. Please migrate from using `require_active_session(None)` to `require_active_session()`. If a session is still needed, please use the context `with shiny.session.session_context(MY_SESSION):`. (#793) + ### New features * `@render.data_frame` has added a few new methods: diff --git a/examples/airmass/app.py b/examples/airmass/app.py index bfb83ef32..c930067b5 100644 --- a/examples/airmass/app.py +++ b/examples/airmass/app.py @@ -12,7 +12,7 @@ from astropy.coordinates import AltAz, EarthLocation, SkyCoord from location import location_server, location_ui -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui app_ui = ui.page_fixed( ui.tags.h3("Air mass calculator"), @@ -57,7 +57,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): loc = location_server("location") time_padding = datetime.timedelta(hours=1.5) diff --git a/examples/annotation-export/app.py b/examples/annotation-export/app.py index e3835a05e..b0ddaa3f3 100644 --- a/examples/annotation-export/app.py +++ b/examples/annotation-export/app.py @@ -5,7 +5,7 @@ import pandas as pd import seaborn as sns -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui from shiny.plotutils import brushed_points path = Path(__file__).parent / "boulder_temp.csv" @@ -38,7 +38,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): annotated_data = reactive.value(weather_df) @reactive.calc diff --git a/examples/brownian/app.py b/examples/brownian/app.py index 998506e9c..40d3edac5 100644 --- a/examples/brownian/app.py +++ b/examples/brownian/app.py @@ -41,7 +41,7 @@ ) -def server(input, output, session): +def server(input): # BROWNIAN MOTION ==== @reactive.calc diff --git a/examples/cpuinfo/app.py b/examples/cpuinfo/app.py index cd34b728f..060e8c999 100644 --- a/examples/cpuinfo/app.py +++ b/examples/cpuinfo/app.py @@ -13,7 +13,7 @@ import numpy as np import pandas as pd -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui # The agg matplotlib backend seems to be a little more efficient than the default when # running on macOS, and also gives more consistent results across operating systems @@ -110,7 +110,7 @@ def cpu_current(): return cpu_percent(percpu=True) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): cpu_history = reactive.value(None) @reactive.calc diff --git a/examples/dataframe/app.py b/examples/dataframe/app.py index fba2dd07c..f10d4d081 100644 --- a/examples/dataframe/app.py +++ b/examples/dataframe/app.py @@ -2,7 +2,7 @@ import seaborn as sns from shinyswatch.theme import darkly -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui def app_ui(req): @@ -59,7 +59,7 @@ def light_dark_switcher(dark): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): df: reactive.value[pd.DataFrame] = reactive.value() @reactive.effect diff --git a/examples/duckdb/app.py b/examples/duckdb/app.py index c18ed967a..930e52ab5 100644 --- a/examples/duckdb/app.py +++ b/examples/duckdb/app.py @@ -66,7 +66,7 @@ def load_csv(con, csv_name, table_name): ) -def server(input, output, session): +def server(input): mod_counter = reactive.value(0) query_output_server("initial_query", con=con, remove_id="initial_query") diff --git a/examples/event/app.py b/examples/event/app.py index 683ffdded..b7e40a49c 100644 --- a/examples/event/app.py +++ b/examples/event/app.py @@ -1,6 +1,6 @@ import asyncio -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.tags.p( @@ -26,7 +26,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.btn) def _(): diff --git a/examples/global_pyplot/app.py b/examples/global_pyplot/app.py index a43d503cf..a5f325ee4 100644 --- a/examples/global_pyplot/app.py +++ b/examples/global_pyplot/app.py @@ -1,6 +1,6 @@ import matplotlib.pyplot as plt -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_checkbox("render", "Render", value=True), @@ -18,7 +18,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot def mpl(): if input.render(): diff --git a/examples/inputs-update/app.py b/examples/inputs-update/app.py index 092a1d2c6..759ed6def 100644 --- a/examples/inputs-update/app.py +++ b/examples/inputs-update/app.py @@ -1,6 +1,6 @@ from datetime import date -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.panel_title("Changing the values of inputs from the server"), @@ -87,7 +87,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): # We'll use these multiple times, so use short var names for diff --git a/examples/model-score/app.py b/examples/model-score/app.py index a5cf275d1..2c28bf2e9 100644 --- a/examples/model-score/app.py +++ b/examples/model-score/app.py @@ -9,7 +9,7 @@ from plotly_streaming import render_plotly_streaming from shinywidgets import output_widget -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui THRESHOLD_MID = 0.85 THRESHOLD_MID_COLOR = "rgb(0, 137, 26)" @@ -137,7 +137,7 @@ def app_ui(req): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def recent_df(): """ diff --git a/examples/moduleapp/app.py b/examples/moduleapp/app.py index fa3e3a624..fd8e73b58 100644 --- a/examples/moduleapp/app.py +++ b/examples/moduleapp/app.py @@ -59,7 +59,7 @@ def dynamic_counter(): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): counter_server("counter1") counter_wrapper_server("counter2_wrapper", "Counter 2") diff --git a/examples/penguins/app.py b/examples/penguins/app.py index 378f49cea..0d6d3b714 100644 --- a/examples/penguins/app.py +++ b/examples/penguins/app.py @@ -8,7 +8,7 @@ import seaborn as sns from colors import bg_palette, palette -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui sns.set_theme() @@ -52,7 +52,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def filtered_df() -> pd.DataFrame: """Returns a Pandas data frame that includes only the desired rows""" diff --git a/examples/req/app.py b/examples/req/app.py index 6e0f6e5e2..2847a40b2 100644 --- a/examples/req/app.py +++ b/examples/req/app.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui from shiny.types import SafeException app_ui = ui.page_fluid( @@ -15,7 +15,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def safe_click(): req(input.safe()) diff --git a/examples/static_plots/app.py b/examples/static_plots/app.py index b5c928169..c12a38a36 100644 --- a/examples/static_plots/app.py +++ b/examples/static_plots/app.py @@ -4,7 +4,7 @@ import seaborn as sns from plotnine.data import mtcars -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui nav = ui.navset_pill_list( ui.nav_control(ui.p("Choose a package", class_="lead text-center")), @@ -53,7 +53,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def fake_data(): n = 5000 diff --git a/examples/typed_inputs/app.py b/examples/typed_inputs/app.py index 0a8eabe4a..891bf7fc5 100644 --- a/examples/typed_inputs/app.py +++ b/examples/typed_inputs/app.py @@ -2,7 +2,7 @@ import typing -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_numeric("n", "N", 20), @@ -24,7 +24,7 @@ class ShinyInputs(Inputs): check: reactive.value[bool] -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): # Cast `input` to our ShinyInputs class. This just tells the static type checker # that we want it treated as a ShinyInputs object for type checking; it has no # run-time effect. diff --git a/examples/ui-func/app.py b/examples/ui-func/app.py index 08357fbde..785576f90 100644 --- a/examples/ui-func/app.py +++ b/examples/ui-func/app.py @@ -2,7 +2,7 @@ from starlette.requests import Request -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui def app_ui(request: Request): @@ -12,7 +12,7 @@ def app_ui(request: Request): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def now(): reactive.invalidate_later(0.1) diff --git a/shiny/_app.py b/shiny/_app.py index 3c960aca0..2e23ad8c4 100644 --- a/shiny/_app.py +++ b/shiny/_app.py @@ -57,8 +57,10 @@ class App: returns a UI definition, if you need the UI definition to be created dynamically for each pageview. server - A function which is called once for each session, ensuring that each session is - independent. + A function which is called once for each session, ensuring that each app is + independent. This function can either take one positional argument (``input: + Inputs``) or three positional arguments (``input: Inputs``, ``output:Outputs``, + ``session: Session``). static_assets Static files to be served by the app. If this is a string or Path object, it must be a directory, and it will be mounted at `/`. If this is a dictionary, diff --git a/shiny/_deprecated.py b/shiny/_deprecated.py index f6be2e40b..dc8db93cc 100644 --- a/shiny/_deprecated.py +++ b/shiny/_deprecated.py @@ -1,8 +1,6 @@ import warnings from typing import Any -from . import reactive, render - __all__ = ( "render_text", "render_plot", @@ -29,29 +27,53 @@ def warn_deprecated(message: str): def render_text(): """Deprecated. Please use render.text() instead of render_text().""" + import shiny.render as render + warn_deprecated("render_text() is deprecated. Use render.text() instead.") return render.text() def render_ui(): """Deprecated. Please use render.ui() instead of render_ui().""" + import shiny.render as render + warn_deprecated("render_ui() is deprecated. Use render.ui() instead.") return render.ui() def render_plot(*args: Any, **kwargs: Any): # type: ignore """Deprecated. Please use render.plot() instead of render_plot().""" + import shiny.render as render + warn_deprecated("render_plot() is deprecated. Use render.plot() instead.") return render.plot(*args, **kwargs) # type: ignore def render_image(*args: Any, **kwargs: Any): # type: ignore """Deprecated. Please use render.image() instead of render_image().""" + import shiny.render as render + warn_deprecated("render_image() is deprecated. Use render.image() instead.") return render.image(*args, **kwargs) # type: ignore def event(*args: Any, **kwargs: Any): """Deprecated. Please use @reactive.event() instead of @event().""" + import shiny.reactive as reactive + warn_deprecated("@event() is deprecated. Use @reactive.event() instead.") return reactive.event(*args, **kwargs) + + +def session_type_warning() -> None: + warn_deprecated( + "`session=` is deprecated. Please call with no `session=` argument as this parameter will be removed in the near future. If you need to pass a session, use your code inside a `with session_context(session):`." + ) + + +_session_param_docs = """ + session + Deprecated. If a custom :class:`~shiny.Session` is needed, please execute your + code inside `with shiny.session.session_context(session):`. See + :func:`~shiny.session.session_context` for more details. + """ diff --git a/shiny/api-examples/Module/app-core.py b/shiny/api-examples/Module/app-core.py index e5fe83d20..ea9d0d5e2 100644 --- a/shiny/api-examples/Module/app-core.py +++ b/shiny/api-examples/Module/app-core.py @@ -38,7 +38,7 @@ def out() -> str: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): counter_server("counter1") counter_server("counter2") diff --git a/shiny/api-examples/Progress/app-core.py b/shiny/api-examples/Progress/app-core.py index 5cb790251..d49971b11 100644 --- a/shiny/api-examples/Progress/app-core.py +++ b/shiny/api-examples/Progress/app-core.py @@ -1,6 +1,6 @@ import asyncio -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_action_button("button", "Compute"), @@ -8,7 +8,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text @reactive.event(input.button) async def compute(): diff --git a/shiny/api-examples/Renderer/app-core.py b/shiny/api-examples/Renderer/app-core.py index 3931c17e8..9b998405d 100644 --- a/shiny/api-examples/Renderer/app-core.py +++ b/shiny/api-examples/Renderer/app-core.py @@ -3,7 +3,7 @@ # Import the custom renderer implementations from renderers import render_capitalize, render_upper -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.h1("Capitalization renderer"), @@ -19,7 +19,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): # Hovering over `@render_upper` will display the class documentation @render_upper def upper(): diff --git a/shiny/api-examples/SafeException/app-core.py b/shiny/api-examples/SafeException/app-core.py index 00c261b0d..5da313698 100644 --- a/shiny/api-examples/SafeException/app-core.py +++ b/shiny/api-examples/SafeException/app-core.py @@ -1,10 +1,10 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui from shiny.types import SafeException app_ui = ui.page_fluid(ui.output_ui("safe"), ui.output_ui("unsafe")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def safe(): raise SafeException("This is a safe exception") diff --git a/shiny/api-examples/SilentCancelOutputException/app-core.py b/shiny/api-examples/SilentCancelOutputException/app-core.py index 4008f583c..9230ea6d7 100644 --- a/shiny/api-examples/SilentCancelOutputException/app-core.py +++ b/shiny/api-examples/SilentCancelOutputException/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui from shiny.types import SilentCancelOutputException app_ui = ui.page_fluid( @@ -12,7 +12,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def txt_out(): if not input.txt(): diff --git a/shiny/api-examples/SilentException/app-core.py b/shiny/api-examples/SilentException/app-core.py index 0fb3d7060..67e8e9446 100644 --- a/shiny/api-examples/SilentException/app-core.py +++ b/shiny/api-examples/SilentException/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui from shiny.types import SilentException app_ui = ui.page_fluid( @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def txt_out(): if not input.txt(): diff --git a/shiny/api-examples/Value/app-core.py b/shiny/api-examples/Value/app-core.py index bd854117e..317fe284e 100644 --- a/shiny/api-examples/Value/app-core.py +++ b/shiny/api-examples/Value/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_sidebar( ui.sidebar( @@ -9,7 +9,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): val = reactive.value(0) @reactive.effect diff --git a/shiny/api-examples/accordion/app-core.py b/shiny/api-examples/accordion/app-core.py index 4c91ec8d5..16ebc6722 100644 --- a/shiny/api-examples/accordion/app-core.py +++ b/shiny/api-examples/accordion/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui def make_items(): @@ -33,7 +33,7 @@ def make_items(): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def acc_single_val(): return "input.acc_single(): " + str(input.acc_single()) diff --git a/shiny/api-examples/accordion_panel/app-core.py b/shiny/api-examples/accordion_panel/app-core.py index c53697eb0..43a812899 100644 --- a/shiny/api-examples/accordion_panel/app-core.py +++ b/shiny/api-examples/accordion_panel/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui items = [ ui.accordion_panel(f"Section {letter}", f"Some narrative for section {letter}") @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): print(input.acc()) diff --git a/shiny/api-examples/calc/app-core.py b/shiny/api-examples/calc/app-core.py index fe2620253..bc52cb826 100644 --- a/shiny/api-examples/calc/app-core.py +++ b/shiny/api-examples/calc/app-core.py @@ -1,7 +1,7 @@ import random import time -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.card( @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def first(): input.first() diff --git a/shiny/api-examples/data_frame/app-core.py b/shiny/api-examples/data_frame/app-core.py index 3f12de9df..936d775e9 100644 --- a/shiny/api-examples/data_frame/app-core.py +++ b/shiny/api-examples/data_frame/app-core.py @@ -37,7 +37,7 @@ ) -def server(input, output, session): +def server(input): @render.data_frame def summary_data(): return render.DataGrid(summary_df.round(2), selection_mode="rows") diff --git a/shiny/api-examples/download_link/app-core.py b/shiny/api-examples/download_link/app-core.py index f8bc61ac2..ca8f497cf 100644 --- a/shiny/api-examples/download_link/app-core.py +++ b/shiny/api-examples/download_link/app-core.py @@ -2,14 +2,14 @@ import random from datetime import date -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.download_link("downloadData", "Download"), ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.download( filename=lambda: f"新型-{date.today().isoformat()}-{random.randint(100, 999)}.csv" ) diff --git a/shiny/api-examples/effect/app-core.py b/shiny/api-examples/effect/app-core.py index 01ac2e272..998aba9d4 100644 --- a/shiny/api-examples/effect/app-core.py +++ b/shiny/api-examples/effect/app-core.py @@ -1,9 +1,9 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid(ui.input_action_button("btn", "Press me!")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.btn) def _(): diff --git a/shiny/api-examples/event/app-core.py b/shiny/api-examples/event/app-core.py index 02f7a03b0..f62161dd4 100644 --- a/shiny/api-examples/event/app-core.py +++ b/shiny/api-examples/event/app-core.py @@ -1,6 +1,6 @@ import random -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.markdown( @@ -34,7 +34,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): # Update a random number every second val = reactive.value(random.randint(0, 1000)) diff --git a/shiny/api-examples/file_reader/app-core.py b/shiny/api-examples/file_reader/app-core.py index 7df04d271..4fdd32244 100644 --- a/shiny/api-examples/file_reader/app-core.py +++ b/shiny/api-examples/file_reader/app-core.py @@ -2,7 +2,7 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui dir = pathlib.Path(__file__).parent @@ -14,7 +14,7 @@ def read_file(): return pd.read_csv(dir / "mtcars.csv") -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.table def result(): return read_file() diff --git a/shiny/api-examples/input_action_button/app-core.py b/shiny/api-examples/input_action_button/app-core.py index 66f6abe13..0da530fd4 100644 --- a/shiny/api-examples/input_action_button/app-core.py +++ b/shiny/api-examples/input_action_button/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_slider("n", "Number of observations", min=0, max=1000, value=500), @@ -10,7 +10,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @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-core.py b/shiny/api-examples/input_action_link/app-core.py index 054ebacaa..5055a2065 100644 --- a/shiny/api-examples/input_action_link/app-core.py +++ b/shiny/api-examples/input_action_link/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_slider("n", "Number of observations", min=0, max=1000, value=500), @@ -10,7 +10,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @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-core.py b/shiny/api-examples/input_checkbox/app-core.py index b84f92de3..c5569a0c7 100644 --- a/shiny/api-examples/input_checkbox/app-core.py +++ b/shiny/api-examples/input_checkbox/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_checkbox("somevalue", "Some value", False), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def value(): return input.somevalue() diff --git a/shiny/api-examples/input_checkbox_group/app-core.py b/shiny/api-examples/input_checkbox_group/app-core.py index 638eee52b..0f8db41fc 100644 --- a/shiny/api-examples/input_checkbox_group/app-core.py +++ b/shiny/api-examples/input_checkbox_group/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, req, ui +from shiny import App, Inputs, render, req, ui app_ui = ui.page_fluid( ui.input_checkbox_group( @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def val(): req(input.colors()) diff --git a/shiny/api-examples/input_date/app-core.py b/shiny/api-examples/input_date/app-core.py index 8717487db..dda2e93d8 100644 --- a/shiny/api-examples/input_date/app-core.py +++ b/shiny/api-examples/input_date/app-core.py @@ -1,6 +1,6 @@ from datetime import date -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.input_date("date1", "Date:", value="2016-02-29"), @@ -26,7 +26,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/input_date_range/app-core.py b/shiny/api-examples/input_date_range/app-core.py index 07b4bcbac..b6c1723ac 100644 --- a/shiny/api-examples/input_date_range/app-core.py +++ b/shiny/api-examples/input_date_range/app-core.py @@ -1,6 +1,6 @@ from datetime import date -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.input_date_range( @@ -31,7 +31,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/input_file/app-core.py b/shiny/api-examples/input_file/app-core.py index 4cd0d99ee..a7e25897b 100644 --- a/shiny/api-examples/input_file/app-core.py +++ b/shiny/api-examples/input_file/app-core.py @@ -1,6 +1,6 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui from shiny.types import FileInfo app_ui = ui.page_fluid( @@ -15,7 +15,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def parsed_file(): file: list[FileInfo] | None = input.file1() diff --git a/shiny/api-examples/input_numeric/app-core.py b/shiny/api-examples/input_numeric/app-core.py index 3c6f2b0dc..bcf5e96ae 100644 --- a/shiny/api-examples/input_numeric/app-core.py +++ b/shiny/api-examples/input_numeric/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_numeric("obs", "Observations:", 10, min=1, max=100), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): return input.obs() diff --git a/shiny/api-examples/input_password/app-core.py b/shiny/api-examples/input_password/app-core.py index 4d56c7024..79399383d 100644 --- a/shiny/api-examples/input_password/app-core.py +++ b/shiny/api-examples/input_password/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_password("password", "Password:"), @@ -7,7 +7,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text @reactive.event(input.go) def value(): diff --git a/shiny/api-examples/input_radio_buttons/app-core.py b/shiny/api-examples/input_radio_buttons/app-core.py index 5b6129fa4..92fe7d789 100644 --- a/shiny/api-examples/input_radio_buttons/app-core.py +++ b/shiny/api-examples/input_radio_buttons/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_radio_buttons( @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def val(): return "You chose " + input.rb() diff --git a/shiny/api-examples/input_select/app-core.py b/shiny/api-examples/input_select/app-core.py index d7a668430..bdbddd177 100644 --- a/shiny/api-examples/input_select/app-core.py +++ b/shiny/api-examples/input_select/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_select( @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): return "You choose: " + str(input.state()) diff --git a/shiny/api-examples/input_selectize/app-core.py b/shiny/api-examples/input_selectize/app-core.py index 23a63d376..f3ca10e97 100644 --- a/shiny/api-examples/input_selectize/app-core.py +++ b/shiny/api-examples/input_selectize/app-core.py @@ -1,6 +1,6 @@ from html import escape # noqa: F401 -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui states = { "East Coast": {"NY": "New York", "NJ": "New Jersey", "CT": "Connecticut"}, @@ -41,7 +41,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): return "You choose: " + str(input.state()) diff --git a/shiny/api-examples/input_slider/app-core.py b/shiny/api-examples/input_slider/app-core.py index c8a238506..798e65bde 100644 --- a/shiny/api-examples/input_slider/app-core.py +++ b/shiny/api-examples/input_slider/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_slider("obs", "Number of bins:", min=10, max=100, value=30), @@ -9,7 +9,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot def distPlot(): np.random.seed(19680801) diff --git a/shiny/api-examples/input_switch/app-core.py b/shiny/api-examples/input_switch/app-core.py index f05cba431..a9042db28 100644 --- a/shiny/api-examples/input_switch/app-core.py +++ b/shiny/api-examples/input_switch/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_switch("somevalue", "Some value", False), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): return input.somevalue() diff --git a/shiny/api-examples/input_text/app-core.py b/shiny/api-examples/input_text/app-core.py index bf6ddd859..5d1bb6344 100644 --- a/shiny/api-examples/input_text/app-core.py +++ b/shiny/api-examples/input_text/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_text("caption", "Caption:", "Data summary"), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): return input.caption() diff --git a/shiny/api-examples/input_text_area/app-core.py b/shiny/api-examples/input_text_area/app-core.py index 3c708bc4e..23c73521c 100644 --- a/shiny/api-examples/input_text_area/app-core.py +++ b/shiny/api-examples/input_text_area/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_text_area( @@ -17,7 +17,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value_regular(): return input.caption_regular() diff --git a/shiny/api-examples/insert_accordion_panel/app-core.py b/shiny/api-examples/insert_accordion_panel/app-core.py index c9a2d0f98..fd28832b2 100644 --- a/shiny/api-examples/insert_accordion_panel/app-core.py +++ b/shiny/api-examples/insert_accordion_panel/app-core.py @@ -1,6 +1,6 @@ import random -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui def make_panel(letter: str) -> ui.AccordionPanel: @@ -17,7 +17,7 @@ def make_panel(letter: str) -> ui.AccordionPanel: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.add_panel) def _(): diff --git a/shiny/api-examples/insert_ui/app-core.py b/shiny/api-examples/insert_ui/app-core.py index 98cc7d49e..8d6c12c0d 100644 --- a/shiny/api-examples/insert_ui/app-core.py +++ b/shiny/api-examples/insert_ui/app-core.py @@ -1,11 +1,11 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("add", "Add UI"), ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.add) def _(): diff --git a/shiny/api-examples/invalidate_later/app-core.py b/shiny/api-examples/invalidate_later/app-core.py index 66de8233d..8ee800ac0 100644 --- a/shiny/api-examples/invalidate_later/app-core.py +++ b/shiny/api-examples/invalidate_later/app-core.py @@ -1,11 +1,11 @@ import random -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid(ui.output_text("value")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def value(): reactive.invalidate_later(0.5) diff --git a/shiny/api-examples/isolate/app-core.py b/shiny/api-examples/isolate/app-core.py index f6a12559d..1ab39ae8b 100644 --- a/shiny/api-examples/isolate/app-core.py +++ b/shiny/api-examples/isolate/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_slider("n", "Number of observations", min=0, max=1000, value=500), @@ -10,7 +10,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot(): # Take a reactive dependency on the action button... diff --git a/shiny/api-examples/layout_sidebar/app-core.py b/shiny/api-examples/layout_sidebar/app-core.py index 78e36e744..7d5cdab54 100644 --- a/shiny/api-examples/layout_sidebar/app-core.py +++ b/shiny/api-examples/layout_sidebar/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.layout_sidebar( @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/markdown/app-core.py b/shiny/api-examples/markdown/app-core.py index 0e2875ece..3e05828b3 100644 --- a/shiny/api-examples/markdown/app-core.py +++ b/shiny/api-examples/markdown/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui ui_app = ui.page_fluid( ui.markdown( @@ -15,7 +15,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/modal/app-core.py b/shiny/api-examples/modal/app-core.py index 8fb35ae1d..20476ad64 100644 --- a/shiny/api-examples/modal/app-core.py +++ b/shiny/api-examples/modal/app-core.py @@ -1,11 +1,11 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("show", "Show modal dialog"), ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.show) def _(): diff --git a/shiny/api-examples/nav_panel/app-core.py b/shiny/api-examples/nav_panel/app-core.py index db7650f65..b592787b1 100644 --- a/shiny/api-examples/nav_panel/app-core.py +++ b/shiny/api-examples/nav_panel/app-core.py @@ -1,6 +1,6 @@ from typing import List -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui from shiny.types import NavSetArg @@ -65,7 +65,7 @@ def nav_controls(prefix: str) -> List[NavSetArg]: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): print("Current navbar page: ", input.navbar_id()) diff --git a/shiny/api-examples/navset_hidden/app-core.py b/shiny/api-examples/navset_hidden/app-core.py index 8f537160d..1517ba04e 100644 --- a/shiny/api-examples/navset_hidden/app-core.py +++ b/shiny/api-examples/navset_hidden/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_sidebar( ui.sidebar( @@ -15,7 +15,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.controller) def _(): diff --git a/shiny/api-examples/notification_show/app-core.py b/shiny/api-examples/notification_show/app-core.py index 2f8e3a056..42c475d54 100644 --- a/shiny/api-examples/notification_show/app-core.py +++ b/shiny/api-examples/notification_show/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("show", "Show"), @@ -7,7 +7,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): ids: list[str] = [] n: int = 0 diff --git a/shiny/api-examples/output_image/app-core.py b/shiny/api-examples/output_image/app-core.py index 09452566a..a3b5f8bb8 100644 --- a/shiny/api-examples/output_image/app-core.py +++ b/shiny/api-examples/output_image/app-core.py @@ -1,10 +1,10 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui from shiny.types import ImgData app_ui = ui.page_fluid(ui.output_image("image")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.image def image(): from pathlib import Path diff --git a/shiny/api-examples/output_plot/app-core.py b/shiny/api-examples/output_plot/app-core.py index 339a1a6b7..1edada3f2 100644 --- a/shiny/api-examples/output_plot/app-core.py +++ b/shiny/api-examples/output_plot/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_slider( @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot def p(): np.random.seed(19680801) diff --git a/shiny/api-examples/output_table/app-core.py b/shiny/api-examples/output_table/app-core.py index 1dac0bab9..67dfd9ac1 100644 --- a/shiny/api-examples/output_table/app-core.py +++ b/shiny/api-examples/output_table/app-core.py @@ -2,7 +2,7 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui dir = pathlib.Path(__file__).parent mtcars = pd.read_csv(dir / "mtcars.csv") @@ -25,7 +25,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.table def result(): if not input.highlight(): diff --git a/shiny/api-examples/output_text/app-core.py b/shiny/api-examples/output_text/app-core.py index cf0d8e3d9..1e1cb0c29 100644 --- a/shiny/api-examples/output_text/app-core.py +++ b/shiny/api-examples/output_text/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.input_text("txt", "Enter the text to display below:", "delete me"), @@ -21,7 +21,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def text(): return input.txt() diff --git a/shiny/api-examples/output_transformer/app-core.py b/shiny/api-examples/output_transformer/app-core.py index e1c6a6a88..ab33c3fd4 100644 --- a/shiny/api-examples/output_transformer/app-core.py +++ b/shiny/api-examples/output_transformer/app-core.py @@ -2,7 +2,7 @@ from typing import Literal, overload -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui from shiny.render.transformer import ( TransformerMetadata, ValueFn, @@ -117,7 +117,7 @@ def render_capitalize( ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): # Without parentheses @render_capitalize def no_parens(): diff --git a/shiny/api-examples/output_ui/app-core.py b/shiny/api-examples/output_ui/app-core.py index cc074e377..5f9d8a91b 100644 --- a/shiny/api-examples/output_ui/app-core.py +++ b/shiny/api-examples/output_ui/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.input_action_button("add", "Add more controls"), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui @reactive.event(input.add) def moreControls(): diff --git a/shiny/api-examples/page_fixed/app-core.py b/shiny/api-examples/page_fixed/app-core.py index 80521e92c..24aaec1c7 100644 --- a/shiny/api-examples/page_fixed/app-core.py +++ b/shiny/api-examples/page_fixed/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fixed( ui.layout_sidebar( @@ -15,7 +15,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/page_fluid/app-core.py b/shiny/api-examples/page_fluid/app-core.py index a05d3618a..9eebf8eb6 100644 --- a/shiny/api-examples/page_fluid/app-core.py +++ b/shiny/api-examples/page_fluid/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.layout_sidebar( @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/page_sidebar/app-core.py b/shiny/api-examples/page_sidebar/app-core.py index 8ce58f8b8..a0eb92953 100644 --- a/shiny/api-examples/page_sidebar/app-core.py +++ b/shiny/api-examples/page_sidebar/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_sidebar( ui.sidebar( @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/panel_absolute/app-core.py b/shiny/api-examples/panel_absolute/app-core.py index 3340e5f93..70a47ee48 100644 --- a/shiny/api-examples/panel_absolute/app-core.py +++ b/shiny/api-examples/panel_absolute/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.panel_title("A basic absolute panel example", "Demo"), @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/panel_conditional/app-core.py b/shiny/api-examples/panel_conditional/app-core.py index 8e26f6558..17ff668d3 100644 --- a/shiny/api-examples/panel_conditional/app-core.py +++ b/shiny/api-examples/panel_conditional/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.input_checkbox("show", "Show radio buttons", False), @@ -16,7 +16,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/panel_title/app-core.py b/shiny/api-examples/panel_title/app-core.py index 7b42ac5d6..e13da14cd 100644 --- a/shiny/api-examples/panel_title/app-core.py +++ b/shiny/api-examples/panel_title/app-core.py @@ -1,9 +1,9 @@ -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid(ui.panel_title("Page title", "Window title")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/api-examples/poll/app-core.py b/shiny/api-examples/poll/app-core.py index 6079a0be4..94cfea752 100644 --- a/shiny/api-examples/poll/app-core.py +++ b/shiny/api-examples/poll/app-core.py @@ -6,7 +6,7 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui SYMBOLS = ["AAA", "BBB", "CCC", "DDD", "EEE", "FFF"] @@ -107,7 +107,7 @@ def stock_quotes() -> pd.DataFrame: ) -def server(input: Inputs, output: Outputs, session: Session) -> None: +def server(input: Inputs): def filtered_quotes(): df = stock_quotes() if input.symbols(): diff --git a/shiny/api-examples/popover/app-core.py b/shiny/api-examples/popover/app-core.py index 474821028..ab24aac30 100644 --- a/shiny/api-examples/popover/app-core.py +++ b/shiny/api-examples/popover/app-core.py @@ -1,6 +1,6 @@ from icons import gear_fill -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.popover( @@ -30,7 +30,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def plot_txt(): return f"" diff --git a/shiny/api-examples/remove_accordion_panel/app-core.py b/shiny/api-examples/remove_accordion_panel/app-core.py index fa51fd56d..fd7003f3a 100644 --- a/shiny/api-examples/remove_accordion_panel/app-core.py +++ b/shiny/api-examples/remove_accordion_panel/app-core.py @@ -1,6 +1,6 @@ import random -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui def make_panel(letter: str) -> ui.AccordionPanel: @@ -25,7 +25,7 @@ def make_panel(letter: str) -> ui.AccordionPanel: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): # Copy the list for user user_choices = [choice for choice in choices] diff --git a/shiny/api-examples/remove_ui/app-core.py b/shiny/api-examples/remove_ui/app-core.py index 2ae33b8f4..191f0d8c5 100644 --- a/shiny/api-examples/remove_ui/app-core.py +++ b/shiny/api-examples/remove_ui/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("rmv", "Remove UI"), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.rmv) def _(): diff --git a/shiny/api-examples/render_image/app-core.py b/shiny/api-examples/render_image/app-core.py index 60d35d90a..f256f7491 100644 --- a/shiny/api-examples/render_image/app-core.py +++ b/shiny/api-examples/render_image/app-core.py @@ -1,10 +1,10 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui from shiny.types import ImgData app_ui = ui.page_fluid(ui.output_image("image")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.image def image(): from pathlib import Path diff --git a/shiny/api-examples/req/app-core.py b/shiny/api-examples/req/app-core.py index e7246df2d..86a85ce5e 100644 --- a/shiny/api-examples/req/app-core.py +++ b/shiny/api-examples/req/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui from shiny.types import SafeException app_ui = ui.page_fluid( @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def safe_click(): req(input.safe()) diff --git a/shiny/api-examples/row/app-core.py b/shiny/api-examples/row/app-core.py index be69085a7..075b1e2a3 100644 --- a/shiny/api-examples/row/app-core.py +++ b/shiny/api-examples/row/app-core.py @@ -1,7 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.row( @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/session_context/app.py b/shiny/api-examples/session_context/app.py new file mode 100644 index 000000000..c20259e10 --- /dev/null +++ b/shiny/api-examples/session_context/app.py @@ -0,0 +1,61 @@ +from shiny import App, Inputs, Outputs, Session, module, reactive, render, ui +from shiny.session import session_context + + +# ============================================================ +# Slider module +# ============================================================ +@module.ui +def slider_ui(label: str = "Slider", value: int = 0) -> ui.TagChild: + return ui.card( + ui.input_slider(id="slider", label=label, min=0, max=5, value=value), + ui.output_text_verbatim(id="out"), + ) + + +@module.server +def slider_server( + input: Inputs, + # Include `output`, `session` so that we can return `session` + output: Outputs, + session: Session, +) -> Session: + @render.text + def out() -> str: + return f"Module - input.slider(): {input.slider()}" + + # Return module's session + return session + + +# ============================================================================= +# App that uses module +# ============================================================================= +app_ui = ui.page_fluid( + ui.markdown( + "# Use `session_context(session)` to manually set the session value within a context" + ), + ui.br(), + ui.input_action_button(id="reset_sliders", label="Reset sliders to 0"), + ui.br(), + ui.br(), + slider_ui("slider1", "Module: Slider 1", 2), + slider_ui("slider2", "Module: Slider 2", 5), +) + + +def server(input: Inputs): + slider1_session = slider_server("slider1") + slider2_session = slider_server("slider2") + + @reactive.Effect + @reactive.event(input.reset_sliders) + def _(): + # Update each slider within its module's session + with session_context(slider1_session): + ui.update_slider("slider", value=0) + with session_context(slider2_session): + ui.update_slider("slider", value=0) + + +app = App(app_ui, server) diff --git a/shiny/api-examples/sidebar/app-core.py b/shiny/api-examples/sidebar/app-core.py index 48e4c53d7..5efa5b724 100644 --- a/shiny/api-examples/sidebar/app-core.py +++ b/shiny/api-examples/sidebar/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.card( @@ -28,7 +28,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def state_left(): return f"input.sidebar_left(): {input.sidebar_left()}" diff --git a/shiny/api-examples/template/app-core.py b/shiny/api-examples/template/app-core.py index 7dacdbec2..8a622cc2e 100644 --- a/shiny/api-examples/template/app-core.py +++ b/shiny/api-examples/template/app-core.py @@ -7,7 +7,7 @@ ) -def server(input, output, session): +def server(input): @render.text def txt(): return f"n*2 is {input.n() * 2}" diff --git a/shiny/api-examples/todo_list/app-core.py b/shiny/api-examples/todo_list/app-core.py index fed00a20b..d6f920279 100644 --- a/shiny/api-examples/todo_list/app-core.py +++ b/shiny/api-examples/todo_list/app-core.py @@ -20,7 +20,7 @@ ) -def server(input, output, session): +def server(input): finished_tasks = reactive.value(0) task_counter = reactive.value(0) diff --git a/shiny/api-examples/update_accordion/app-core.py b/shiny/api-examples/update_accordion/app-core.py index 0cf6b58e4..c73d9c757 100644 --- a/shiny/api-examples/update_accordion/app-core.py +++ b/shiny/api-examples/update_accordion/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui items = [ ui.accordion_panel(f"Section {letter}", f"Some narrative for section {letter}") @@ -12,7 +12,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.set_acc) def _(): diff --git a/shiny/api-examples/update_accordion_panel/app-core.py b/shiny/api-examples/update_accordion_panel/app-core.py index b02d257f6..bae5212df 100644 --- a/shiny/api-examples/update_accordion_panel/app-core.py +++ b/shiny/api-examples/update_accordion_panel/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui def make_panel(letter: str) -> ui.AccordionPanel: @@ -17,7 +17,7 @@ def make_panel(letter: str) -> ui.AccordionPanel: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.update_panel) def _(): diff --git a/shiny/api-examples/update_action_button/app-core.py b/shiny/api-examples/update_action_button/app-core.py index 021b65c53..a49eefe16 100644 --- a/shiny/api-examples/update_action_button/app-core.py +++ b/shiny/api-examples/update_action_button/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, req, ui +from shiny import App, Inputs, reactive, req, ui app_ui = ui.page_fluid( ui.input_action_button("update", "Update other buttons and link"), @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): req(input.update()) diff --git a/shiny/api-examples/update_checkbox/app-core.py b/shiny/api-examples/update_checkbox/app-core.py index be8443fec..817e93240 100644 --- a/shiny/api-examples/update_checkbox/app-core.py +++ b/shiny/api-examples/update_checkbox/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_slider("controller", "Controller", min=0, max=1, value=0, step=1), @@ -6,7 +6,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): # True if controller is odd, False if even. diff --git a/shiny/api-examples/update_checkbox_group/app-core.py b/shiny/api-examples/update_checkbox_group/app-core.py index e842532a5..f5fff10c6 100644 --- a/shiny/api-examples/update_checkbox_group/app-core.py +++ b/shiny/api-examples/update_checkbox_group/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.tags.p("The first checkbox group controls the second"), @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): x = input.inCheckboxGroup() diff --git a/shiny/api-examples/update_date/app-core.py b/shiny/api-examples/update_date/app-core.py index b6785b522..f122ca759 100644 --- a/shiny/api-examples/update_date/app-core.py +++ b/shiny/api-examples/update_date/app-core.py @@ -1,6 +1,6 @@ from datetime import date, timedelta -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_slider("n", "Day of month", min=1, max=30, value=10), @@ -8,7 +8,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): d = date(2013, 4, input.n()) diff --git a/shiny/api-examples/update_date_range/app-core.py b/shiny/api-examples/update_date_range/app-core.py index b68cbba6e..7f4bc210b 100644 --- a/shiny/api-examples/update_date_range/app-core.py +++ b/shiny/api-examples/update_date_range/app-core.py @@ -1,6 +1,6 @@ from datetime import date, timedelta -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_slider("n", "Day of month", min=1, max=30, value=10), @@ -8,7 +8,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): d = date(2013, 4, input.n()) diff --git a/shiny/api-examples/update_navs/app-core.py b/shiny/api-examples/update_navs/app-core.py index d788e0efa..ef58ca387 100644 --- a/shiny/api-examples/update_navs/app-core.py +++ b/shiny/api-examples/update_navs/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_sidebar( ui.sidebar(ui.input_slider("controller", "Controller", min=1, max=3, value=1)), @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): ui.update_navs("inTabset", selected="panel" + str(input.controller())) diff --git a/shiny/api-examples/update_numeric/app-core.py b/shiny/api-examples/update_numeric/app-core.py index 84368a9ba..0fd164f26 100644 --- a/shiny/api-examples/update_numeric/app-core.py +++ b/shiny/api-examples/update_numeric/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_slider("controller", "Controller", min=0, max=20, value=10), @@ -7,7 +7,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): x = input.controller() diff --git a/shiny/api-examples/update_popover/app-core.py b/shiny/api-examples/update_popover/app-core.py index 9c6542ba0..e902b01e0 100644 --- a/shiny/api-examples/update_popover/app-core.py +++ b/shiny/api-examples/update_popover/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"), @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.btn_show) def _(): diff --git a/shiny/api-examples/update_radio_buttons/app-core.py b/shiny/api-examples/update_radio_buttons/app-core.py index 9cf6168d4..9a6ef6026 100644 --- a/shiny/api-examples/update_radio_buttons/app-core.py +++ b/shiny/api-examples/update_radio_buttons/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.tags.p("The first radio button group controls the second"), @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): x = input.inRadioButtons() diff --git a/shiny/api-examples/update_select/app-core.py b/shiny/api-examples/update_select/app-core.py index 237efbbdc..c366f6d43 100644 --- a/shiny/api-examples/update_select/app-core.py +++ b/shiny/api-examples/update_select/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.tags.p("The checkbox group controls the select input"), @@ -9,7 +9,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): x = input.inCheckboxGroup() diff --git a/shiny/api-examples/update_selectize/app-core.py b/shiny/api-examples/update_selectize/app-core.py index 31b83a501..fb81f346c 100644 --- a/shiny/api-examples/update_selectize/app-core.py +++ b/shiny/api-examples/update_selectize/app-core.py @@ -1,11 +1,11 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_selectize("x", "Server side selectize", choices=[], multiple=True), ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): ui.update_selectize( diff --git a/shiny/api-examples/update_sidebar/app-core.py b/shiny/api-examples/update_sidebar/app-core.py index 9dc0a155d..dc6a01621 100644 --- a/shiny/api-examples/update_sidebar/app-core.py +++ b/shiny/api-examples/update_sidebar/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_sidebar( ui.sidebar("Sidebar content", id="sidebar"), @@ -11,7 +11,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.open_sidebar) def _(): diff --git a/shiny/api-examples/update_slider/app-core.py b/shiny/api-examples/update_slider/app-core.py index cbc0ce142..58d6edb47 100644 --- a/shiny/api-examples/update_slider/app-core.py +++ b/shiny/api-examples/update_slider/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fixed( ui.input_slider( @@ -15,7 +15,7 @@ ) -def server(input, output, session): +def server(input: Inputs): @reactive.effect def _(): # You can update the value, min, max, and step. diff --git a/shiny/api-examples/update_text/app-core.py b/shiny/api-examples/update_text/app-core.py index c200124e0..bcb9778f7 100644 --- a/shiny/api-examples/update_text/app-core.py +++ b/shiny/api-examples/update_text/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.layout_column_wrap( @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.pet_type) def _(): diff --git a/shiny/api-examples/update_tooltip/app-core.py b/shiny/api-examples/update_tooltip/app-core.py index 9d44842a3..0afddfa99 100644 --- a/shiny/api-examples/update_tooltip/app-core.py +++ b/shiny/api-examples/update_tooltip/app-core.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.input_action_button("btn_show", "Show tooltip", class_="mt-3 me-3"), @@ -16,7 +16,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.btn_show) def _(): diff --git a/shiny/api-examples/www_dir/app-core.py b/shiny/api-examples/www_dir/app-core.py index 7f593c9df..1ecfbb6a7 100644 --- a/shiny/api-examples/www_dir/app-core.py +++ b/shiny/api-examples/www_dir/app-core.py @@ -1,6 +1,6 @@ from pathlib import Path -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui app_ui = ui.page_fluid( ui.tags.link(href="css/styles.css", rel="stylesheet"), @@ -22,7 +22,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): pass diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 64c68c5dd..0db976df0 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -15,7 +15,7 @@ from ..._deprecated import warn_deprecated from ..._namespaces import resolve_id from ..._utils import drop_none -from ...session import Session, require_active_session +from ...session import require_active_session, session_context from ...types import MISSING, MISSING_TYPE from ...ui import AccordionPanel as MainAccordionPanel from ...ui import accordion as main_accordion @@ -173,7 +173,7 @@ def as_width_unit(x: str | float | int) -> str: def toggle_switch( id: str, value: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Defunct. Please do not use method.""" warn_deprecated( @@ -185,12 +185,12 @@ def toggle_switch( if value is not None and not isinstance(value, bool): raise TypeError("`value` must be `None` or a single boolean value.") msg = drop_none({"id": resolve_id(id), "value": value}) - session = require_active_session(session) + active_session = require_active_session(session) async def callback(): - await session.send_custom_message("bslib.toggle-input-binary", msg) + await active_session.send_custom_message("bslib.toggle-input-binary", msg) - session.on_flush(callback, once=True) + active_session.on_flush(callback, once=True) ###################### @@ -323,40 +323,35 @@ def tooltip( # Deprecated 2023-08-23 -def tooltip_update(id: str, *args: TagChild, session: Optional[Session] = None) -> None: +def tooltip_update(id: str, *args: TagChild, session: MISSING_TYPE = MISSING) -> None: """Deprecated. Please use :func:`~shiny.ui.update_tooltip()` instead.""" warn_deprecated( "`shiny.experimental.ui.tooltip_update()` is deprecated. " "This method will be removed in a future version, " "please use `shiny.ui.update_tooltip()` instead." ) - main_update_tooltip( - id, - *args, - session=session, - ) + # Used to check session value + with session_context(require_active_session(session)): + main_update_tooltip(id, *args) # Deprecated 2023-09-12 -def update_tooltip(id: str, *args: TagChild, session: Optional[Session] = None) -> None: +def update_tooltip(id: str, *args: TagChild, session: MISSING_TYPE = MISSING) -> None: """Deprecated. Please use :func:`~shiny.ui.update_tooltip()` instead.""" warn_deprecated( "`shiny.experimental.ui.update_tooltip()` is deprecated. " "This method will be removed in a future version, " "please use `shiny.ui.update_tooltip()` instead." ) - main_update_tooltip( - id, - *args, - session=session, - ) + with session_context(require_active_session(session)): + main_update_tooltip(id, *args) # Deprecated 2023-08-23 def tooltip_toggle( id: str, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_tooltip()`.""" warn_deprecated( @@ -364,18 +359,15 @@ def tooltip_toggle( "This method will be removed in a future version, " "please use `shiny.ui.update_tooltip()` instead." ) - main_update_tooltip( - id=id, - show=show, - session=session, - ) + with session_context(require_active_session(session)): + main_update_tooltip(id=id, show=show) # Deprecated 2023-09-12 def toggle_tooltip( id: str, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_tooltip()` instead.""" warn_deprecated( @@ -383,11 +375,8 @@ def toggle_tooltip( "This method will be removed in a future version, " "please use `shiny.ui.update_tooltip()` instead." ) - main_update_tooltip( - id=id, - show=show, - session=session, - ) + with session_context(require_active_session(session)): + main_update_tooltip(id=id, show=show) ###################### @@ -515,7 +504,7 @@ def layout_sidebar( def toggle_sidebar( id: str, open: Literal["toggle", "open", "closed", "always"] | bool | None = None, - session: Session | None = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_sidebar()` instead.""" warn_deprecated( @@ -524,11 +513,8 @@ def toggle_sidebar( "please use `shiny.ui.update_sidebar()` instead." ) open_val = (open is True) or (open == "open") - return main_update_sidebar( - id, - show=open_val, - session=session, - ) + with session_context(require_active_session(session)): + return main_update_sidebar(id, show=open_val) # ---------------------------- @@ -541,7 +527,7 @@ def toggle_sidebar( def sidebar_toggle( id: str, open: Literal["toggle", "open", "closed", "always"] | bool | None = None, - session: Session | None = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_sidebar()` instead of `shiny.experimental.ui.sidebar_toggle()`.""" @@ -551,11 +537,8 @@ def sidebar_toggle( "please use `shiny.ui.update_sidebar()` instead." ) open_val = (open is True) or (open == "open") - main_update_sidebar( - id=id, - show=open_val, - session=session, - ) + with session_context(require_active_session(session)): + main_update_sidebar(id=id, show=open_val) # Deprecated 2023-06-13 @@ -689,7 +672,7 @@ def popover( def toggle_popover( id: str, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_popover()` instead.""" warn_deprecated( @@ -697,7 +680,8 @@ def toggle_popover( "This method will be removed in a future version, " "please use `shiny.ui.update_popover()` instead." ) - return main_update_popover(id, show, session=session) + with session_context(require_active_session(session)): + return main_update_popover(id, show) # Deprecated 2023-09-12 @@ -705,7 +689,7 @@ def update_popover( id: str, *args: TagChild, title: Optional[TagChild] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_popover()` instead.""" warn_deprecated( @@ -713,7 +697,8 @@ def update_popover( "This method will be removed in a future version, " "please use `shiny.ui.update_popover()` instead." ) - return main_update_popover(id, *args, title=title, session=session) + with session_context(require_active_session(session)): + return main_update_popover(id, *args, title=title) # ###################### @@ -786,7 +771,7 @@ def accordion_panel( def accordion_panel_set( id: str, values: bool | str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_accordion()` instead.""" warn_deprecated( @@ -794,14 +779,15 @@ def accordion_panel_set( "This method will be removed in a future version, " "please use `shiny.ui.update_accordion()` instead." ) - return main_update_accordion(id, show=values, session=session) + with session_context(require_active_session(session)): + return main_update_accordion(id, show=values) # # Deprecated 2023-09-12 def accordion_panel_open( id: str, values: bool | str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_accordion_panel(id, value, show=True)` or :func:`~shiny.ui.update_accordion(id, show = True)` instead.""" warn_deprecated( @@ -810,22 +796,23 @@ def accordion_panel_open( "please use `shiny.ui.shiny.ui.update_accordion_panel(id, value, show=True)` or `shiny.ui.update_accordion(id, show = True)` instead." ) - if isinstance(values, bool): - main_update_accordion(id, show=True, session=session) - return + with session_context(require_active_session(session)): + if isinstance(values, bool): + main_update_accordion(id, show=True) + return - if not isinstance(values, list): - values = [values] + if not isinstance(values, list): + values = [values] - for value in values: - main_update_accordion_panel(id, value, show=True, session=session) + for value in values: + main_update_accordion_panel(id, value, show=True) # # Deprecated 2023-09-12 def accordion_panel_close( id: str, values: bool | str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_accordion_panel(id, value, show=False)` or :func:`~shiny.ui.update_accordion(id, show = False)` instead.""" warn_deprecated( @@ -833,15 +820,16 @@ def accordion_panel_close( "This method will be removed in a future version, " "please use `shiny.ui.update_accordion_panel(id, value, show=False)` or `shiny.ui.update_accordion(id, show = False)` instead." ) - if isinstance(values, bool): - main_update_accordion(id, show=False, session=session) - return + with session_context(require_active_session(session)): + if isinstance(values, bool): + main_update_accordion(id, show=False) + return - if not isinstance(values, list): - values = [values] + if not isinstance(values, list): + values = [values] - for value in values: - main_update_accordion_panel(id, value, show=False, session=session) + for value in values: + main_update_accordion_panel(id, value, show=False) # # Deprecated 2023-09-12 @@ -850,7 +838,7 @@ def accordion_panel_insert( panel: AccordionPanel, target: Optional[str] = None, position: Literal["after", "before"] = "after", - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.insert_accordion_panel()` instead.""" warn_deprecated( @@ -858,20 +846,20 @@ def accordion_panel_insert( "This method will be removed in a future version, " "please use `shiny.ui.insert_accordion_panel()` instead." ) - return main_insert_accordion_panel( - id, - panel, - target=target, - position=position, - session=session, - ) + with session_context(require_active_session(session)): + return main_insert_accordion_panel( + id, + panel, + target=target, + position=position, + ) # # Deprecated 2023-09-12 def accordion_panel_remove( id: str, target: str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.remove_accordion_panel()` instead.""" warn_deprecated( @@ -879,11 +867,11 @@ def accordion_panel_remove( "This method will be removed in a future version, " "please use `shiny.ui.remove_accordion_panel()` instead." ) - return main_remove_accordion_panel( - id, - target=target, - session=session, - ) + with session_context(require_active_session(session)): + return main_remove_accordion_panel( + id, + target=target, + ) # Deprecated 2023-09-12 @@ -894,7 +882,7 @@ def update_accordion_panel( title: TagChild | None | MISSING_TYPE = MISSING, value: str | None | MISSING_TYPE = MISSING, icon: TagChild | None | MISSING_TYPE = MISSING, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """Deprecated. Please use :func:`~shiny.ui.update_accordion_panel()` instead.""" warn_deprecated( @@ -902,15 +890,15 @@ def update_accordion_panel( "This method will be removed in a future version, " "please use `shiny.ui.update_accordion_panel()` instead." ) - return main_update_accordion_panel( - id, - target, - *body, - title=title, - value=value, - icon=icon, - session=session, - ) + with session_context(require_active_session(session)): + return main_update_accordion_panel( + id, + target, + *body, + title=title, + value=value, + icon=icon, + ) # ###################### diff --git a/shiny/express/_module.py b/shiny/express/_module.py index 1c79a1b3d..35b49033a 100644 --- a/shiny/express/_module.py +++ b/shiny/express/_module.py @@ -23,7 +23,7 @@ def module( @functools.wraps(fn) def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: - parent_session = require_active_session(None) + parent_session = require_active_session() module_session = parent_session.make_scope(id) with session_context(module_session): diff --git a/shiny/module.py b/shiny/module.py index c4001b3b9..53b52d7a2 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -37,7 +37,7 @@ def server( from .session import require_active_session, session_context def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: - sess = require_active_session(None) + sess = require_active_session() child_sess = sess.make_scope(id) with session_context(child_sess): return fn(child_sess.input, child_sess.output, child_sess, *args, **kwargs) diff --git a/shiny/reactive/_core.py b/shiny/reactive/_core.py index b961cbcdc..5f10e997a 100644 --- a/shiny/reactive/_core.py +++ b/shiny/reactive/_core.py @@ -22,7 +22,9 @@ from .. import _utils from .._datastructures import PriorityQueueFIFO -from .._docstring import add_example, no_example +from .._deprecated import _session_param_docs as _session_param +from .._deprecated import session_type_warning +from .._docstring import add_example, doc_format, no_example from ..types import MISSING, MISSING_TYPE if TYPE_CHECKING: @@ -302,8 +304,11 @@ def lock() -> asyncio.Lock: @add_example() +@doc_format(session_param=_session_param) def invalidate_later( - delay: float, *, session: "MISSING_TYPE | Session | None" = MISSING + delay: float, + *, + session: MISSING_TYPE = MISSING, ) -> None: """ Scheduled Invalidation @@ -315,6 +320,7 @@ def invalidate_later( ---------- delay The number of seconds to wait before invalidating. + {session_param} Note ---- @@ -327,12 +333,19 @@ def invalidate_later( run. """ + cur_session: Session | MISSING_TYPE | None = None + if isinstance(session, MISSING_TYPE): from ..session import get_current_session # If no session is provided, autodetect the current session (this # could be None if outside of a session). - session = get_current_session() + cur_session = get_current_session() + else: + # Display warning that they should use `session_context()` + session_type_warning() + # Use current session value + cur_session = session ctx = get_current_context() # Pass an absolute time to our subtask, rather than passing the delay directly, in @@ -382,5 +395,5 @@ def cancel_task(): task.cancel() ctx.on_invalidate(cancel_task) - if session: - unsub = session.on_ended(cancel_task) + if cur_session: + unsub = cur_session.on_ended(cancel_task) diff --git a/shiny/render/_data_frame.py b/shiny/render/_data_frame.py index 8302dd03b..c3e06496e 100644 --- a/shiny/render/_data_frame.py +++ b/shiny/render/_data_frame.py @@ -803,7 +803,7 @@ def _set_output_metadata(self, *, output_id: str) -> None: # from creating a renderer in one module and registering it on an output with a # different session. - active_session = require_active_session(None) + active_session = require_active_session() if self._get_session() != active_session: raise RuntimeError( "The session used when creating the renderer " diff --git a/shiny/render/_data_frame_utils/_datagridtable.py b/shiny/render/_data_frame_utils/_datagridtable.py index f829fb018..512f7dc20 100644 --- a/shiny/render/_data_frame_utils/_datagridtable.py +++ b/shiny/render/_data_frame_utils/_datagridtable.py @@ -335,7 +335,7 @@ def serialize_pandas_df(df: "pd.DataFrame") -> dict[str, Any]: with pd.option_context("mode.copy_on_write", True): df = df.copy(deep=False) - session = require_active_session(None) + session = require_active_session() def wrap_shiny_html_with_session(x: TagNode): return wrap_shiny_html(x, session=session) diff --git a/shiny/render/_express.py b/shiny/render/_express.py index 719d25706..ad07832f3 100644 --- a/shiny/render/_express.py +++ b/shiny/render/_express.py @@ -122,7 +122,7 @@ async def render(self) -> JsonifiableDict | None: if len(results) == 0: return None - session = require_active_session(None) + session = require_active_session() return rendered_deps_to_jsonifiable( session._process_ui( TagList(*results) # pyright: ignore[reportArgumentType] diff --git a/shiny/render/_render.py b/shiny/render/_render.py index 0fec8ac0d..8dc063dcf 100644 --- a/shiny/render/_render.py +++ b/shiny/render/_render.py @@ -280,7 +280,7 @@ def __init__( async def render(self) -> dict[str, Jsonifiable] | Jsonifiable | None: is_userfn_async = self.fn.is_async() name = self.output_id - session = require_active_session(None) + session = require_active_session() # Module support name = session.ns(name) width = self.width @@ -605,7 +605,7 @@ def auto_output_ui(self) -> Tag: return _ui.output_ui(self.output_id) async def transform(self, value: TagChild) -> Jsonifiable: - session = require_active_session(None) + session = require_active_session() return rendered_deps_to_jsonifiable( session._process_ui(value), ) @@ -702,7 +702,7 @@ def __call__( # pyright: ignore[reportIncompatibleMethodOverride] def url() -> str: from urllib.parse import quote - session = require_active_session(None) + session = require_active_session() return f"session/{quote(session.id)}/download/{quote(self.output_id)}?w=" # Unlike most value functions, this one's name is `url`. But we want to get the diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index b79009c91..32401ec60 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -123,7 +123,7 @@ class Renderer(Generic[IT]): This value **will not** contain a module prefix (or session name-spacing). To get the fully resolved ID, call - `shiny.session.require_active_session(None).ns(self.output_id)`. + `shiny.session.require_active_session().ns(self.output_id)`. An initial value of `.__name__` (set within `Renderer.__call__(_fn)`) will be used until the output renderer is registered within the session. @@ -312,7 +312,7 @@ def _on_register(self) -> None: # (w/ module support) from ...session import require_active_session - session = require_active_session(None) + session = require_active_session() ns_name = session.output._ns(self.__name__) session.output.remove(ns_name) self._auto_registered = False diff --git a/shiny/render/transformer/_transformer.py b/shiny/render/transformer/_transformer.py index 635286ef7..8312296e9 100644 --- a/shiny/render/transformer/_transformer.py +++ b/shiny/render/transformer/_transformer.py @@ -276,7 +276,7 @@ def _meta(self) -> TransformerMetadata: """ from ...session import require_active_session - session = require_active_session(None) + session = require_active_session() return TransformerMetadata( session=session, name=self.output_id, diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 92f27374b..0f352232a 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -665,7 +665,7 @@ def verify_state(expected_state: ConnectionState) -> None: await flush() except ConnectionClosed: - ... + pass except Exception as e: try: # Starting in Python 3.10 this could be traceback.print_exception(e) diff --git a/shiny/session/_utils.py b/shiny/session/_utils.py index 637b6e83d..d298b05bd 100644 --- a/shiny/session/_utils.py +++ b/shiny/session/_utils.py @@ -2,6 +2,8 @@ # See https://www.python.org/dev/peps/pep-0655/#usage-in-python-3-11 from __future__ import annotations +from ..types import MISSING, MISSING_TYPE + __all__ = ( "get_current_session", "session_context", @@ -12,13 +14,14 @@ from contextvars import ContextVar, Token from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar -if TYPE_CHECKING: - from ._session import Session - -from .._docstring import no_example +from .._deprecated import session_type_warning +from .._docstring import add_example, no_example from .._namespaces import namespace_context from .._typing_extensions import TypedDict +if TYPE_CHECKING: + from ._session import Session + class RenderedDeps(TypedDict): deps: list[dict[str, Any]] @@ -58,11 +61,18 @@ def get_current_session() -> Optional[Session]: return session if session is not None else _default_session +@add_example() @contextmanager -def session_context(session: Optional[Session]): +def session_context(session: Session | None): """ A context manager for current session. + This context manager is used to set the current session for the duration of the code + block. This is meant for advanced use cases where a custom session handling is + needed. + + For example, if you want to use a session value (e.g. module's session) that is different from the current session (e.g. a global session), then executing the module code within `with session_context(mod_sess):` will execute the code in the context of the module session. + Parameters ---------- session @@ -78,15 +88,16 @@ def session_context(session: Optional[Session]): @no_example() -def require_active_session(session: Optional[Session]) -> Session: +def require_active_session(session: MISSING_TYPE = MISSING) -> Session: """ Raise an exception if no Shiny session is currently active. Parameters ---------- session - A :class:`~shiny.Session` instance. If not provided, the session is inferred via - :func:`~shiny.session.get_current_session`. + Deprecated. If a custom :class:`~shiny.Session` is needed, please execute your code inside `with + shiny.session.session_context(session):`. See + :func:`~shiny.session.session_context` for more details. Returns ------- @@ -109,9 +120,19 @@ def require_active_session(session: Optional[Session]) -> Session: * :func:`~shiny.session.get_current_session` """ - if session is None: - session = get_current_session() - if session is None: + session_is_missing = True + + if not isinstance(session, MISSING_TYPE): + session_type_warning() + session_is_missing = ( + session is None + ) # pyright: ignore[reportUnnecessaryComparison] + + ret_session: Session | None = None + if session_is_missing: + ret_session = get_current_session() + + if ret_session is None: import inspect call_stack = inspect.stack() @@ -131,7 +152,7 @@ def require_active_session(session: Optional[Session]) -> Session: raise RuntimeError( f"{calling_fn_name}() must be called from within an active Shiny session." ) - return session + return ret_session # Ideally I'd love not to limit the types for T, but if I don't, the type checker has diff --git a/shiny/templates/app-templates/dashboard/app-core.py b/shiny/templates/app-templates/dashboard/app-core.py index 8cb1f85c4..d248a1c08 100644 --- a/shiny/templates/app-templates/dashboard/app-core.py +++ b/shiny/templates/app-templates/dashboard/app-core.py @@ -53,7 +53,7 @@ ) -def server(input, output, session): +def server(input): @reactive.calc def filtered_df(): filt_df = df[df["species"].isin(input.species())] diff --git a/shiny/ui/_accordion.py b/shiny/ui/_accordion.py index e82c6376b..8e666bb12 100644 --- a/shiny/ui/_accordion.py +++ b/shiny/ui/_accordion.py @@ -1,21 +1,19 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Literal, Optional, TypeVar +from typing import Literal, Optional, TypeVar from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, css, tags -from .._docstring import add_example +from .._deprecated import _session_param_docs as _session_param +from .._docstring import add_example, doc_format from .._namespaces import resolve_id_or_none from .._utils import drop_none, private_random_id -from ..session import require_active_session +from ..session import require_active_session, session_context from ..types import MISSING, MISSING_TYPE from ._html_deps_shinyverse import components_dependencies from ._tag import consolidate_attrs from .css._css_unit import CssUnit, as_css_unit -if TYPE_CHECKING: - from ..session import Session - __all__ = ( "accordion", "accordion_panel", @@ -365,11 +363,10 @@ def accordion_panel( # you might want to open a panel after inserting it. def _send_panel_message( id: str, - session: Session | None, **kwargs: object, ) -> None: message = drop_none(kwargs) - session = require_active_session(session) + session = require_active_session() session.on_flush(lambda: session.send_input_message(id, message), once=True) @@ -378,7 +375,6 @@ def _accordion_panel_action( id: str, method: str, values: bool | str | list[str], - session: Session | None, ) -> None: if not isinstance(values, bool): if not isinstance(values, list): @@ -387,18 +383,18 @@ def _accordion_panel_action( _send_panel_message( id, - session, method=method, values=values, ) @add_example() +@doc_format(session_param=_session_param) def update_accordion( id: str, *, show: bool | str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Dynamically set accordions' states. @@ -415,8 +411,7 @@ def update_accordion( Either a string or list of strings (used to identify particular :func:`~shiny.ui.accordion_panel`(s) by their `value`) or a `bool` to set the state of all panels. - session - A Shiny session object (the default should almost always be used). + {session_param} References ---------- @@ -434,16 +429,19 @@ def update_accordion( show_val = [] else: show_val = show - _accordion_panel_action(id=id, method="set", values=show_val, session=session) + active_session = require_active_session(session) + with session_context(active_session): + _accordion_panel_action(id=id, method="set", values=show_val) @add_example() +@doc_format(session_param=_session_param) def insert_accordion_panel( id: str, panel: AccordionPanel, target: Optional[str] = None, position: Literal["after", "before"] = "after", - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Insert an :func:`~shiny.ui.accordion_panel`. @@ -460,8 +458,7 @@ def insert_accordion_panel( Should `panel` be added before or after the target? When `target=None`, `"after"` will append after the last panel and `"before"` will prepend before the first panel. - session - A Shiny session object (the default should almost always be used). + {session_param} References ---------- @@ -478,25 +475,25 @@ def insert_accordion_panel( if position not in ("after", "before"): raise ValueError("`position` must be either 'after' or 'before'") - session = require_active_session(session) + active_session = require_active_session(session) # Add accordion ID to panel; Used when `accordion(multiple=False)` panel._accordion_id = id - - _send_panel_message( - id, - session, - method="insert", - panel=session._process_ui(panel.resolve()), - target=None if target is None else _assert_str(target), - position=position, - ) + with session_context(active_session): + _send_panel_message( + id, + method="insert", + panel=active_session._process_ui(panel.resolve()), + target=None if target is None else _assert_str(target), + position=position, + ) @add_example() +@doc_format(session_param=_session_param) def remove_accordion_panel( id: str, target: str | list[str], - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Remove an :func:`~shiny.ui.accordion_panel`. @@ -507,8 +504,7 @@ def remove_accordion_panel( A string that matches an existing :func:`~shiny.ui.accordion`'s `id`. target The `value` of an existing panel to remove. - session - A Shiny session object (the default should almost always be used). + {session_param} References ---------- @@ -525,12 +521,13 @@ def remove_accordion_panel( if not isinstance(target, list): target = [target] - _send_panel_message( - id, - session, - method="remove", - target=_assert_list_str(target), - ) + active_session = require_active_session(session) + with session_context(active_session): + _send_panel_message( + id, + method="remove", + target=_assert_list_str(target), + ) T = TypeVar("T") @@ -545,6 +542,7 @@ def _missing_none_x(x: T | None | MISSING_TYPE) -> T | Literal[""] | None: @add_example() +@doc_format(session_param=_session_param) def update_accordion_panel( id: str, target: str, @@ -553,7 +551,7 @@ def update_accordion_panel( value: str | None | MISSING_TYPE = MISSING, icon: TagChild | None | MISSING_TYPE = MISSING, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Dynamically update accordion panel contents. @@ -576,8 +574,9 @@ def update_accordion_panel( If not missing, the new value of the panel. icon If not missing, the new icon of the panel. - session - A Shiny session object (the default should almost always be used). + show + If provided, open/close the targeted panel. + {session_param} References ---------- @@ -592,31 +591,30 @@ def update_accordion_panel( * :func:`~shiny.ui.remove_accordion_panel` """ - session = require_active_session(session) + active_session = require_active_session(session) + with session_context(active_session): - # If `show` is given, then we need to open/close the targeted panel - # Perform before changing `value` at the same time. - if show is not None: - _accordion_panel_action( - id=id, - method="open" if bool(show) else "close", - values=[target], - session=session, - ) + # If `show` is given, then we need to open/close the targeted panel + # Perform before changing `value` at the same time. + if show is not None: + _accordion_panel_action( + id=id, + method="open" if bool(show) else "close", + values=[target], + ) - title = _missing_none_x(title) - value = _missing_none_x(value) - icon = _missing_none_x(icon) - _send_panel_message( - id, - session, - method="update", - target=_assert_str(target), - value=None if value is None else _assert_str(value), - body=None if len(body) == 0 else session._process_ui(body), - title=None if title is None else session._process_ui(title), - icon=None if icon is None else session._process_ui(icon), - ) + title = _missing_none_x(title) + value = _missing_none_x(value) + icon = _missing_none_x(icon) + _send_panel_message( + id, + method="update", + target=_assert_str(target), + value=None if value is None else _assert_str(value), + body=None if len(body) == 0 else active_session._process_ui(body), + title=None if title is None else active_session._process_ui(title), + icon=None if icon is None else active_session._process_ui(icon), + ) def _assert_str(x: str) -> str: diff --git a/shiny/ui/_input_dark_mode.py b/shiny/ui/_input_dark_mode.py index 1d4f68bc6..9edec9bc9 100644 --- a/shiny/ui/_input_dark_mode.py +++ b/shiny/ui/_input_dark_mode.py @@ -8,7 +8,8 @@ from .._docstring import add_example, no_example from .._namespaces import resolve_id -from ..session import Session, require_active_session +from ..session import require_active_session +from ..types import MISSING, MISSING_TYPE from ._web_component import web_component BootstrapColorMode = Literal["light", "dark"] @@ -80,9 +81,11 @@ def validate_dark_mode_option(mode: BootstrapColorMode) -> BootstrapColorMode: @no_example() def update_dark_mode( - mode: BootstrapColorMode, *, session: Optional[Session] = None + mode: BootstrapColorMode, + *, + session: MISSING_TYPE = MISSING, ) -> None: - session = require_active_session(session) + active_session = require_active_session(session) mode = validate_dark_mode_option(mode) @@ -90,4 +93,4 @@ def update_dark_mode( "method": "toggle", "value": mode, } - session._send_message_sync({"custom": {"bslib.toggle-dark-mode": msg}}) + active_session._send_message_sync({"custom": {"bslib.toggle-dark-mode": msg}}) diff --git a/shiny/ui/_input_update.py b/shiny/ui/_input_update.py index c01cf7ef4..aa97db3ee 100644 --- a/shiny/ui/_input_update.py +++ b/shiny/ui/_input_update.py @@ -27,18 +27,19 @@ from starlette.requests import Request from starlette.responses import JSONResponse, Response +from .._deprecated import _session_param_docs as _session_param from .._docstring import add_example, doc_format, no_example from .._namespaces import ResolvedId, resolve_id from .._typing_extensions import NotRequired, TypedDict from .._utils import drop_none from ..input_handler import input_handlers from ..session import require_active_session, session_context -from ..types import ActionButtonValue +from ..types import MISSING, MISSING_TYPE, ActionButtonValue from ._input_check_radio import ChoicesArg, _generate_options from ._input_date import _as_date_attr from ._input_select import SelectChoicesArg, _normalize_choices, _render_choices from ._input_slider import SliderStepArg, SliderValueArg, _as_numeric, _slider_type -from ._utils import JSEval, _session_on_flush_send_msg, extract_js_keys +from ._utils import JSEval, extract_js_keys, session_on_flush_send_msg if TYPE_CHECKING: from ..session import Session @@ -67,13 +68,13 @@ # input_action_button.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_action_button( id: str, *, label: Optional[str] = None, icon: TagChild = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the label and/or icon of an action button on the client. @@ -86,9 +87,7 @@ def update_action_button( An input label. icon An icon to appear inline with the button/link. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -99,11 +98,14 @@ def update_action_button( * :func:`~shiny.input_action_button` """ - session = require_active_session(session) + active_session = require_active_session(session) # TODO: supporting a TagChild for label would require changes to shiny.js # https://github.com/rstudio/shiny/issues/1140 - msg = {"label": label, "icon": session._process_ui(icon)["html"] if icon else None} - session.send_input_message(id, drop_none(msg)) + msg = { + "label": label, + "icon": active_session._process_ui(icon)["html"] if icon else None, + } + active_session.send_input_message(id, drop_none(msg)) update_action_link = update_action_button @@ -118,7 +120,7 @@ def update_task_button( id: str, *, state: Optional[str] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the state of a task button on the client. @@ -141,17 +143,17 @@ def update_task_button( :func:`~shiny.session.get_current_session`. """ - session = require_active_session(session) + active_session = require_active_session(session) if state is not None: - resolved_id = session.ns(id) + resolved_id = active_session.ns(id) if state != "ready": manual_task_reset_buttons.add(resolved_id) else: manual_task_reset_buttons.discard(resolved_id) msg = {"state": state} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) manual_task_reset_buttons: set[ResolvedId] = set() @@ -169,7 +171,8 @@ def callback() -> None: # opted out using set_task_button_manual_reset(), we should reset after a # flush cycle where a bslib.taskbutton value is seen. if name not in manual_task_reset_buttons: - update_task_button(name, state="ready", session=session) + with session_context(session): + update_task_button(name, state="ready") return ActionButtonValue(cast(int, value["value"])) @@ -178,13 +181,13 @@ def callback() -> None: # input_check_radio.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_checkbox( id: str, *, label: Optional[str] = None, value: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a checkbox input on the client. @@ -197,9 +200,7 @@ def update_checkbox( An input label. value A new value. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -210,19 +211,19 @@ def update_checkbox( * :func:`~shiny.ui.input_checkbox` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = {"label": label, "value": value} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) @no_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_switch( id: str, *, label: Optional[str] = None, value: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a switch input on the client. @@ -235,9 +236,7 @@ def update_switch( An input label. value A new value. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -248,13 +247,13 @@ def update_switch( * :func:`~shiny.ui.input_switch` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = {"label": label, "value": value} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_checkbox_group( id: str, *, @@ -262,7 +261,7 @@ def update_checkbox_group( choices: Optional[ChoicesArg] = None, selected: Optional[str | list[str] | tuple[str, ...]] = None, inline: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a checkbox group input on the client. @@ -281,9 +280,7 @@ def update_checkbox_group( The values that should be initially selected, if any. inline If ``True``, the result is displayed inline - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -306,7 +303,7 @@ def update_checkbox_group( @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_radio_buttons( id: str, *, @@ -314,7 +311,7 @@ def update_radio_buttons( choices: Optional[ChoicesArg] = None, selected: Optional[str] = None, inline: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a radio input on the client. @@ -333,9 +330,7 @@ def update_radio_buttons( The values that should be initially selected, if any. inline If ``True```, the result is displayed inline - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -365,13 +360,13 @@ def _update_choice_input( choices: Optional[ChoicesArg] = None, selected: Optional[str | list[str] | tuple[str, ...]] = None, inline: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: - session = require_active_session(session) + active_session = require_active_session(session) options = None if choices is not None: # https://github.com/posit-dev/py-shiny/issues/708#issuecomment-1696352934 - with session_context(session): + with session_context(active_session): resolved_id = resolve_id(id) opts = _generate_options( @@ -381,16 +376,16 @@ def _update_choice_input( selected=selected, inline=inline, ) - options = session._process_ui(opts)["html"] + options = active_session._process_ui(opts)["html"] msg = {"label": label, "options": options, "value": selected} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # input_date.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_date( id: str, *, @@ -398,7 +393,7 @@ def update_date( value: Optional[date | str] = None, min: Optional[date | str] = None, max: Optional[date | str] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a date input on the client. @@ -416,9 +411,7 @@ def update_date( The minimum allowed value. max The maximum allowed value. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -429,18 +422,18 @@ def update_date( * :func:`~shiny.ui.input_date` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = { "label": label, "value": _as_date_attr(value), "min": _as_date_attr(min), "max": _as_date_attr(max), } - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_date_range( id: str, *, @@ -449,7 +442,7 @@ def update_date_range( end: Optional[date | str] = None, min: Optional[date | str] = None, max: Optional[date | str] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the start and end values of a date range input on the client. @@ -472,9 +465,7 @@ def update_date_range( The minimum allowed value. max The maximum allowed value. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -485,7 +476,7 @@ def update_date_range( * :func:`~shiny.ui.input_date_range` """ - session = require_active_session(session) + active_session = require_active_session(session) value = {"start": _as_date_attr(start), "end": _as_date_attr(end)} msg = { "label": label, @@ -493,14 +484,14 @@ def update_date_range( "min": _as_date_attr(min), "max": _as_date_attr(max), } - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # input_numeric.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_numeric( id: str, *, @@ -509,7 +500,7 @@ def update_numeric( min: Optional[float] = None, max: Optional[float] = None, step: Optional[float] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a number input on the client. @@ -528,8 +519,7 @@ def update_numeric( The maximum allowed value. step Interval to use when stepping between min and max. - session - The :class:`~shiny.Session` object passed to the server function of a :class:`~shiny.App`. + {session_param} Note ---- @@ -540,7 +530,7 @@ def update_numeric( * :func:`~shiny.ui.input_numeric` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = { "label": label, "value": value, @@ -548,21 +538,21 @@ def update_numeric( "max": max, "step": step, } - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # input_select.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_select( id: str, *, label: Optional[str] = None, choices: Optional[SelectChoicesArg] = None, selected: Optional[str | list[str]] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a select input on the client. @@ -581,9 +571,7 @@ def update_select( ```` labels. selected The values that should be initially selected, if any. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -595,7 +583,7 @@ def update_select( * :func:`~shiny.ui.update_selectize` """ - session = require_active_session(session) + active_session = require_active_session(session) selected_values = selected if isinstance(selected, str): @@ -605,16 +593,14 @@ def update_select( options = None else: option_tags = _render_choices(_normalize_choices(choices), selected) - # Typing problem due to a bug in pylance: - # https://github.com/microsoft/pylance-release/issues/2377 - options = session._process_ui(option_tags)["html"] # type: ignore + options = active_session._process_ui(option_tags)["html"] msg = { "label": label, "options": options, "value": selected_values, } - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) class FlatSelectChoice(TypedDict): @@ -624,7 +610,7 @@ class FlatSelectChoice(TypedDict): @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_selectize( id: str, *, @@ -633,7 +619,7 @@ def update_selectize( selected: Optional[str | list[str]] = None, options: Optional[dict[str, str | float | JSEval]] = None, server: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a selectize.js powered input on the client. @@ -658,9 +644,7 @@ def update_selectize( Whether to store choices on the server side, and load the select options dynamically on searching, instead of writing all choices into the page at once (i.e., only use the client-side version of selectize.js) - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -671,12 +655,16 @@ def update_selectize( * :func:`~shiny.ui.input_selectize` """ - session = require_active_session(session) + active_session = require_active_session(session) if not server: - return update_select( - id, label=label, choices=choices, selected=selected, session=session - ) + with session_context(active_session): + return update_select( + id, + label=label, + choices=choices, + selected=selected, + ) if options is not None: cfg = TagList( @@ -687,7 +675,10 @@ def update_selectize( data_eval=json.dumps(extract_js_keys(options)), ) ) - session.send_input_message(id, drop_none({"config": cfg.get_html_string()})) + active_session.send_input_message( + id, + drop_none({"config": cfg.get_html_string()}), + ) # Transform choices to a list of dicts (this is the form the client wants) # [{"label": "Foo", "value": "foo", "optgroup": "foo"}, ...] @@ -696,13 +687,18 @@ def update_selectize( for k, v in _normalize_choices(choices).items(): if not isinstance(v, Mapping): flat_choices.append( - FlatSelectChoice(value=k, label=session._process_ui(v)["html"]) + FlatSelectChoice( + value=k, + label=active_session._process_ui(v)["html"], + ) ) else: # The optgroup case flat_choices.extend( [ FlatSelectChoice( - optgroup=k, value=k2, label=session._process_ui(v2)["html"] + optgroup=k, + value=k2, + label=active_session._process_ui(v2)["html"], ) for (k2, v2) in v.items() ] @@ -793,17 +789,19 @@ def selectize_choices_json(request: Request) -> Response: msg = { "label": label, "value": selected_values, - "url": session.dynamic_route(f"update_selectize_{id}", selectize_choices_json), + "url": active_session.dynamic_route( + f"update_selectize_{id}", selectize_choices_json + ), } - return session.send_input_message(id, drop_none(msg)) + return active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # input_slider.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_slider( id: str, *, @@ -814,7 +812,7 @@ def update_slider( step: Optional[SliderStepArg] = None, time_format: Optional[str] = None, timezone: Optional[str] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a slider input on the client. @@ -847,9 +845,7 @@ def update_slider( specifying the time zone offset for the displayed times, in the format "+HHMM" or "-HHMM". If ``None`` (the default), times will be displayed in the browser's time zone. The value "+0000" will result in UTC time. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -860,7 +856,7 @@ def update_slider( * :func:`~shiny.ui.input_slider` """ - session = require_active_session(session) + active_session = require_active_session(session) # Get any non-None value to see if the `data-type` may need to change val = value[0] if isinstance(value, (tuple, list)) else value @@ -890,21 +886,21 @@ def update_slider( "time_format": time_format, "timezone": timezone, } - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # input_text.py # ----------------------------------------------------------------------------- @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_text( id: str, *, label: Optional[str] = None, value: Optional[str] = None, placeholder: Optional[str] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a text input on the client. @@ -919,9 +915,7 @@ def update_text( A new value. placeholder A hint as to what can be entered into the control. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -932,9 +926,9 @@ def update_text( * :func:`~shiny.ui.input_text` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = {"label": label, "value": value, "placeholder": placeholder} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) update_text_area = update_text @@ -948,9 +942,11 @@ def update_text( # TODO: we should probably provide a nav_select() alias for this as well @add_example() -@doc_format(note=_note) +@doc_format(note=_note, session_param=_session_param) def update_navs( - id: str, selected: Optional[str] = None, session: Optional[Session] = None + id: str, + selected: Optional[str] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Change the value of a navs container on the client. @@ -961,9 +957,7 @@ def update_navs( An input id. selected The values that should be initially selected, if any. - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -976,20 +970,21 @@ def update_navs( * :func:`~shiny.ui.page_navbar` """ - session = require_active_session(session) + active_session = require_active_session(session) msg = {"value": selected} - session.send_input_message(id, drop_none(msg)) + active_session.send_input_message(id, drop_none(msg)) # ----------------------------------------------------------------------------- # tooltips.py # ----------------------------------------------------------------------------- @add_example() +@doc_format(session_param=_session_param) def update_tooltip( id: str, *args: TagChild, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Update tooltip contents. @@ -1002,33 +997,32 @@ def update_tooltip( Contents to the tooltip's body. show Opens (`True`) or closes (`False`) the tooltip. - session - A Shiny session object (the default should almost always be used). + {session_param} """ - _session_on_flush_send_msg( - id, - session, - drop_none( - { - "method": "update", - "title": ( - require_active_session(session)._process_ui(TagList(*args)) - if len(args) > 0 - else None - ), - } - ), - ) - if show is not None: - _session_on_flush_send_msg( + active_session = require_active_session(session) + with session_context(active_session): + session_on_flush_send_msg( id, - session, - { - "method": "toggle", - "value": _normalize_show_value(show), - }, + drop_none( + { + "method": "update", + "title": ( + active_session._process_ui(TagList(*args)) + if len(args) > 0 + else None + ), + } + ), ) + if show is not None: + session_on_flush_send_msg( + id, + { + "method": "toggle", + "value": _normalize_show_value(show), + }, + ) # ----------------------------------------------------------------------------- @@ -1037,12 +1031,13 @@ def update_tooltip( @add_example() +@doc_format(session_param=_session_param) def update_popover( id: str, *args: TagChild, title: Optional[TagChild] = None, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Update the contents or title of a popover. @@ -1057,38 +1052,44 @@ def update_popover( The new title of the popover. show Opens (`True`) or closes (`False`) the popover. - session - A Shiny session object (the default should almost always be used). + {session_param} See Also -------- * :func:`~shiny.ui.popover` """ - session = require_active_session(session) - if title is not None or len(args) > 0: - _session_on_flush_send_msg( - id, - session, - drop_none( + active_session = require_active_session(session) + + with session_context(active_session): + + if title is not None or len(args) > 0: + session_on_flush_send_msg( + id, + drop_none( + { + "method": "update", + "content": ( + active_session._process_ui(TagList(*args)) + if len(args) > 0 + else None + ), + "header": ( + active_session._process_ui(title) + if title is not None + else None + ), + }, + ), + ) + if show is not None: + session_on_flush_send_msg( + id, { - "method": "update", - "content": ( - session._process_ui(TagList(*args)) if len(args) > 0 else None - ), - "header": session._process_ui(title) if title is not None else None, + "method": "toggle", + "value": _normalize_show_value(show), }, - ), - ) - if show is not None: - _session_on_flush_send_msg( - id, - session, - { - "method": "toggle", - "value": _normalize_show_value(show), - }, - ) + ) @overload diff --git a/shiny/ui/_insert.py b/shiny/ui/_insert.py index 3397d2154..69dd19373 100644 --- a/shiny/ui/_insert.py +++ b/shiny/ui/_insert.py @@ -1,22 +1,24 @@ __all__ = ("insert_ui", "remove_ui") -from typing import Literal, Optional +from typing import Literal from htmltools import TagChild -from .._docstring import add_example +from .._deprecated import _session_param_docs as _session_param +from .._docstring import add_example, doc_format from ..session import require_active_session -from ..session._session import Session +from ..types import MISSING, MISSING_TYPE @add_example() +@doc_format(session_param=_session_param) def insert_ui( ui: TagChild, selector: str, where: Literal["beforeBegin", "afterBegin", "beforeEnd", "afterEnd"] = "beforeEnd", multiple: bool = False, immediate: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Insert UI objects. @@ -48,9 +50,7 @@ def insert_ui( Whether the UI object should be immediately inserted or removed, or whether Shiny should wait until all outputs have been updated and all effects have been run (default). - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Note ---- @@ -68,28 +68,29 @@ def insert_ui( * :class:`~shiny.render.ui` """ - session = require_active_session(session) + active_session = require_active_session(session) def callback() -> None: - session._send_insert_ui( + active_session._send_insert_ui( selector=selector, multiple=multiple, where=where, - content=session._process_ui(ui), + content=active_session._process_ui(ui), ) if immediate: callback() else: - session.on_flushed(callback, once=True) + active_session.on_flushed(callback, once=True) @add_example() +@doc_format(session_param=_session_param) def remove_ui( selector: str, multiple: bool = False, immediate: bool = False, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Remove UI objects. @@ -111,9 +112,7 @@ def remove_ui( Whether the UI object should be immediately inserted or removed, or whether Shiny should wait until all outputs have been updated and all effects have been run (default). - session - A :class:`~shiny.Session` instance. If not provided, it is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} See Also -------- @@ -121,12 +120,12 @@ def remove_ui( * :class:`~shiny.render.ui` """ - session = require_active_session(session) + active_session = require_active_session(session) def callback(): - session._send_remove_ui(selector=selector, multiple=multiple) + active_session._send_remove_ui(selector=selector, multiple=multiple) if immediate: callback() else: - session.on_flushed(callback, once=True) + active_session.on_flushed(callback, once=True) diff --git a/shiny/ui/_modal.py b/shiny/ui/_modal.py index cdfc76f76..ecfe3acdc 100644 --- a/shiny/ui/_modal.py +++ b/shiny/ui/_modal.py @@ -7,17 +7,15 @@ "modal_remove", ) -from typing import TYPE_CHECKING, Literal, Optional +from typing import Literal, Optional from htmltools import HTML, Tag, TagAttrs, TagAttrValue, TagChild, div, tags -from .._docstring import add_example +from .._deprecated import _session_param_docs as _session_param +from .._docstring import add_example, doc_format from ..session import require_active_session from ..types import MISSING, MISSING_TYPE -if TYPE_CHECKING: - from ..session import Session - @add_example(ex_dir="../api-examples/modal") def modal_button(label: TagChild, icon: TagChild = None, **kwargs: TagAttrValue) -> Tag: @@ -159,7 +157,11 @@ def modal( @add_example(ex_dir="../api-examples/modal") -def modal_show(modal: Tag, session: Optional[Session] = None) -> None: +@doc_format(session_param=_session_param) +def modal_show( + modal: Tag, + session: MISSING_TYPE = MISSING, +) -> None: """ Show a modal dialog. @@ -170,22 +172,21 @@ def modal_show(modal: Tag, session: Optional[Session] = None) -> None: ---------- modal Typically a :func:`~shiny.ui.modal` instance. - session - The :class:`~shiny.Session` instance to display the modal in. If not provided, - the session is inferred via :func:`~shiny.session.get_current_session`. + {session_param} See Also -------- * :func:`~shiny.ui.modal_remove` * :func:`~shiny.ui.modal` """ - session = require_active_session(session) - msg = session._process_ui(modal) - session._send_message_sync({"modal": {"type": "show", "message": msg}}) + active_session = require_active_session(session) + msg = active_session._process_ui(modal) + active_session._send_message_sync({"modal": {"type": "show", "message": msg}}) @add_example(ex_dir="../api-examples/modal") -def modal_remove(session: Optional[Session] = None) -> None: +@doc_format(session_param=_session_param) +def modal_remove(session: MISSING_TYPE = MISSING) -> None: """ Remove a modal dialog box. @@ -195,14 +196,12 @@ def modal_remove(session: Optional[Session] = None) -> None: Parameters ---------- - session - The :class:`~shiny.Session` instance that contains the modal to remove. If not - provided, the session is inferred via :func:`~shiny.session.get_current_session`. + {session_param} See Also -------- * :func:`~shiny.ui.modal_show` * :func:`~shiny.ui.modal` """ - session = require_active_session(session) - session._send_message_sync({"modal": {"type": "remove", "message": None}}) + active_session = require_active_session(session) + active_session._send_message_sync({"modal": {"type": "remove", "message": None}}) diff --git a/shiny/ui/_notification.py b/shiny/ui/_notification.py index e8ac05e47..169b61967 100644 --- a/shiny/ui/_notification.py +++ b/shiny/ui/_notification.py @@ -2,19 +2,19 @@ __all__ = ("notification_show", "notification_remove") -from typing import TYPE_CHECKING, Any, Literal, Optional +from typing import Any, Literal, Optional from htmltools import TagChild -from .._docstring import add_example, no_example +from .._deprecated import _session_param_docs as _session_param +from .._docstring import add_example, doc_format, no_example from .._utils import rand_hex from ..session import require_active_session - -if TYPE_CHECKING: - from ..session import Session +from ..types import MISSING, MISSING_TYPE @add_example() +@doc_format(session_param=_session_param) def notification_show( ui: TagChild, *, @@ -23,7 +23,7 @@ def notification_show( close_button: bool = True, id: Optional[str] = None, type: Literal["default", "message", "warning", "error"] = "default", - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> str: """ Show a notification to the user. @@ -54,9 +54,7 @@ def notification_show( type A string which controls the color of the notification. This should be one of "default" (gray), "message" (blue), "warning" (yellow), or "error" (red). - session - The :class:`~shiny.Session` in which the notification should appear. If not - provided, the session is inferred via :func:`~shiny.session.get_current_session`. + {session_param} Returns ------- @@ -69,10 +67,10 @@ def notification_show( * :func:`~shiny.ui.modal` """ - session = require_active_session(session) + active_session = require_active_session(session) - ui_ = session._process_ui(ui) - action_ = session._process_ui(action) + ui_ = active_session._process_ui(ui) + action_ = active_session._process_ui(action) id = id if id else rand_hex(8) @@ -88,13 +86,20 @@ def notification_show( if duration: payload.update({"duration": duration * 1000}) - session._send_message_sync({"notification": {"type": "show", "message": payload}}) + active_session._send_message_sync( + {"notification": {"type": "show", "message": payload}} + ) return id @no_example() -def notification_remove(id: str, *, session: Optional[Session] = None) -> str: +@doc_format(session_param=_session_param) +def notification_remove( + id: str, + *, + session: MISSING_TYPE = MISSING, +) -> str: """ Remove a notification. @@ -106,9 +111,7 @@ def notification_remove(id: str, *, session: Optional[Session] = None) -> str: ---------- id The ``id`` of the notification to remove. - session - The :class:`~shiny.Session` in which the notification appears. If not provided, the session is inferred via - :func:`~shiny.session.get_current_session`. + {session_param} Returns ------- @@ -124,6 +127,8 @@ def notification_remove(id: str, *, session: Optional[Session] = None) -> str: ------- See :func:`shiny.ui.notification_show`. """ - session = require_active_session(session) - session._send_message_sync({"notification": {"type": "remove", "message": id}}) + active_session = require_active_session(session) + active_session._send_message_sync( + {"notification": {"type": "remove", "message": id}} + ) return id diff --git a/shiny/ui/_progress.py b/shiny/ui/_progress.py index a302d8c6d..5a457a285 100644 --- a/shiny/ui/_progress.py +++ b/shiny/ui/_progress.py @@ -3,19 +3,19 @@ __all__ = ("Progress",) from types import TracebackType -from typing import TYPE_CHECKING, Optional, Type +from typing import Optional, Type from warnings import warn -from .._docstring import add_example +from .._deprecated import _session_param_docs as _session_param +from .._docstring import add_example, doc_format from .._utils import rand_hex from ..session import require_active_session from ..session._session import UpdateProgressMessage - -if TYPE_CHECKING: - from ..session import Session +from ..types import MISSING, MISSING_TYPE @add_example() +@doc_format(session_param=_session_param) class Progress: """ Initialize a progress bar. @@ -33,9 +33,7 @@ class Progress: max The value that represents the end of the progress bar. Must be greater than ``min``. - session - The :class:`~shiny.Session` instance that the progress bar should appear in. If not - provided, the session is inferred via :func:`~shiny.session.get_current_session`. + {session_param} """ _style = "notification" @@ -45,7 +43,10 @@ class Progress: value: float | None def __init__( - self, min: int = 0, max: int = 1, session: Optional[Session] = None + self, + min: int = 0, + max: int = 1, + session: MISSING_TYPE = MISSING, ) -> None: self.min = min self.max = max diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index 2ddd91085..940e17b61 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -2,7 +2,7 @@ import warnings from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Literal, Optional, cast +from typing import Literal, Optional, cast from htmltools import ( HTML, @@ -16,8 +16,9 @@ tags, ) +from .._deprecated import _session_param_docs as _session_param from .._deprecated import warn_deprecated -from .._docstring import add_example, no_example +from .._docstring import add_example, doc_format, no_example from .._namespaces import ResolvedId, resolve_id_or_none from .._typing_extensions import TypedDict from .._utils import private_random_id @@ -30,9 +31,6 @@ from .css import CssUnit, as_css_padding, as_css_unit from .fill import as_fill_item, as_fillable_container -if TYPE_CHECKING: - from ..session import Session - __all__ = ( "Sidebar", "SidebarOpen", @@ -760,11 +758,12 @@ def _get_layout_sidebar_sidebar( @add_example() +@doc_format(session_param=_session_param) def update_sidebar( id: str, *, show: Optional[bool] = None, - session: Optional[Session] = None, + session: MISSING_TYPE = MISSING, ) -> None: """ Update a sidebar's visibility. @@ -777,15 +776,14 @@ def update_sidebar( The `id` of the :func:`~shiny.ui.sidebar` to toggle. show The desired visible state of the sidebar, where `True` opens the sidebar and `False` closes the sidebar (if not already in that state). - session - A Shiny session object (the default should almost always be used). + {session_param} See Also -------- * :func:`~shiny.ui.sidebar` * :func:`~shiny.ui.layout_sidebar` """ - session = require_active_session(session) + active_session = require_active_session(session) # method: Literal["toggle", "open", "close"] # if open is None or open == "toggle": @@ -806,9 +804,9 @@ def update_sidebar( method = "open" if bool(show) else "close" def callback() -> None: - session.send_input_message(id, {"method": method}) + active_session.send_input_message(id, {"method": method}) - session.on_flush(callback, once=True) + active_session.on_flush(callback, once=True) def _collapse_icon() -> TagChild: diff --git a/shiny/ui/_utils.py b/shiny/ui/_utils.py index aa9d80a95..641e87c28 100644 --- a/shiny/ui/_utils.py +++ b/shiny/ui/_utils.py @@ -13,7 +13,7 @@ ) from .._typing_extensions import TypeGuard -from ..session import Session, require_active_session +from ..session import require_active_session from ..types import MISSING, MISSING_TYPE @@ -67,10 +67,11 @@ def _find_child_strings(x: TagList | TagNode) -> str: return "" -def _session_on_flush_send_msg( - id: str, session: Session | None, msg: dict[str, object] +def session_on_flush_send_msg( + id: str, + msg: dict[str, object], ) -> None: - session = require_active_session(session) + session = require_active_session() session.on_flush(lambda: session.send_input_message(id, msg), once=True) diff --git a/tests/playwright/examples/example_apps.py b/tests/playwright/examples/example_apps.py index 95118ee3d..99ac23769 100644 --- a/tests/playwright/examples/example_apps.py +++ b/tests/playwright/examples/example_apps.py @@ -48,6 +48,13 @@ def get_apps(path: str) -> typing.List[str]: "ShinyDeprecationWarning:", "shiny.render.transformer.output_transformer()", ] +session_warnings = [ + # shinywidgets.register_widget() uses `session` when registering widget + "ShinyDeprecationWarning: `session=` is deprecated", + "session_type_warning()", # continutation of line above + # Brownian + "`session=` is deprecated", +] express_warnings = ["Detected Shiny Express app. "] app_allow_shiny_errors: typing.Dict[ str, typing.Union[Literal[True], typing.List[str]] @@ -65,8 +72,8 @@ def get_apps(path: str) -> typing.List[str]: "api-examples/output_transformer": [*output_transformer_errors], "api-examples/render_express": [*express_warnings], "app-templates/multi-page": [*output_transformer_errors], - "examples/airmass": [*output_transformer_errors], - "examples/brownian": [*output_transformer_errors], + "examples/airmass": [*output_transformer_errors, *session_warnings], + "examples/brownian": [*output_transformer_errors, *session_warnings], "examples/model-score": [*output_transformer_errors], "deploys/plotly": [*output_transformer_errors], } @@ -93,6 +100,9 @@ def get_apps(path: str) -> typing.List[str]: "pd.option_context('mode.use_inf_as_na", # continutation of line above ] app_allow_js_errors: typing.Dict[str, typing.List[str]] = { + "examples/airmass": [ + "Failed to load resource: the server responded with a status of 404" + ], "examples/brownian": ["Failed to acquire camera feed:"], } diff --git a/tests/playwright/shiny/TODO/sidebar/app.py b/tests/playwright/shiny/TODO/sidebar/app.py index 21b18ce62..f260abb57 100644 --- a/tests/playwright/shiny/TODO/sidebar/app.py +++ b/tests/playwright/shiny/TODO/sidebar/app.py @@ -1,6 +1,6 @@ from data import adjectives, animals, dark_color, light_color -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fixed( ui.h1("Toggle Sidebars"), @@ -56,7 +56,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session) -> None: +def server(input: Inputs): @render.ui def ui_content(): return f"Hello, {input.adjective()} {input.animal()}!" diff --git a/tests/playwright/shiny/TODO/update_popover/app.py b/tests/playwright/shiny/TODO/update_popover/app.py index 639987639..449c90d3c 100644 --- a/tests/playwright/shiny/TODO/update_popover/app.py +++ b/tests/playwright/shiny/TODO/update_popover/app.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, req, ui +from shiny import App, Inputs, reactive, req, ui app_ui = ui.page_fluid( ui.input_action_button("btn_update", "Update popover phrase", class_="mt-3 me-3"), @@ -13,7 +13,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): # Immediately display popover diff --git a/tests/playwright/shiny/async/app.py b/tests/playwright/shiny/async/app.py index cafa7dbc8..ab0fae93b 100644 --- a/tests/playwright/shiny/async/app.py +++ b/tests/playwright/shiny/async/app.py @@ -2,7 +2,7 @@ import hashlib import time -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui def calc(value: str) -> str: @@ -22,7 +22,7 @@ def calc(value: str) -> str: ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @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 b9025647a..eee07918f 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 @@ -70,7 +70,7 @@ def reset_time(): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): slider_with_reset_server("one", value=end_time) slider_with_reset_server("two", value=(start_time, end_time)) slider_with_reset_server("three", value=[start_time, end_time]) diff --git a/tests/playwright/shiny/bugs/0676-row-selection/app.py b/tests/playwright/shiny/bugs/0676-row-selection/app.py index bf016cc46..727feaf78 100644 --- a/tests/playwright/shiny/bugs/0676-row-selection/app.py +++ b/tests/playwright/shiny/bugs/0676-row-selection/app.py @@ -2,7 +2,7 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui df = pd.DataFrame( dict( @@ -30,7 +30,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.data_frame def grid(): return render.DataGrid( diff --git a/tests/playwright/shiny/components/accordion/app.py b/tests/playwright/shiny/components/accordion/app.py index 5ee4d2b7a..0cc4e2b44 100644 --- a/tests/playwright/shiny/components/accordion/app.py +++ b/tests/playwright/shiny/components/accordion/app.py @@ -1,6 +1,6 @@ from __future__ import annotations -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui def make_panel(letter: str) -> ui.AccordionPanel: @@ -28,7 +28,7 @@ def make_panel(letter: str) -> ui.AccordionPanel: ) -def server(input: Inputs, output: Outputs, session: Session) -> None: +def server(input: Inputs) -> None: @reactive.calc def acc() -> list[str]: acc_val: list[str] | None = input.acc() diff --git a/tests/playwright/shiny/components/navset_hidden/app.py b/tests/playwright/shiny/components/navset_hidden/app.py index 35462dcc9..a72b59f8d 100644 --- a/tests/playwright/shiny/components/navset_hidden/app.py +++ b/tests/playwright/shiny/components/navset_hidden/app.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, reactive, ui app_ui = ui.page_fluid( ui.layout_sidebar( @@ -19,7 +19,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect @reactive.event(input.controller) def _(): diff --git a/tests/playwright/shiny/components/popover/app.py b/tests/playwright/shiny/components/popover/app.py index c7add2786..2243f2e01 100644 --- a/tests/playwright/shiny/components/popover/app.py +++ b/tests/playwright/shiny/components/popover/app.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, req, ui +from shiny import App, Inputs, reactive, req, ui app_ui = ui.page_fluid( ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"), @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): req(input.btn_show()) diff --git a/tests/playwright/shiny/components/tooltip/app.py b/tests/playwright/shiny/components/tooltip/app.py index 375d32eb4..621427ff9 100644 --- a/tests/playwright/shiny/components/tooltip/app.py +++ b/tests/playwright/shiny/components/tooltip/app.py @@ -1,4 +1,4 @@ -from shiny import App, Inputs, Outputs, Session, reactive, req, ui +from shiny import App, Inputs, reactive, req, ui app_ui = ui.page_fluid( ui.input_action_button("btn_show", "Show tooltip", class_="mt-3 me-3"), @@ -14,7 +14,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.effect def _(): req(input.btn_show()) diff --git a/tests/playwright/shiny/default-render-ui/app.py b/tests/playwright/shiny/default-render-ui/app.py index e597e1a67..87cee019c 100644 --- a/tests/playwright/shiny/default-render-ui/app.py +++ b/tests/playwright/shiny/default-render-ui/app.py @@ -1,9 +1,9 @@ -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid(ui.output_ui("dynamic_ui")) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.ui def dynamic_ui(): @render.text diff --git a/tests/playwright/shiny/deprecated/output_transformer/app.py b/tests/playwright/shiny/deprecated/output_transformer/app.py index 725d9f6f1..e927684e5 100644 --- a/tests/playwright/shiny/deprecated/output_transformer/app.py +++ b/tests/playwright/shiny/deprecated/output_transformer/app.py @@ -123,7 +123,7 @@ def server(input: Inputs, output: Outputs, session: Session): def no_output(): return input.caption() - @output + @output # performs type checking # Without parentheses @render_capitalize def no_parens(): diff --git a/tests/playwright/shiny/inputs/input_file/app.py b/tests/playwright/shiny/inputs/input_file/app.py index 369d7f90a..edec53c1d 100644 --- a/tests/playwright/shiny/inputs/input_file/app.py +++ b/tests/playwright/shiny/inputs/input_file/app.py @@ -2,7 +2,7 @@ import pandas as pd -from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +from shiny import App, Inputs, reactive, render, req, ui from shiny.types import FileInfo app_ui = ui.page_fluid( @@ -19,7 +19,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @reactive.calc def parsed_file(): file: typing.Union[typing.List["FileInfo"], None] = input.file1() 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 898124cf1..48532f462 100644 --- a/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py +++ b/tests/playwright/shiny/inputs/input_radio_checkbox_group/app.py @@ -1,6 +1,6 @@ from htmltools import HTML -from shiny import App, Inputs, Outputs, Session, render, ui +from shiny import App, Inputs, render, ui app_ui = ui.page_fluid( ui.row( @@ -64,7 +64,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def radio1_out(): return input.radio1() diff --git a/tests/playwright/shiny/module-conditional/app.py b/tests/playwright/shiny/module-conditional/app.py index ccb8da13e..b9bb04bca 100644 --- a/tests/playwright/shiny/module-conditional/app.py +++ b/tests/playwright/shiny/module-conditional/app.py @@ -28,7 +28,7 @@ def mod_server(input: Inputs, output: Outputs, session: Session): ... ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): mod_server("mod") diff --git a/tests/playwright/shiny/plot-sizing/app.py b/tests/playwright/shiny/plot-sizing/app.py index 646fe7fe4..2e90e63bf 100644 --- a/tests/playwright/shiny/plot-sizing/app.py +++ b/tests/playwright/shiny/plot-sizing/app.py @@ -93,7 +93,7 @@ def plot_native_size(): ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): def plot_with_mpl(fig_size: tuple[float, float] | None) -> object: fig, ax = plt.subplots(facecolor="lavender") X, Y = np.mgrid[-4:4, -4:4] diff --git a/tests/playwright/shiny/server/output_transformer/app.py b/tests/playwright/shiny/server/output_transformer/app.py index 8c988cc62..a3c15adee 100644 --- a/tests/playwright/shiny/server/output_transformer/app.py +++ b/tests/playwright/shiny/server/output_transformer/app.py @@ -2,7 +2,7 @@ from typing import Optional, overload -from shiny import App, Inputs, Outputs, Session, ui +from shiny import App, Inputs, ui from shiny.render.transformer import ( TransformerMetadata, ValueFn, @@ -67,7 +67,7 @@ def render_test_text( ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render_test_text def t1(): return "t1; no call" diff --git a/tests/playwright/shiny/server/reactive_event/app.py b/tests/playwright/shiny/server/reactive_event/app.py index 8fc83359f..3b9027f55 100644 --- a/tests/playwright/shiny/server/reactive_event/app.py +++ b/tests/playwright/shiny/server/reactive_event/app.py @@ -1,6 +1,6 @@ from __future__ import annotations -from shiny import App, Inputs, Outputs, Session, reactive, render, ui +from shiny import App, Inputs, reactive, render, ui app_ui = ui.page_fluid( ui.h2(ui.code("@reactive.event")), @@ -17,7 +17,7 @@ ) -def server(input: Inputs, output: Outputs, session: Session): +def server(input: Inputs): @render.text def txt_immediate(): return input.btn_count()