From 15be8adfb87df1a0f49de849a88631cc91ee6759 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 22 Apr 2025 18:02:41 -0500 Subject: [PATCH 01/13] feat(session): Add a current_output_id attribute for identifying currently executing output --- shiny/render/_express.py | 2 +- shiny/render/renderer/_renderer.py | 18 ++++++++++++++++-- shiny/session/_session.py | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/shiny/render/_express.py b/shiny/render/_express.py index ab5139758..7c414929c 100644 --- a/shiny/render/_express.py +++ b/shiny/render/_express.py @@ -67,7 +67,7 @@ def __call__(self, fn: ValueFn[None]) -> Self: if fn is None: # pyright: ignore[reportUnnecessaryComparison] raise TypeError("@render.express requires a function when called") - async_fn = AsyncValueFn(fn) + async_fn = AsyncValueFn(fn, self) if async_fn.is_async(): raise TypeError( "@render.express does not support async functions. Use @render.ui instead." diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index b79009c91..30716b2da 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -1,5 +1,6 @@ from __future__ import annotations +from contextlib import contextmanager from typing import ( TYPE_CHECKING, Any, @@ -162,7 +163,7 @@ def __call__(self, _fn: ValueFn[IT]) -> Self: raise TypeError("Value function must be callable") # Set value function with extra meta information - self.fn = AsyncValueFn(_fn) + self.fn = AsyncValueFn(_fn, self) # Copy over function name as it is consistent with how Session and Output # retrieve function names @@ -350,6 +351,7 @@ class AsyncValueFn(Generic[IT]): def __init__( self, fn: Callable[[], IT | None] | Callable[[], Awaitable[IT | None]], + renderer: Renderer[Any], ): if isinstance(fn, AsyncValueFn): raise TypeError( @@ -358,12 +360,14 @@ def __init__( self._is_async = is_async_callable(fn) self._fn = wrap_async(fn) self._orig_fn = fn + self._renderer = renderer async def __call__(self) -> IT | None: """ Call the asynchronous function. """ - return await self._fn() + with self._current_output_id(): + return await self._fn() def is_async(self) -> bool: """ @@ -404,3 +408,13 @@ def get_sync_fn(self) -> Callable[[], IT | None]: ) sync_fn = cast(Callable[[], IT], self._orig_fn) return sync_fn + + @contextmanager + def _current_output_id(self): + from ...session import get_current_session + + session = get_current_session() + if session is not None: + session.current_output_id = self._renderer.output_id + yield + session.current_output_id = None diff --git a/shiny/session/_session.py b/shiny/session/_session.py index dd78a6819..672b4455c 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,9 +50,8 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect +from ..reactive import Effect_, Value, effect, isolate from ..reactive import flush as reactive_flush -from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT @@ -180,6 +179,7 @@ class Session(ABC): input: Inputs output: Outputs clientdata: ClientData + current_output_id: str | None # Could be done with a weak ref dict from root to all children. Then we could just # iterate over all modules and check the `.bookmark_exclude` list of each proxy From abaebbb77c91af7e6b42f6c0d3ab8302c613489d Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 22 Apr 2025 18:03:36 -0500 Subject: [PATCH 02/13] feat(session.clientdata): allow output_*() methods to be called without an id inside an output renderer --- shiny/session/_session.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 672b4455c..fa0512072 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -180,6 +180,7 @@ class Session(ABC): output: Outputs clientdata: ClientData current_output_id: str | None + "ID for the currently rendering output." # Could be done with a weak ref dict from root to all children. Then we could just # iterate over all modules and check the `.bookmark_exclude` list of each proxy @@ -1556,7 +1557,7 @@ def pixelratio(self) -> float: """ return cast(int, self._read_input("pixelratio")) - def output_height(self, id: str) -> float | None: + def output_height(self, id: Optional[str] = None) -> float | None: """ Reactively read the height of an output. @@ -1573,7 +1574,7 @@ def output_height(self, id: str) -> float | None: """ return cast(float, self._read_output(id, "height")) - def output_width(self, id: str) -> float | None: + def output_width(self, id: Optional[str] = None) -> float | None: """ Reactively read the width of an output. @@ -1590,7 +1591,7 @@ def output_width(self, id: str) -> float | None: """ return cast(float, self._read_output(id, "width")) - def output_hidden(self, id: str) -> bool | None: + def output_hidden(self, id: Optional[str] = None) -> bool | None: """ Reactively read whether an output is hidden. @@ -1606,7 +1607,7 @@ def output_hidden(self, id: str) -> bool | None: """ return cast(bool, self._read_output(id, "hidden")) - def output_bg_color(self, id: str) -> str | None: + def output_bg_color(self, id: Optional[str] = None) -> str | None: """ Reactively read the background color of an output. @@ -1623,7 +1624,7 @@ def output_bg_color(self, id: str) -> str | None: """ return cast(str, self._read_output(id, "bg")) - def output_fg_color(self, id: str) -> str | None: + def output_fg_color(self, id: Optional[str] = None) -> str | None: """ Reactively read the foreground color of an output. @@ -1640,7 +1641,7 @@ def output_fg_color(self, id: str) -> str | None: """ return cast(str, self._read_output(id, "fg")) - def output_accent_color(self, id: str) -> str | None: + def output_accent_color(self, id: Optional[str] = None) -> str | None: """ Reactively read the accent color of an output. @@ -1657,7 +1658,7 @@ def output_accent_color(self, id: str) -> str | None: """ return cast(str, self._read_output(id, "accent")) - def output_font(self, id: str) -> str | None: + def output_font(self, id: Optional[str] = None) -> str | None: """ Reactively read the font(s) of an output. @@ -1685,9 +1686,18 @@ def _read_input(self, key: str) -> str: return self._session.input[id]() - def _read_output(self, id: str, key: str) -> str | None: + def _read_output(self, id: str | None, key: str) -> str | None: self._check_current_context(f"output_{key}") + if id is None: + id = self._session.current_output_id + + if id is None: + raise ValueError( + "session.clientdata.output_*() must be either be supplied with an id " + "or called from within an output renderer." + ) + input_id = ResolvedId(f".clientdata_output_{id}_{key}") if input_id in self._session.input: return self._session.input[input_id]() From e97b0f05dde5e8190a57363b1fd4b2984eba2c00 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 22 Apr 2025 18:20:54 -0500 Subject: [PATCH 03/13] Update changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25256d205..957141c86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [UNRELEASED] +### New features + +* The `Session` class gains a `current_output_id` attribute. If an output renderer is currently executing, this attribute will contain the relevant ID. (#1978) +* The `.output_*()` methods of the `ClientData` class (e.g., `session.clientdata.output_height()`) can now be called without an `id` inside a output renderer. (#1978) + ### Improvements * Improved the styling and readability of markdown tables rendered by `ui.Chat()` and `ui.MarkdownStream()`. (#1973) ## [1.4.0] - 2025-04-08 -## New features +### New features * Added support for bookmarking Shiny applications. Bookmarking allows users to save the current state of an application and return to it later. This feature is available in both Shiny Core and Shiny Express. (#1870, #1915, #1919, #1920, #1922, #1934, #1938, #1945, #1955) * To enable bookmarking in Express mode, set `shiny.express.app_opts(bookmark_store=)` during the app's initial construction. From cb9fdde0c3a03b02455ffe5dd1931fe9debb3357 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 09:36:10 -0500 Subject: [PATCH 04/13] Appease import lint --- shiny/session/_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shiny/session/_session.py b/shiny/session/_session.py index fa0512072..7846de0c5 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,8 +50,9 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect, isolate +from ..reactive import Effect_, Value, effect from ..reactive import flush as reactive_flush +from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT From 7f3e958d0f35681b594672ccab620279b0c8010c Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 09:40:56 -0500 Subject: [PATCH 05/13] Make sure context manager always yields --- shiny/render/renderer/_renderer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index 30716b2da..a4e1f5915 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -414,7 +414,9 @@ def _current_output_id(self): from ...session import get_current_session session = get_current_session() - if session is not None: + if session is None: + yield + else: session.current_output_id = self._renderer.output_id yield session.current_output_id = None From 0908a4e0046b593aa2e1c9a0bfd7a4e08ea24022 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 17:44:37 -0500 Subject: [PATCH 06/13] Make it a method instead of attribute. More careful cleanup --- shiny/render/renderer/_renderer.py | 10 +++++++--- shiny/session/_session.py | 14 +++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index a4e1f5915..385685f7a 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -416,7 +416,11 @@ def _current_output_id(self): session = get_current_session() if session is None: yield - else: - session.current_output_id = self._renderer.output_id + return + + old_id = session._current_output_id + try: + session._current_output_id = self._renderer.output_id yield - session.current_output_id = None + finally: + session._current_output_id = old_id diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 7846de0c5..5f5fd4a7d 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,9 +50,8 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect +from ..reactive import Effect_, Value, effect, isolate from ..reactive import flush as reactive_flush -from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT @@ -180,8 +179,6 @@ class Session(ABC): input: Inputs output: Outputs clientdata: ClientData - current_output_id: str | None - "ID for the currently rendering output." # Could be done with a weak ref dict from root to all children. Then we could just # iterate over all modules and check the `.bookmark_exclude` list of each proxy @@ -190,6 +187,9 @@ class Session(ABC): user: str | None groups: list[str] | None + # Internal state for current_output_id() + _current_output_id: str | None + # TODO: not sure these should be directly exposed _outbound_message_queues: OutBoundMessageQueues _downloads: dict[str, DownloadInfo] @@ -380,6 +380,10 @@ def on_flushed( A function that can be used to cancel the registration. """ + def current_output_id(self) -> str | None: + "Returns the id of the currently executing output renderer (if any)." + return self._current_output_id + @abstractmethod async def _unhandled_error(self, e: Exception) -> None: ... @@ -1691,7 +1695,7 @@ def _read_output(self, id: str | None, key: str) -> str | None: self._check_current_context(f"output_{key}") if id is None: - id = self._session.current_output_id + id = self._session.current_output_id() if id is None: raise ValueError( From 0088ff42553e4114b4459d9d64737ad3b4200560 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 17:50:54 -0500 Subject: [PATCH 07/13] Tweak wording --- CHANGELOG.md | 2 +- shiny/session/_session.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 957141c86..7c8ded0fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features -* The `Session` class gains a `current_output_id` attribute. If an output renderer is currently executing, this attribute will contain the relevant ID. (#1978) * The `.output_*()` methods of the `ClientData` class (e.g., `session.clientdata.output_height()`) can now be called without an `id` inside a output renderer. (#1978) +* The `Session` class gains a `.current_output_id()` method. It returns the ID of the currently executing output renderer (if any). (#1978) ### Improvements diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 5f5fd4a7d..f226e6f25 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -1699,8 +1699,8 @@ def _read_output(self, id: str | None, key: str) -> str | None: if id is None: raise ValueError( - "session.clientdata.output_*() must be either be supplied with an id " - "or called from within an output renderer." + "session.clientdata.output_*() requires an id when not called within " + "an output renderer." ) input_id = ResolvedId(f".clientdata_output_{id}_{key}") From ce79a5f299abfd7b0512f1fa53da1ef7813d29a5 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 18:13:54 -0500 Subject: [PATCH 08/13] Bugfix and add test --- shiny/session/_session.py | 2 +- .../shiny/session/current_output_info/app.py | 30 +++++++++++++++++++ .../test_current_output_info.py | 29 ++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/playwright/shiny/session/current_output_info/app.py create mode 100644 tests/playwright/shiny/session/current_output_info/test_current_output_info.py diff --git a/shiny/session/_session.py b/shiny/session/_session.py index f226e6f25..33a1109d9 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -188,7 +188,7 @@ class Session(ABC): groups: list[str] | None # Internal state for current_output_id() - _current_output_id: str | None + _current_output_id: str | None = None # TODO: not sure these should be directly exposed _outbound_message_queues: OutBoundMessageQueues diff --git a/tests/playwright/shiny/session/current_output_info/app.py b/tests/playwright/shiny/session/current_output_info/app.py new file mode 100644 index 000000000..e72d6cc55 --- /dev/null +++ b/tests/playwright/shiny/session/current_output_info/app.py @@ -0,0 +1,30 @@ +from shiny import App, Inputs, Outputs, Session, render, ui + +app_ui = ui.page_fluid( + ui.input_dark_mode(mode="light", id="dark_mode"), + ui.output_text("text1"), + ui.output_text("text2"), + ui.output_text("info").add_class("shiny-report-theme"), +) + + +def server(input: Inputs, output: Outputs, session: Session): + + @render.text + def text1(): + id = session.current_output_id() or "None" + return f"Output ID: {id}" + + @output(id="text2") + @render.text + def _(): + id = session.current_output_id() or "None" + return f"Output ID: {id}" + + @render.text + def info(): + bg_color = session.clientdata.output_bg_color() + return f"BG color: {bg_color}" + + +app = App(app_ui, server) diff --git a/tests/playwright/shiny/session/current_output_info/test_current_output_info.py b/tests/playwright/shiny/session/current_output_info/test_current_output_info.py new file mode 100644 index 000000000..d1fffa0d4 --- /dev/null +++ b/tests/playwright/shiny/session/current_output_info/test_current_output_info.py @@ -0,0 +1,29 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_current_output_info(page: Page, local_app: ShinyAppProc) -> None: + + page.goto(local_app.url) + + # Check that the output ID is displayed correctly in the UI + text1 = controller.OutputText(page, "text1") + text2 = controller.OutputText(page, "text2") + + text1.expect_value("Output ID: text1") + text2.expect_value("Output ID: text2") + + # Check that we can get background color from clientdata + info = controller.OutputText(page, "info") + info.expect_value("BG color: rgb(255, 255, 255)") + + # Click the dark mode button to change the background color + dark_mode = controller.InputDarkMode(page, "dark_mode") + dark_mode.expect_mode("light") + dark_mode.click() + dark_mode.expect_mode("dark") + + # Check that the background color has changed + info.expect_value("BG color: rgb(29, 31, 33)") From 11e8a7092b024c4a8ef64f9ee9b938ed929fa9a5 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 23 Apr 2025 18:16:11 -0500 Subject: [PATCH 09/13] Import lint --- shiny/session/_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 33a1109d9..3e6b3b269 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,8 +50,9 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect, isolate +from ..reactive import Effect_, Value, effect from ..reactive import flush as reactive_flush +from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT From 1a9ed7f4c81db170573bf6360843c94affbb962d Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 1 May 2025 13:02:41 -0500 Subject: [PATCH 10/13] Address feedback --- CHANGELOG.md | 1 - shiny/render/renderer/_renderer.py | 10 +++++----- shiny/session/_session.py | 12 +++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8ded0fb..6095738d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features * The `.output_*()` methods of the `ClientData` class (e.g., `session.clientdata.output_height()`) can now be called without an `id` inside a output renderer. (#1978) -* The `Session` class gains a `.current_output_id()` method. It returns the ID of the currently executing output renderer (if any). (#1978) ### Improvements diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index 385685f7a..76f65ce84 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -366,7 +366,7 @@ async def __call__(self) -> IT | None: """ Call the asynchronous function. """ - with self._current_output_id(): + with self._current_renderer(): return await self._fn() def is_async(self) -> bool: @@ -410,7 +410,7 @@ def get_sync_fn(self) -> Callable[[], IT | None]: return sync_fn @contextmanager - def _current_output_id(self): + def _current_renderer(self): from ...session import get_current_session session = get_current_session() @@ -418,9 +418,9 @@ def _current_output_id(self): yield return - old_id = session._current_output_id + old_renderer = session._current_renderer try: - session._current_output_id = self._renderer.output_id + session._current_renderer = self._renderer yield finally: - session._current_output_id = old_id + session._current_renderer = old_renderer diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 3e6b3b269..e8dad2471 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,9 +50,8 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect +from ..reactive import Effect_, Value, effect, isolate from ..reactive import flush as reactive_flush -from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT @@ -189,7 +188,7 @@ class Session(ABC): groups: list[str] | None # Internal state for current_output_id() - _current_output_id: str | None = None + _current_renderer: Renderer[Any] | None = None # TODO: not sure these should be directly exposed _outbound_message_queues: OutBoundMessageQueues @@ -381,9 +380,12 @@ def on_flushed( A function that can be used to cancel the registration. """ - def current_output_id(self) -> str | None: + def _current_output_id(self) -> str | None: "Returns the id of the currently executing output renderer (if any)." - return self._current_output_id + if self._current_renderer is None: + return None + else: + return self._current_renderer.output_id @abstractmethod async def _unhandled_error(self, e: Exception) -> None: ... From 6b07a8e350c2b0e6180f22b78e3a1ddbd3d2a934 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 1 May 2025 13:07:22 -0500 Subject: [PATCH 11/13] Update tests --- .../shiny/session/current_output_info/app.py | 11 ----------- .../current_output_info/test_current_output_info.py | 7 ------- 2 files changed, 18 deletions(-) diff --git a/tests/playwright/shiny/session/current_output_info/app.py b/tests/playwright/shiny/session/current_output_info/app.py index e72d6cc55..58c7b338b 100644 --- a/tests/playwright/shiny/session/current_output_info/app.py +++ b/tests/playwright/shiny/session/current_output_info/app.py @@ -10,17 +10,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @render.text - def text1(): - id = session.current_output_id() or "None" - return f"Output ID: {id}" - - @output(id="text2") - @render.text - def _(): - id = session.current_output_id() or "None" - return f"Output ID: {id}" - @render.text def info(): bg_color = session.clientdata.output_bg_color() diff --git a/tests/playwright/shiny/session/current_output_info/test_current_output_info.py b/tests/playwright/shiny/session/current_output_info/test_current_output_info.py index d1fffa0d4..6d015b510 100644 --- a/tests/playwright/shiny/session/current_output_info/test_current_output_info.py +++ b/tests/playwright/shiny/session/current_output_info/test_current_output_info.py @@ -8,13 +8,6 @@ def test_current_output_info(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url) - # Check that the output ID is displayed correctly in the UI - text1 = controller.OutputText(page, "text1") - text2 = controller.OutputText(page, "text2") - - text1.expect_value("Output ID: text1") - text2.expect_value("Output ID: text2") - # Check that we can get background color from clientdata info = controller.OutputText(page, "info") info.expect_value("BG color: rgb(255, 255, 255)") From 58666000b1993a0b6114716bafaeeb112112eb68 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 1 May 2025 13:10:20 -0500 Subject: [PATCH 12/13] Run isort --- shiny/session/_session.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shiny/session/_session.py b/shiny/session/_session.py index e8dad2471..0dc149759 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -50,8 +50,9 @@ from ..http_staticfiles import FileResponse from ..input_handler import input_handlers from ..module import ResolvedId -from ..reactive import Effect_, Value, effect, isolate +from ..reactive import Effect_, Value, effect from ..reactive import flush as reactive_flush +from ..reactive import isolate from ..reactive._core import lock from ..reactive._core import on_flushed as reactive_on_flushed from ..render.renderer import Renderer, RendererT From 4de9a7f6215c9383c81cfde7f5d662067c91e56c Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 1 May 2025 13:17:53 -0500 Subject: [PATCH 13/13] Fix --- shiny/session/_session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 0dc149759..5aa551424 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -1699,7 +1699,7 @@ def _read_output(self, id: str | None, key: str) -> str | None: self._check_current_context(f"output_{key}") if id is None: - id = self._session.current_output_id() + id = self._session._current_output_id() if id is None: raise ValueError(