Skip to content

Commit

Permalink
api!: Renderer.auto_output_ui() drops id arg. Make `RendererBase.…
Browse files Browse the repository at this point in the history
…output_id` a non-namespaced value. (#1030)
  • Loading branch information
schloerke authored Jan 18, 2024
1 parent 158016c commit 1c360a9
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 68 deletions.
2 changes: 1 addition & 1 deletion shiny/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""A package for building reactive web applications."""

__version__ = "0.6.1.9004"
__version__ = "0.6.1.9005"

from ._shinyenv import is_pyodide as _is_pyodide

Expand Down
8 changes: 4 additions & 4 deletions shiny/api-examples/Renderer/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class render_capitalize(Renderer[str]):
Whether to render a placeholder value. (Defaults to `True`)
"""

def auto_output_ui(self, id: str):
def auto_output_ui(self):
"""
Express UI for the renderer
"""
return ui.output_text_verbatim(id, placeholder=self.placeholder)
return ui.output_text_verbatim(self.output_name, placeholder=self.placeholder)

def __init__(
self,
Expand Down Expand Up @@ -94,11 +94,11 @@ class render_upper(Renderer[str]):
Note: This renderer is equivalent to `render_capitalize(to="upper")`.
"""

def auto_output_ui(self, id: str):
def auto_output_ui(self):
"""
Express UI for the renderer
"""
return ui.output_text_verbatim(id, placeholder=True)
return ui.output_text_verbatim(self.output_name, placeholder=True)

async def transform(self, value: str) -> str:
"""
Expand Down
2 changes: 0 additions & 2 deletions shiny/express/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ def suspend_display_ctxmgr() -> Generator[None, None, None]:


def null_ui(
id: str,
*args: object,
**kwargs: object,
) -> ui.TagList:
return ui.TagList()
Expand Down
4 changes: 2 additions & 2 deletions shiny/render/_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ class data_frame(Renderer[DataFrameResult]):
objects you can return from the rendering function to specify options.
"""

def auto_output_ui(self, id: str) -> Tag:
return ui.output_data_frame(id=id)
def auto_output_ui(self) -> Tag:
return ui.output_data_frame(id=self.output_id)

async def transform(self, value: DataFrameResult) -> Jsonifiable:
if not isinstance(value, AbstractTabularData):
Expand Down
3 changes: 1 addition & 2 deletions shiny/render/_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
class display(Renderer[None]):
def auto_output_ui(
self,
id: str,
*,
inline: bool | MISSING_TYPE = MISSING,
container: TagFunction | MISSING_TYPE = MISSING,
Expand All @@ -35,7 +34,7 @@ def auto_output_ui(
set_kwargs_value(kwargs, "fillable", fillable, self.fillable)

return _ui.output_ui(
id,
self.output_id,
# (possibly) contains `inline`, `container`, `fill`, and `fillable` keys!
**kwargs, # pyright: ignore[reportGeneralTypeIssues]
)
Expand Down
25 changes: 11 additions & 14 deletions shiny/render/_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,13 @@ class text(Renderer[str]):

def auto_output_ui(
self,
id: str,
*,
inline: bool | MISSING_TYPE = MISSING,
) -> Tag:
kwargs: dict[str, Any] = {}
set_kwargs_value(kwargs, "inline", inline, self.inline)

return _ui.output_text(id, **kwargs)
return _ui.output_text(self.output_id, **kwargs)

def __init__(
self,
Expand Down Expand Up @@ -155,13 +154,12 @@ class code(Renderer[str]):

def auto_output_ui(
self,
id: str,
*,
placeholder: bool | MISSING_TYPE = MISSING,
) -> Tag:
kwargs: dict[str, bool] = {}
set_kwargs_value(kwargs, "placeholder", placeholder, self.placeholder)
return _ui.output_code(id, **kwargs)
return _ui.output_code(self.output_id, **kwargs)

def __init__(
self,
Expand Down Expand Up @@ -243,7 +241,6 @@ class plot(Renderer[object]):

def auto_output_ui(
self,
id: str,
*,
width: str | float | int | MISSING_TYPE = MISSING,
height: str | float | int | MISSING_TYPE = MISSING,
Expand All @@ -253,7 +250,7 @@ def auto_output_ui(
set_kwargs_value(kwargs, "width", width, self.width)
set_kwargs_value(kwargs, "height", height, self.height)
return _ui.output_plot(
id,
self.output_id,
# (possibly) contains `width` and `height` keys!
**kwargs, # pyright: ignore[reportGeneralTypeIssues]
)
Expand Down Expand Up @@ -413,9 +410,9 @@ class image(Renderer[ImgData]):
* ~shiny.render.plot
"""

def auto_output_ui(self, id: str, **kwargs: object):
def auto_output_ui(self, **kwargs: object):
return _ui.output_image(
id,
self.output_id,
**kwargs, # pyright: ignore[reportGeneralTypeIssues]
)
# TODO: Make width/height handling consistent with render_plot
Expand Down Expand Up @@ -506,8 +503,8 @@ class table(Renderer[TableResult]):
* ~shiny.ui.output_table for the corresponding UI component to this render function.
"""

def auto_output_ui(self, id: str, **kwargs: TagAttrValue) -> Tag:
return _ui.output_table(id, **kwargs)
def auto_output_ui(self, **kwargs: TagAttrValue) -> Tag:
return _ui.output_table(self.output_id, **kwargs)
# TODO: Deal with kwargs

def __init__(
Expand Down Expand Up @@ -585,8 +582,8 @@ class ui(Renderer[TagChild]):
* ~shiny.ui.output_ui
"""

def auto_output_ui(self, id: str) -> Tag:
return _ui.output_ui(id)
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)
Expand Down Expand Up @@ -623,9 +620,9 @@ class download(Renderer[str]):
* ~shiny.ui.download_button
"""

def auto_output_ui(self, id: str) -> Tag:
def auto_output_ui(self) -> Tag:
return _ui.download_button(
id,
self.output_id,
label=self.label,
)

Expand Down
45 changes: 26 additions & 19 deletions shiny/render/renderer/_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"Jsonifiable",
"ValueFn",
"AsyncValueFn",
"RendererBaseT",
)

RendererBaseT = TypeVar("RendererBaseT", bound="RendererBase")
Expand Down Expand Up @@ -106,42 +107,46 @@ class RendererBase(ABC):
# A: No. Even if we had a `P` in the Generic, the calling decorator would not have access to it.
# Idea: Possibly use a chained method of `.ui_kwargs()`? https://github.com/posit-dev/py-shiny/issues/971
_auto_output_ui_kwargs: dict[str, Any] = dict()
# _auto_output_ui_args: tuple[Any, ...] = tuple()

__name__: str
"""
Name of output function supplied. (The value will not contain any module prefix.)
Set within `.__call__()` method.
Set within `Renderer.__call__()` method.
"""

# Meta
output_id: str
"""
Output function name or ID (provided to `@output(id=)`). This value will contain any module prefix.
Output function name or ID (provided to `@output(id=)`).
Set when the output is registered with the session.
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)`.
An initial value of `.__name__` (set within `Renderer.__call__(_fn)`) will be used until the
output renderer is registered within the session.
"""

def _set_output_metadata(
self,
*,
output_name: str,
output_id: str,
) -> None:
"""
Method to be called within `@output` to set the renderer's metadata.
Parameters
----------
output_name : str
Output function name or ID (provided to `@output(id=)`). This value will contain any module prefix.
output_id : str
Output function name or ID (provided to `@output(id=)`). This value **will
not** contain a module prefix (or session name-spacing).
"""
self.output_id = output_name
self.output_id = output_id

def auto_output_ui(
self,
id: str,
# *args: object,
# *
# **kwargs: object,
) -> DefaultUIFnResultOrNone:
return None
Expand Down Expand Up @@ -174,7 +179,6 @@ def tagify(self) -> DefaultUIFnResult:

def _render_auto_output_ui(self) -> DefaultUIFnResultOrNone:
return self.auto_output_ui(
self.__name__,
# Pass the `@output_args(foo="bar")` kwargs through to the auto_output_ui function.
**self._auto_output_ui_kwargs,
)
Expand Down Expand Up @@ -206,11 +210,8 @@ def _auto_register(self) -> None:

s = get_current_session()
if s is not None:
from ._renderer import RendererBase

# Cast to avoid circular import as this mixin is ONLY used within RendererBase
renderer_self = cast(RendererBase, self)
s.output(renderer_self)
# Register output on reactive graph
s.output(self)
# We mark the fact that we're auto-registered so that, if an explicit
# registration now occurs, we can undo this auto-registration.
self._auto_registered = True
Expand Down Expand Up @@ -309,16 +310,22 @@ def __call__(self, _fn: ValueFn[IT]) -> Self:
if not callable(_fn):
raise TypeError("Value function must be callable")

# Set value function with extra meta information
self.fn = AsyncValueFn(_fn)

# Copy over function name as it is consistent with how Session and Output
# retrieve function names
self.__name__: str = _fn.__name__
self.__name__ = _fn.__name__

# Set value function with extra meta information
self.fn: AsyncValueFn[IT] = AsyncValueFn(_fn)
# Set the value of `output_id` to the function name.
# This helps with testing and other situations where no session is present
# for auto-registration to occur.
self.output_id = self.__name__

# Allow for App authors to not require `@output`
self._auto_register()

# Return self for possible chaining of methods!
return self

def __init__(
Expand Down
11 changes: 5 additions & 6 deletions shiny/render/transformer/_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,6 @@ def __init__(
"`shiny.render.transformer.output_transformer()` and `shiny.render.transformer.OutputRenderer()` output render function utilities have been superseded by `shiny.render.renderer.Renderer` and will be removed in the near future."
)

# Copy over function name as it is consistent with how Session and Output
# retrieve function names
self.__name__ = value_fn.__name__

if not is_async_callable(transform_fn):
raise TypeError(
self.__class__.__name__
Expand All @@ -278,7 +274,11 @@ def __init__(

self._value_fn = value_fn
self._value_fn_is_async = is_async_callable(value_fn) # legacy key
# Copy over function name as it is consistent with how Session and Output
# retrieve function names
self.__name__ = value_fn.__name__
# Initial value for output_id until it is set by the Session
self.output_id = value_fn.__name__

self._transformer = transform_fn
self._params = params
Expand Down Expand Up @@ -333,7 +333,6 @@ async def _run(self) -> OT:

def auto_output_ui(
self,
id: str,
**kwargs: object,
) -> DefaultUIFnResultOrNone:
if self._default_ui is None:
Expand All @@ -348,7 +347,7 @@ def auto_output_ui(
}
)

return self._default_ui(id, *self._default_ui_args, **kwargs)
return self._default_ui(self.output_id, *self._default_ui_args, **kwargs)

async def render(self) -> Jsonifiable:
ret = await self._run()
Expand Down
5 changes: 3 additions & 2 deletions shiny/session/_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,10 +1002,11 @@ def set_renderer(renderer: RendererBaseT) -> RendererBaseT:
)

# Get the (possibly namespaced) output id
output_name = self._ns(id or renderer.__name__)
output_id = id or renderer.__name__
output_name = self._ns(output_id)

# renderer is a Renderer object. Give it a bit of metadata.
renderer._set_output_metadata(output_name=output_name)
renderer._set_output_metadata(output_id=output_name)

renderer._on_register()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class render_custom_component(Renderer[int]):
"""

# The UI used within Shiny Express mode
def auto_output_ui(self, id: str) -> Tag:
return custom_component(id, height=self.height)
def auto_output_ui(self) -> Tag:
return custom_component(self.output_id, height=self.height)

# The init method is used to set up the renderer's parameters.
# If no parameters are needed, then the `__init__()` method can be omitted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class render_custom_component(Renderer[str]):
"""

# The UI used within Shiny Express mode
def auto_output_ui(self, id: str) -> Tag:
return output_custom_component(id)
def auto_output_ui(self) -> Tag:
return output_custom_component(self.output_id)

# # There are no parameters being supplied to the `output_custom_component` rendering function.
# # Therefore, we can omit the `__init__()` method.
Expand Down
18 changes: 6 additions & 12 deletions tests/pytest/test_output_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,12 @@ def async_renderer(

test_val = "Test: Hello World!"

def app_render_fn() -> str:
return test_val

# ## Test Sync: X =============================================

renderer_sync = async_renderer(app_render_fn)
renderer_sync._set_output_metadata(
output_name="renderer_sync",
)
@async_renderer
def renderer_sync() -> str:
return test_val

# All renderers are async in execution.
assert not is_async_callable(renderer_sync)

Expand All @@ -264,14 +261,11 @@ def app_render_fn() -> str:

async_test_val = "Async: Hello World!"

async def async_app_render_fn() -> str:
@async_renderer
async def renderer_async() -> str:
await asyncio.sleep(0)
return async_test_val

renderer_async = async_renderer(async_app_render_fn)
renderer_async._set_output_metadata(
output_name="renderer_async",
)
if not is_async_callable(renderer_async):
raise RuntimeError("Expected `renderer_async` to be a coro function")

Expand Down

0 comments on commit 1c360a9

Please sign in to comment.