Skip to content

Commit 15be8ad

Browse files
committed
feat(session): Add a current_output_id attribute for identifying currently executing output
1 parent 4d7a3f5 commit 15be8ad

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

shiny/render/_express.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def __call__(self, fn: ValueFn[None]) -> Self:
6767
if fn is None: # pyright: ignore[reportUnnecessaryComparison]
6868
raise TypeError("@render.express requires a function when called")
6969

70-
async_fn = AsyncValueFn(fn)
70+
async_fn = AsyncValueFn(fn, self)
7171
if async_fn.is_async():
7272
raise TypeError(
7373
"@render.express does not support async functions. Use @render.ui instead."

shiny/render/renderer/_renderer.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from contextlib import contextmanager
34
from typing import (
45
TYPE_CHECKING,
56
Any,
@@ -162,7 +163,7 @@ def __call__(self, _fn: ValueFn[IT]) -> Self:
162163
raise TypeError("Value function must be callable")
163164

164165
# Set value function with extra meta information
165-
self.fn = AsyncValueFn(_fn)
166+
self.fn = AsyncValueFn(_fn, self)
166167

167168
# Copy over function name as it is consistent with how Session and Output
168169
# retrieve function names
@@ -350,6 +351,7 @@ class AsyncValueFn(Generic[IT]):
350351
def __init__(
351352
self,
352353
fn: Callable[[], IT | None] | Callable[[], Awaitable[IT | None]],
354+
renderer: Renderer[Any],
353355
):
354356
if isinstance(fn, AsyncValueFn):
355357
raise TypeError(
@@ -358,12 +360,14 @@ def __init__(
358360
self._is_async = is_async_callable(fn)
359361
self._fn = wrap_async(fn)
360362
self._orig_fn = fn
363+
self._renderer = renderer
361364

362365
async def __call__(self) -> IT | None:
363366
"""
364367
Call the asynchronous function.
365368
"""
366-
return await self._fn()
369+
with self._current_output_id():
370+
return await self._fn()
367371

368372
def is_async(self) -> bool:
369373
"""
@@ -404,3 +408,13 @@ def get_sync_fn(self) -> Callable[[], IT | None]:
404408
)
405409
sync_fn = cast(Callable[[], IT], self._orig_fn)
406410
return sync_fn
411+
412+
@contextmanager
413+
def _current_output_id(self):
414+
from ...session import get_current_session
415+
416+
session = get_current_session()
417+
if session is not None:
418+
session.current_output_id = self._renderer.output_id
419+
yield
420+
session.current_output_id = None

shiny/session/_session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@
5050
from ..http_staticfiles import FileResponse
5151
from ..input_handler import input_handlers
5252
from ..module import ResolvedId
53-
from ..reactive import Effect_, Value, effect
53+
from ..reactive import Effect_, Value, effect, isolate
5454
from ..reactive import flush as reactive_flush
55-
from ..reactive import isolate
5655
from ..reactive._core import lock
5756
from ..reactive._core import on_flushed as reactive_on_flushed
5857
from ..render.renderer import Renderer, RendererT
@@ -180,6 +179,7 @@ class Session(ABC):
180179
input: Inputs
181180
output: Outputs
182181
clientdata: ClientData
182+
current_output_id: str | None
183183

184184
# Could be done with a weak ref dict from root to all children. Then we could just
185185
# iterate over all modules and check the `.bookmark_exclude` list of each proxy

0 commit comments

Comments
 (0)