diff --git a/shiny/api-examples/Renderer/app.py b/shiny/api-examples/Renderer/app.py index 332a470f0..e1a8cb513 100644 --- a/shiny/api-examples/Renderer/app.py +++ b/shiny/api-examples/Renderer/app.py @@ -28,7 +28,7 @@ class render_capitalize(Renderer[str]): Whether to render a placeholder value. (Defaults to `True`) """ - def default_ui(self, id: str): + def auto_output_ui(self, id: str): """ Express UI for the renderer """ @@ -94,7 +94,7 @@ class render_upper(Renderer[str]): Note: This renderer is equivalent to `render_capitalize(to="upper")`. """ - def default_ui(self, id: str): + def auto_output_ui(self, id: str): """ Express UI for the renderer """ diff --git a/shiny/express/__init__.py b/shiny/express/__init__.py index 1c430267f..8add0557a 100644 --- a/shiny/express/__init__.py +++ b/shiny/express/__init__.py @@ -4,22 +4,23 @@ # console. from ..session import Inputs as _Inputs, Outputs as _Outputs, Session as _Session from ..session import _utils as _session_utils +from .. import render from . import ui from ._is_express import is_express_app from ._output import ( # noqa: F401 - ui_kwargs, suspend_display, output_args, # pyright: ignore[reportUnusedImport] ) from ._run import wrap_express_app from .display_decorator import display_body + __all__ = ( + "render", "input", "output", "session", "is_express_app", - "ui_kwargs", "suspend_display", "wrap_express_app", "ui", diff --git a/shiny/express/_output.py b/shiny/express/_output.py index 6ab1362d8..6d5b2c1a8 100644 --- a/shiny/express/_output.py +++ b/shiny/express/_output.py @@ -6,81 +6,29 @@ from typing import Callable, Generator, TypeVar, overload from .. import ui +from .._deprecated import warn_deprecated from .._typing_extensions import ParamSpec from ..render.renderer import RendererBase, RendererBaseT from ..render.transformer import OutputRenderer from ..render.transformer._transformer import OT -__all__ = ( - "ui_kwargs", - "suspend_display", -) +__all__ = ("suspend_display",) P = ParamSpec("P") R = TypeVar("R") CallableT = TypeVar("CallableT", bound=Callable[..., object]) -# TODO-barret-future; quartodoc entry? -def ui_kwargs( - **kwargs: object, -) -> Callable[[RendererBaseT], RendererBaseT]: - """ - Sets default UI arguments for a Shiny rendering function. - - Each Shiny render function (like :func:`~shiny.render.plot`) can display itself when - declared within a Shiny inline-style application. In the case of - :func:`~shiny.render.plot`, the :func:`~shiny.ui.output_plot` function is called - implicitly to display the plot. Use the `@ui_kwargs` decorator to specify - arguments to be passed to `output_plot` (or whatever the corresponding UI function - is) when the render function displays itself. - - Parameters - ---------- - **kwargs - Keyword arguments to be passed to the UI function. - - Returns - ------- - : - A decorator that sets the default UI arguments for a Shiny rendering function. - """ - - def wrapper(renderer: RendererBaseT) -> RendererBaseT: - # renderer._default_ui_args = args - renderer._default_ui_kwargs = kwargs - return renderer - - return wrapper - - def output_args( *args: object, **kwargs: object, ) -> Callable[[OutputRenderer[OT]], OutputRenderer[OT]]: """ - Sets default UI arguments for a Shiny rendering function. - - Each Shiny render function (like :func:`~shiny.render.plot`) can display itself when - declared within a Shiny inline-style application. In the case of - :func:`~shiny.render.plot`, the :func:`~shiny.ui.output_plot` function is called - implicitly to display the plot. Use the `@output_args` decorator to specify - arguments to be passed to `output_plot` (or whatever the corresponding UI function - is) when the render function displays itself. - - - Parameters - ---------- - *args - Positional arguments to be passed to the UI function. - **kwargs - Keyword arguments to be passed to the UI function. - - Returns - ------- - : - A decorator that sets the default UI arguments for a Shiny rendering function. + Deprecated. Sets default UI arguments for a Shiny rendering function. """ + warn_deprecated( + "`shiny.express.output_args()` is deprecated and will be removed in a future version of shiny." + ) def wrapper(renderer: OutputRenderer[OT]) -> OutputRenderer[OT]: if not isinstance(renderer, OutputRenderer): @@ -89,8 +37,8 @@ def wrapper(renderer: OutputRenderer[OT]) -> OutputRenderer[OT]: "\nIf you are trying to set default UI arguments for a `Renderer`, use" " `@ui_kwargs` instead." ) - renderer._default_ui_args = args - renderer._default_ui_kwargs = kwargs + renderer._auto_output_ui_args = args + renderer._auto_output_ui_kwargs = kwargs return renderer @@ -152,7 +100,7 @@ def suspend_display( # display yourself" if isinstance(fn, RendererBase): # By setting the class value, the `self` arg will be auto added. - fn.default_ui = null_ui + fn.auto_output_ui = null_ui return fn return suspend_display_ctxmgr()(fn) diff --git a/shiny/express/ui/__init__.py b/shiny/express/ui/__init__.py index 730380281..533371a84 100644 --- a/shiny/express/ui/__init__.py +++ b/shiny/express/ui/__init__.py @@ -38,8 +38,6 @@ SliderStepArg, SliderValueArg, ValueBoxTheme, - download_button, - download_link, brush_opts, click_opts, dblclick_opts, @@ -95,14 +93,7 @@ notification_show, notification_remove, nav_spacer, - output_plot, - output_image, - output_text, - output_text_verbatim, - output_table, - output_ui, Progress, - output_data_frame, value_box_theme, ) @@ -178,8 +169,6 @@ "SliderStepArg", "SliderValueArg", "ValueBoxTheme", - "download_button", - "download_link", "brush_opts", "click_opts", "dblclick_opts", @@ -235,14 +224,7 @@ "notification_show", "notification_remove", "nav_spacer", - "output_plot", - "output_image", - "output_text", - "output_text_verbatim", - "output_table", - "output_ui", "Progress", - "output_data_frame", "value_box_theme", # Imports from ._cm_components "sidebar", @@ -301,6 +283,17 @@ "showcase_bottom", "showcase_left_center", "showcase_top_right", + # Outputs automatically placed by render functions + "download_button", + "download_link", + "output_plot", + "output_image", + "output_text", + "output_code", + "output_text_verbatim", + "output_table", + "output_ui", + "output_data_frame", ), # Items from shiny.express.ui that don't have a counterpart in shiny.ui "shiny.express.ui": ("page_opts",), diff --git a/shiny/render/_dataframe.py b/shiny/render/_dataframe.py index 246aab89f..6ecdc55d5 100644 --- a/shiny/render/_dataframe.py +++ b/shiny/render/_dataframe.py @@ -256,7 +256,7 @@ class data_frame(Renderer[DataFrameResult]): objects you can return from the rendering function to specify options. """ - def default_ui(self, id: str) -> Tag: + def auto_output_ui(self, id: str) -> Tag: return ui.output_data_frame(id=id) async def transform(self, value: DataFrameResult) -> Jsonifiable: diff --git a/shiny/render/_display.py b/shiny/render/_display.py index 593cdda4d..da597a9f1 100644 --- a/shiny/render/_display.py +++ b/shiny/render/_display.py @@ -18,7 +18,7 @@ class display(Renderer[None]): - def default_ui( + def auto_output_ui( self, id: str, *, diff --git a/shiny/render/_render.py b/shiny/render/_render.py index 30f978402..5ab38d7f2 100644 --- a/shiny/render/_render.py +++ b/shiny/render/_render.py @@ -9,7 +9,6 @@ # Can use `dict` in python >= 3.9 from typing import ( TYPE_CHECKING, - Any, Callable, Literal, Optional, @@ -19,7 +18,7 @@ runtime_checkable, ) -from htmltools import Tag, TagAttrValue, TagChild +from htmltools import Tag, TagAttrValue, TagChild, TagFunction if TYPE_CHECKING: from ..session._utils import RenderedDeps @@ -32,6 +31,7 @@ from ..session import get_current_session, require_active_session from ..session._session import DownloadHandler, DownloadInfo from ..types import MISSING, MISSING_TYPE, ImgData +from ..ui._plot_output_opts import BrushOpts, ClickOpts, DblClickOpts, HoverOpts from ._try_render_plot import ( PlotSizeInfo, try_render_matplotlib, @@ -39,11 +39,7 @@ try_render_plotnine, ) from .renderer import Jsonifiable, Renderer, ValueFn -from .renderer._utils import ( - imgdata_to_jsonifiable, - rendered_deps_to_jsonifiable, - set_kwargs_value, -) +from .renderer._utils import imgdata_to_jsonifiable, rendered_deps_to_jsonifiable __all__ = ( "text", @@ -91,25 +87,23 @@ class text(Renderer[str]): ~shiny.ui.output_text """ - def default_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) + def auto_output_ui(self, id: str) -> Tag: + return _ui.output_text( + id, + inline=self.inline, + container=self.container, + ) def __init__( self, _fn: Optional[ValueFn[str]] = None, *, inline: bool = False, + container: Optional[TagFunction] = None, ) -> None: super().__init__(_fn) self.inline = inline + self.container = container async def transform(self, value: str) -> Jsonifiable: return str(value) @@ -154,15 +148,8 @@ class code(Renderer[str]): ~shiny.ui.output_code """ - def default_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) + def auto_output_ui(self, id: str) -> Tag: + return _ui.output_code(id, placeholder=self.placeholder) def __init__( self, @@ -207,6 +194,43 @@ class plot(Renderer[object]): determined by the size of the corresponding :func:`~shiny.ui.output_plot`. (You should not need to use this argument in most Shiny apps--set the desired height on :func:`~shiny.ui.output_plot` instead.) + inline + (Express only) If ``True``, the result is displayed inline. + click + (Express only) This can be a boolean or an object created by + :func:`~shiny.ui.click_opts`. The default is `False`, but if you use `True` (or + equivalently, `click_opts()`), the plot will send coordinates to the server + whenever it is clicked, and the value will be accessible via `input.xx_click()`, + where `xx` is replaced with the ID of this plot. The input value will be a + dictionary with `x` and `y` elements indicating the mouse position. + dblclick + (Express only) This is just like the `click` parameter, but for double-click + events. + hover + (Express only) Similar to the `click` argument, this can be a boolean or an + object created by :func:`~shiny.ui.hover_opts`. The default is `False`, but if + you use `True` (or equivalently, `hover_opts()`), the plot will send coordinates + to the server whenever it is clicked, and the value will be accessible via + `input.xx_hover()`, where `xx` is replaced with the ID of this plot. The input + value will be a dictionary with `x` and `y` elements indicating the mouse + position. To control the hover time or hover delay type, use + :func:`~shiny.ui.hover_opts`. + brush + (Express only) Similar to the `click` argument, this can be a boolean or an + object created by :func:`~shiny.ui.brush_opts`. The default is `False`, but if + you use `True` (or equivalently, `brush_opts()`), the plot will allow the user + to "brush" in the plotting area, and will send information about the brushed + area to the server, and the value will be accessible via `input.plot_brush()`. + Brushing means that the user will be able to draw a rectangle in the plotting + area and drag it around. The value will be a named list with `xmin`, `xmax`, + `ymin`, and `ymax` elements indicating the brush area. To control the brush + behavior, use :func:`~shiny.ui.brush_opts`. Multiple + `output_image`/`output_plot` calls may share the same `id` value; brushing one + image or plot will cause any other brushes with the same `id` to disappear. + fill + (Express only) Whether or not to allow the image output to grow/shrink to fit a + fillable container with an opinionated height (e.g., + :func:`~shiny.ui.page_fillable`). **kwargs Additional keyword arguments passed to the relevant method for saving the image (e.g., for matplotlib, arguments to ``savefig()``; for PIL and plotnine, @@ -241,22 +265,25 @@ class plot(Renderer[object]): ~shiny.ui.output_plot ~shiny.render.image """ - def default_ui( - self, - id: str, - *, - width: str | float | int | MISSING_TYPE = MISSING, - height: str | float | int | MISSING_TYPE = MISSING, - **kwargs: object, - ) -> Tag: - # Only set the arg if it is available. (Prevents duplicating default values) - set_kwargs_value(kwargs, "width", width, self.width) - set_kwargs_value(kwargs, "height", height, self.height) + def auto_output_ui(self, id: str) -> Tag: + kwargs: dict[str, object] = {} + # Only set the arg if it is available + if not isinstance(self.width, MISSING_TYPE): + kwargs["width"] = self.width + if not isinstance(self.height, MISSING_TYPE): + kwargs["height"] = self.height + return _ui.output_plot( id, - # (possibly) contains `width` and `height` keys! + inline=self.inline, + click=self.click, + dblclick=self.dblclick, + hover=self.hover, + brush=self.brush, + fill=self.fill, **kwargs, # pyright: ignore[reportGeneralTypeIssues] ) + # TODO: Deal with output width/height separately from render width/height? def __init__( self, @@ -265,6 +292,12 @@ def __init__( alt: Optional[str] = None, width: float | None | MISSING_TYPE = MISSING, height: float | None | MISSING_TYPE = MISSING, + inline: bool = False, + click: bool | ClickOpts = False, + dblclick: bool | DblClickOpts = False, + hover: bool | HoverOpts = False, + brush: bool | BrushOpts = False, + fill: bool | MISSING_TYPE = MISSING, **kwargs: object, ) -> None: super().__init__(_fn) @@ -273,6 +306,13 @@ def __init__( self.height = height self.kwargs = kwargs + self.inline = inline + self.click = click + self.dblclick = dblclick + self.hover = hover + self.brush = brush + self.fill = fill + async def render(self) -> dict[str, Jsonifiable] | Jsonifiable | None: is_userfn_async = self.fn.is_async() name = self.output_id @@ -393,6 +433,51 @@ class image(Renderer[ImgData]): ---------- delete_file If ``True``, the image file will be deleted after rendering. + width + (Express only) The CSS width, e.g. '400px', or '100%'. Note that this only + affects the output HTML element on the web page, not the actual image data sent + to the browser. + height + (Express only) The CSS height, e.g. '100%' or '600px'. Note that this only + affects the output HTML element on the web page, not the actual image data sent + to the browser. + inline + (Express only) If ``True``, the result is displayed inline. + click + (Express only) This can be a boolean or an object created by + :func:`~shiny.ui.click_opts`. The default is `False`, but if you use `True` (or + equivalently, `click_opts()`), the plot will send coordinates to the server + whenever it is clicked, and the value will be accessible via `input.xx_click()`, + where `xx` is replaced with the ID of this plot. The input value will be a + dictionary with `x` and `y` elements indicating the mouse position. + dblclick + (Express only) This is just like the `click` parameter, but for double-click + events. + hover + (Express only) Similar to the `click` argument, this can be a boolean or an + object created by :func:`~shiny.ui.hover_opts`. The default is `False`, but if + you use `True` (or equivalently, `hover_opts()`), the plot will send coordinates + to the server whenever it is clicked, and the value will be accessible via + `input.xx_hover()`, where `xx` is replaced with the ID of this plot. The input + value will be a dictionary with `x` and `y` elements indicating the mouse + position. To control the hover time or hover delay type, use + :func:`~shiny.ui.hover_opts`. + brush + (Express only) Similar to the `click` argument, this can be a boolean or an + object created by :func:`~shiny.ui.brush_opts`. The default is `False`, but if + you use `True` (or equivalently, `brush_opts()`), the plot will allow the user + to "brush" in the plotting area, and will send information about the brushed + area to the server, and the value will be accessible via `input.plot_brush()`. + Brushing means that the user will be able to draw a rectangle in the plotting + area and drag it around. The value will be a named list with `xmin`, `xmax`, + `ymin`, and `ymax` elements indicating the brush area. To control the brush + behavior, use :func:`~shiny.ui.brush_opts`. Multiple + `output_image`/`output_plot` calls may share the same `id` value; brushing one + image or plot will cause any other brushes with the same `id` to disappear. + fill + (Express only) Whether or not to allow the image output to grow/shrink to fit a + fillable container with an opinionated height (e.g., + :func:`~shiny.ui.page_fillable`). Returns ------- @@ -407,25 +492,48 @@ class image(Renderer[ImgData]): See Also -------- - ~shiny.ui.output_image - ~shiny.types.ImgData - ~shiny.render.plot + ~shiny.ui.output_image ~shiny.types.ImgData ~shiny.render.plot """ - def default_ui(self, id: str, **kwargs: object): + def auto_output_ui(self, id: str): return _ui.output_image( id, - **kwargs, # pyright: ignore[reportGeneralTypeIssues] + width=self.width, + height=self.height, + inline=self.inline, + click=self.click, + dblclick=self.dblclick, + hover=self.hover, + brush=self.brush, + fill=self.fill, ) + # TODO: Make width/height handling consistent with render_plot def __init__( self, _fn: Optional[ValueFn[ImgData]] = None, *, delete_file: bool = False, + width: str | float | int = "100%", + height: str | float | int = "400px", + inline: bool = False, + click: bool | ClickOpts = False, + dblclick: bool | DblClickOpts = False, + hover: bool | HoverOpts = False, + brush: bool | BrushOpts = False, + fill: bool = False, ) -> None: super().__init__(_fn) - self.delete_file: bool = delete_file + + self.delete_file = delete_file + self.width = width + self.height = height + self.inline = inline + self.click = click + self.dblclick = dblclick + self.hover = hover + self.brush = brush + self.fill = fill async def transform(self, value: ImgData) -> dict[str, Jsonifiable] | None: src: str = value.get("src") @@ -503,8 +611,9 @@ class table(Renderer[TableResult]): ~shiny.ui.output_table for the corresponding UI component to this render function. """ - def default_ui(self, id: str, **kwargs: TagAttrValue) -> Tag: + def auto_output_ui(self, id: str, **kwargs: TagAttrValue) -> Tag: return _ui.output_table(id, **kwargs) + # TODO: Deal with kwargs def __init__( self, @@ -521,6 +630,8 @@ def __init__( self.border: int = border self.kwargs: dict[str, object] = kwargs + # TODO: deal with kwargs collision with output_table + async def transform(self, value: TableResult) -> dict[str, Jsonifiable]: import pandas import pandas.io.formats.style @@ -562,6 +673,22 @@ class ui(Renderer[TagChild]): """ Reactively render HTML content. + Parameters + ---------- + inline + (Express only) If ``True``, the result is displayed inline. + container + (Express only) A Callable that returns the output container. + fill + (Express only) Whether or not to allow the UI output to grow/shrink to fit a + fillable container with an opinionated height (e.g., + :func:`~shiny.ui.page_fillable`). + fillable + (Express only) Whether or not the UI output area should be considered a fillable + (i.e., flexbox) container. + **kwargs + (Express only) Attributes to be applied to the output container. + Returns ------- : @@ -579,8 +706,36 @@ class ui(Renderer[TagChild]): ~shiny.ui.output_ui """ - def default_ui(self, id: str) -> Tag: - return _ui.output_ui(id) + def auto_output_ui(self, id: str) -> Tag: + return _ui.output_ui( + id, + inline=self.inline, + container=self.container, + fill=self.fill, + fillable=self.fillable, + **self.kwargs, + ) + + def __init__( + self, + fn: Optional[ValueFn[TagChild]] = None, + *, + inline: bool = False, + container: Optional[TagFunction] = None, + fill: bool = False, + fillable: bool = False, + **kwargs: TagAttrValue, + ) -> None: + super().__init__() + + self.inline = inline + self.container = container + self.fill = fill + self.fillable = fillable + self.kwargs = kwargs + + if fn is not None: + self(fn) async def transform(self, value: TagChild) -> Jsonifiable: session = require_active_session(None) @@ -600,12 +755,18 @@ class download(Renderer[str]): ---------- filename The filename of the download. - label - A label for the button, when used in Express mode. Defaults to "Download". media_type The media type of the download. encoding The encoding of the download. + label + (Express only) A label for the button. Defaults to "Download". + icon + (Express only) An icon to display on the button. + width + (Express only) The width of the button. + **kwargs + (Express only) Additional attributes for the button. Returns ------- @@ -617,24 +778,36 @@ class download(Renderer[str]): ~shiny.ui.download_button """ - def default_ui(self, id: str) -> Tag: - return _ui.download_button(id, label=self.label) + def auto_output_ui(self, id: str) -> Tag: + return _ui.download_button( + id, + label=self.label, + icon=self.icon, + width=self.width, + **self.kwargs, + ) def __init__( self, fn: Optional[DownloadHandler] = None, *, filename: Optional[str | Callable[[], str]] = None, - label: TagChild = "Download", media_type: None | str | Callable[[], str] = None, encoding: str = "utf-8", + label: TagChild = "Download", + icon: TagChild = None, + width: Optional[str] = None, + **kwargs: TagAttrValue, ) -> None: super().__init__() - self.label = label self.filename = filename self.media_type = media_type self.encoding = encoding + self.label = label + self.icon = icon + self.width = width + self.kwargs = kwargs if fn is not None: self(fn) diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index 0be14fc23..5a6cff00f 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -102,8 +102,8 @@ class RendererBase(ABC): # Q: Could we do this with typing without putting `P` in the Generic? # 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 - _default_ui_kwargs: dict[str, Any] = dict() - # _default_ui_args: tuple[Any, ...] = tuple() + _auto_output_ui_kwargs: dict[str, Any] = dict() + # _auto_output_ui_args: tuple[Any, ...] = tuple() __name__: str """ @@ -135,12 +135,7 @@ def _set_output_metadata( """ self.output_id = output_name - def default_ui( - self, - id: str, - # *args: object, - # **kwargs: object, - ) -> DefaultUIFnResultOrNone: + def auto_output_ui(self, id: str) -> DefaultUIFnResultOrNone: return None @abstractmethod @@ -155,13 +150,13 @@ def __init__(self) -> None: # Tagify-like methods # ###### def _repr_html_(self) -> str | None: - rendered_ui = self._render_default_ui() + rendered_ui = self.auto_output_ui(self.__name__) if rendered_ui is None: return None return TagList(rendered_ui)._repr_html_() def tagify(self) -> DefaultUIFnResult: - rendered_ui = self._render_default_ui() + rendered_ui = self.auto_output_ui(self.__name__) if rendered_ui is None: raise TypeError( "No default UI exists for this type of render function: ", @@ -169,13 +164,6 @@ def tagify(self) -> DefaultUIFnResult: ) return rendered_ui - def _render_default_ui(self) -> DefaultUIFnResultOrNone: - return self.default_ui( - self.__name__, - # Pass the `@ui_kwargs(foo="bar")` kwargs through to the default_ui function. - **self._default_ui_kwargs, - ) - # ###### # Auto registering output # ###### diff --git a/shiny/render/transformer/_transformer.py b/shiny/render/transformer/_transformer.py index bb7315d18..7a118bf56 100644 --- a/shiny/render/transformer/_transformer.py +++ b/shiny/render/transformer/_transformer.py @@ -220,8 +220,8 @@ def __init__( value_fn: ValueFn[IT], transform_fn: TransformFn[IT, P, OT], params: TransformerParams[P], - default_ui: Optional[DefaultUIFn] = None, - default_ui_passthrough_args: Optional[tuple[str, ...]] = None, + auto_output_ui: Optional[DefaultUIFn] = None, + auto_output_ui_passthrough_args: Optional[tuple[str, ...]] = None, ) -> None: """ Parameters @@ -233,7 +233,7 @@ def __init__( `OT`. The `params` will used as variadic keyword arguments. params App-provided parameters for the transform function (`transform_fn`). - default_ui + auto_output_ui Optional function that takes an `output_id` string and returns a Shiny UI object that can be used to display the output. This allows render functions to respond to `_repr_html_` method calls in environments like Jupyter. @@ -266,11 +266,11 @@ def __init__( self._transformer = transform_fn self._params = params - self._default_ui = default_ui - self._default_ui_passthrough_args = default_ui_passthrough_args + self._auto_output_ui = auto_output_ui + self._auto_output_ui_passthrough_args = auto_output_ui_passthrough_args - self._default_ui_args: tuple[object, ...] = tuple() - self._default_ui_kwargs: dict[str, object] = dict() + self._auto_output_ui_args: tuple[object, ...] = tuple() + self._auto_output_ui_kwargs: dict[str, object] = dict() # Allow for App authors to not require `@output` self._auto_register() @@ -318,24 +318,24 @@ async def _run(self) -> OT: # # Shims for Renderer class ############################# - def default_ui( + def auto_output_ui( self, id: str, **kwargs: object, ) -> DefaultUIFnResultOrNone: - if self._default_ui is None: + if self._auto_output_ui is None: return None - if self._default_ui_passthrough_args is not None: + if self._auto_output_ui_passthrough_args is not None: kwargs.update( { k: v for k, v in self._params.kwargs.items() - if k in self._default_ui_passthrough_args and v is not MISSING + if k in self._auto_output_ui_passthrough_args and v is not MISSING } ) - return self._default_ui(id, *self._default_ui_args, **kwargs) + return self._auto_output_ui(id, *self._auto_output_ui_args, **kwargs) async def render(self) -> Jsonifiable: ret = await self._run() @@ -507,8 +507,8 @@ def __init__( @overload def output_transformer( *, - default_ui: Optional[DefaultUIFn] = None, - default_ui_passthrough_args: Optional[tuple[str, ...]] = None, + auto_output_ui: Optional[DefaultUIFn] = None, + auto_output_ui_passthrough_args: Optional[tuple[str, ...]] = None, ) -> Callable[[TransformFn[IT, P, OT]], OutputTransformer[IT, OT, P]]: ... @@ -524,8 +524,8 @@ def output_transformer( def output_transformer( transform_fn: TransformFn[IT, P, OT] | None = None, *, - default_ui: Optional[DefaultUIFn] = None, - default_ui_passthrough_args: Optional[tuple[str, ...]] = None, + auto_output_ui: Optional[DefaultUIFn] = None, + auto_output_ui_passthrough_args: Optional[tuple[str, ...]] = None, ) -> ( OutputTransformer[IT, OT, P] | Callable[[TransformFn[IT, P, OT]], OutputTransformer[IT, OT, P]] @@ -588,7 +588,7 @@ def output_transformer( Asynchronous function used to determine the app-supplied output value function return type (`IT`), the transformed type (`OT`), and the keyword arguments (`P`) app authors can supply to the renderer decorator. - default_ui + auto_output_ui Optional function that takes an `output_id` string and returns a Shiny UI object that can be used to display the output. This allows render functions to respond to `_repr_html_` method calls in environments like Jupyter. @@ -602,8 +602,8 @@ def output_transformer( called with parentheses. """ - # If default_ui_passthrough_args was used, modify the default_ui function so it is - # ready to mix in extra arguments from the decorator. + # If auto_output_ui_passthrough_args was used, modify the auto_output_ui function so + # it is ready to mix in extra arguments from the decorator. def output_transformer_impl( transform_fn: TransformFn[IT, P, OT], ) -> OutputTransformer[IT, OT, P]: @@ -620,8 +620,8 @@ def as_value_fn( value_fn=fn, transform_fn=transform_fn, params=params, - default_ui=default_ui, - default_ui_passthrough_args=default_ui_passthrough_args, + auto_output_ui=auto_output_ui, + auto_output_ui_passthrough_args=auto_output_ui_passthrough_args, ) if value_fn is None: diff --git a/shiny/templates/package-templates/js-output/custom_component/custom_component.py b/shiny/templates/package-templates/js-output/custom_component/custom_component.py index 2dd5f13a8..1bf568912 100644 --- a/shiny/templates/package-templates/js-output/custom_component/custom_component.py +++ b/shiny/templates/package-templates/js-output/custom_component/custom_component.py @@ -27,7 +27,7 @@ class render_custom_component(Renderer[int]): """ # The UI used within Shiny Express mode - def default_ui(self, id: str) -> Tag: + def auto_output_ui(self, id: str) -> Tag: return custom_component(id, height=self.height) # The init method is used to set up the renderer's parameters. diff --git a/shiny/templates/package-templates/js-react/custom_component/custom_component.py b/shiny/templates/package-templates/js-react/custom_component/custom_component.py index a8dc26c99..f68f90a12 100644 --- a/shiny/templates/package-templates/js-react/custom_component/custom_component.py +++ b/shiny/templates/package-templates/js-react/custom_component/custom_component.py @@ -39,7 +39,7 @@ class render_custom_component(Renderer[str]): """ # The UI used within Shiny Express mode - def default_ui(self, id: str) -> Tag: + def auto_output_ui(self, id: str) -> Tag: return output_custom_component(id) # # There are no parameters being supplied to the `output_custom_component` rendering function. diff --git a/tests/pytest/test_express_ui.py b/tests/pytest/test_express_ui.py index 4f97c9d5e..510e1d54c 100644 --- a/tests/pytest/test_express_ui.py +++ b/tests/pytest/test_express_ui.py @@ -6,7 +6,7 @@ import pytest from shiny import render, ui -from shiny.express import suspend_display, ui_kwargs +from shiny.express import suspend_display from shiny.express._run import run_express @@ -60,8 +60,7 @@ def text2(): assert ui.TagList(text2.tagify()).get_html_string() == "" - @ui_kwargs(placeholder=True) - @render.code + @render.code(placeholder=True) def code1(): return "text" @@ -70,14 +69,6 @@ def code1(): == ui.output_code("code1", placeholder=True).get_html_string() ) - @ui_kwargs(width=100) - @render.code - def code2(): - return "text" - - with pytest.raises(TypeError, match="width"): - code2.tagify() - def test_suspend_display(): old_displayhook = sys.displayhook diff --git a/tests/pytest/test_plot_sizing.py b/tests/pytest/test_plot_sizing.py index da6a354fc..0340922ac 100644 --- a/tests/pytest/test_plot_sizing.py +++ b/tests/pytest/test_plot_sizing.py @@ -1,5 +1,4 @@ from shiny import render, ui -from shiny.express import ui_kwargs from shiny.types import MISSING @@ -27,38 +26,38 @@ def foo(): assert rendered == str(ui.output_plot("foo")) -def test_decorator_ui_kwargs(): - """@ui_kwargs is respected""" +# def test_decorator_ui_kwargs(): +# """@ui_kwargs is respected""" - @ui_kwargs(width="640px", height="480px") - @render.plot() - def foo(): - ... +# @ui_kwargs(width="640px", height="480px") +# @render.plot() +# def foo(): +# ... - rendered = str(foo.tagify()) - assert rendered == str(ui.output_plot("foo", width="640px", height="480px")) +# rendered = str(foo.tagify()) +# assert rendered == str(ui.output_plot("foo", width="640px", height="480px")) -def test_decorator_ui_kwargs_priority(): - """@ui_kwargs should override render.plot width/height""" +# def test_decorator_ui_kwargs_priority(): +# """@ui_kwargs should override render.plot width/height""" - @ui_kwargs(width="640px", height=480) - @render.plot(width=1280, height=960) - def foo(): - ... +# @ui_kwargs(width="640px", height=480) +# @render.plot(width=1280, height=960) +# def foo(): +# ... - rendered = str(foo.tagify()) - # Note "640px" => 640 and 480 => "480px" - assert rendered == str(ui.output_plot("foo", width=640, height="480px")) +# rendered = str(foo.tagify()) +# # Note "640px" => 640 and 480 => "480px" +# assert rendered == str(ui.output_plot("foo", width=640, height="480px")) -def test_decorator_ui_kwargs_MISSING(): - """Not saying we support this, but test how MISSING interacts""" +# def test_decorator_ui_kwargs_MISSING(): +# """Not saying we support this, but test how MISSING interacts""" - @ui_kwargs(width=MISSING) - @render.plot(width=1280, height=MISSING) - def foo(): - ... +# @ui_kwargs(width=MISSING) +# @render.plot(width=1280, height=MISSING) +# def foo(): +# ... - rendered = str(foo.tagify()) - assert rendered == str(ui.output_plot("foo", width="1280px")) +# rendered = str(foo.tagify()) +# assert rendered == str(ui.output_plot("foo", width="1280px"))