Create output renderers

Package author methods for creating new output renderers.

render.renderer.Renderer

render.renderer.Renderer(self, _fn=None)

Output renderer class

An output renderer is a class that will take in a callable function (value function), transform the returned value into a JSON-serializable object, and send the result to the browser.

When the value function is received, the renderer will be auto registered with the current session's Output class, hooking it into Shiny's reactive graph. By auto registering as an Output, it allows for App authors to skip adding @output above the renderer. (If programmatic id is needed, @output(id="foo") can still be used!)

There are two methods that must be implemented by the subclasses: .auto_output_ui(self) and either .transform(self, value: IT) or .render(self).

  • In Express mode, the output renderer will automatically render its UI via .auto_output_ui(self). This helper method allows App authors to skip adding a ui.output_* function to their UI, making Express mode even more concise. If more control is needed over the UI, @ui.hold can be used to suppress the auto rendering of the UI. When using @ui.hold on a renderer, the renderer's UI will need to be added to the app to connect the rendered output to Shiny's reactive graph.
  • The render method is responsible for executing the value function and performing any transformations for the output value to be JSON-serializable (None is a valid value!). To avoid the boilerplate of resolving the value function and returning early if None is received, package authors may implement the .transform(self, value: IT) method. The transform method's sole job is to transform non-None values into an object that is JSON-serializable.

Examples

#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400

## file: app.py
from __future__ import annotations

# Import the custom renderer implementations
from renderers import render_capitalize, render_upper

from shiny import App, Inputs, Outputs, Session, ui

app_ui = ui.page_fluid(
    ui.h1("Capitalization renderer"),
    ui.input_text("caption", "Caption:", "Data summary"),
    "@render_upper: ",
    ui.output_text_verbatim("upper", placeholder=True),
    "@render_upper(): ",
    ui.output_text_verbatim("upper_with_paren", placeholder=True),
    "@render_capitalize: ",
    ui.output_text_verbatim("cap_upper", placeholder=True),
    "@render_capitalize(to='lower'): ",
    ui.output_text_verbatim("cap_lower", placeholder=True),
)


def server(input: Inputs, output: Outputs, session: Session):
    # Hovering over `@render_upper` will display the class documentation
    @render_upper
    def upper():
        return input.caption()

    # Hovering over `@render_upper` will display the class documentation as there is no
    # `__init__()` documentation
    @render_upper()
    def upper_with_paren():
        return input.caption()

    # Hovering over `@render_capitalize` will display the class documentation
    @render_capitalize
    def cap_upper():
        return input.caption()

    # Hovering over `@render_capitalize` will display the `__init__()` documentation
    @render_capitalize(to_case="lower")
    def cap_lower():
        return input.caption()


app = App(app_ui, server)


## file: renderers.py
from __future__ import annotations

from typing import Literal, Optional

from shiny.render.renderer import Renderer, ValueFn
from shiny.ui import output_text_verbatim


class render_capitalize(Renderer[str]):
    # The documentation for the class will be displayed when the user hovers over the
    # decorator when **no** parenthesis are used. Ex: `@render_capitalize`
    # If no documentation is supplied to the `__init__()` method, then this
    # documentation will be displayed when parenthesis are used on the decorator.
    """
    Render capitalize class documentation goes here.
    """

    to_case: Literal["upper", "lower", "ignore"]
    """
    The case to render the value in.
    """
    placeholder: bool
    """
    Whether to render a placeholder value. (Defaults to `True`)
    """

    def auto_output_ui(self):
        """
        Express UI for the renderer
        """
        return output_text_verbatim(self.output_id, placeholder=True)

    def __init__(
        self,
        _fn: Optional[ValueFn[str]] = None,
        *,
        to_case: Literal["upper", "lower", "ignore"] = "upper",
        placeholder: bool = True,
    ) -> None:
        # If a different set of documentation is supplied to the `__init__` method,
        # then this documentation will be displayed when parenthesis are used on the decorator.
        # Ex: `@render_capitalize()`
        """
        Render capitalize documentation goes here.

        It is a good idea to talk about parameters here!

##         Parameters

        to_case
            The case to render the value. (`"upper"`)

            Options:
            - `"upper"`: Render the value in upper case.
            - `"lower"`: Render the value in lower case.
            - `"ignore"`: Do not alter the case of the value.

        placeholder
            Whether to render a placeholder value. (`True`)
        """
        # Do not pass params
        super().__init__(_fn)
        self.to_case = to_case
        self.placeholder = placeholder

    async def render(self) -> str | None:
        value = await self.fn()
        if value is None:
            # If `None` is returned, then do not render anything.
            return None

        ret = str(value)
        if self.to_case == "upper":
            return ret.upper()
        if self.to_case == "lower":
            return ret.lower()
        if self.to_case == "ignore":
            return ret
        raise ValueError(f"Invalid value for `to_case`: {self.to_case}")


class render_upper(Renderer[str]):
    """
    Minimal capitalize string transformation renderer.

    No parameters are supplied to this renderer. This allows us to skip the `__init__()`
    method and `__init__()` documentation. If you hover over this decorator with and
    without parenthesis, you will see this documentation in both situations.

    Note: This renderer is equivalent to `render_capitalize(to="upper")`.
    """

    def auto_output_ui(self):
        """
        Express UI for the renderer
        """
        return output_text_verbatim(self.output_id, placeholder=True)

    async def transform(self, value: str) -> str:
        """
        Transform the value to upper case.

        This method is shorthand for the default `render()` method. It is useful to
        transform non-`None` values. (Any `None` value returned by the app author will
        be forwarded to the browser.)

##         Parameters

        value
            The a non-`None` value to transform.

##         Returns

        str
            The transformed value. (Must be a subset of `Jsonifiable`.)
        """

        return str(value).upper()

Attributes

Name Description
fn App-supplied output value function which returns type IT. This function is always asyncronous as the original app-supplied function possibly wrapped to execute asynchonously.
output_id Output function name or ID (provided to @output(id=)).

Methods

Name Description
auto_output_ui Express mode method that automatically generates the output’s UI.
render Renders the output value function.
transform Transform an output value into a JSON-serializable object.

auto_output_ui

render.renderer.Renderer.auto_output_ui()

Express mode method that automatically generates the output's UI.

render

render.renderer.Renderer.render()

Renders the output value function.

This method is called when the renderer is requested to render its output.

The Renderer's render() implementation goes as follows:

  • Execute the value function supplied to the renderer.
  • If the output value is None, None will be returned.
  • If the output value is not None, the .transform() method will be called to transform the value into a JSON-serializable object.

When overwriting this method in a subclass, the implementation should execute the value function .fn and return the transformed value (which is JSON-serializable).

transform

render.renderer.Renderer.transform(value)

Transform an output value into a JSON-serializable object.

When subclassing Renderer, this method can be implemented to transform non-None values into a JSON-serializable object.

If a .render() method is not implemented, this method must be implemented. When the output is requested, the Renderer's .render() method will execute the output value function, return None if the value is None, and call this method to transform the value into a JSON-serializable object.

Note, only one of .transform() or .render() should be implemented.

render.renderer.Jsonifiable

render.renderer.Jsonifiable

render.renderer.ValueFn

render.renderer.ValueFn

App-supplied output value function which returns type IT or None. This function can be synchronous or asynchronous.

render.renderer.AsyncValueFn

render.renderer.AsyncValueFn(self, fn)

App-supplied output value function which returns type IT. asynchronous.

Type definition: Callable[[], Awaitable[IT]]

Methods

Name Description
get_async_fn Return the async value function.
get_sync_fn Retrieve the original, synchronous value function function.
is_async Was the original function asynchronous?

get_async_fn

render.renderer.AsyncValueFn.get_async_fn()

Return the async value function.

Returns

Type Description
Callable[[], Awaitable[IT | None]] Async wrapped value function supplied to the AsyncValueFn constructor.

get_sync_fn

render.renderer.AsyncValueFn.get_sync_fn()

Retrieve the original, synchronous value function function.

If the original function was asynchronous, a runtime error will be thrown.

Returns

Type Description
Callable[[], IT | None] Original, synchronous function supplied to the AsyncValueFn constructor.

is_async

render.renderer.AsyncValueFn.is_async()

Was the original function asynchronous?

Returns

Type Description
bool Whether the original function is asynchronous.

render.renderer.RendererT

render.renderer.RendererT

Generic output renderer class to pass the original Renderer subclass through a decorator function.

When accepting and returning a Renderer class, utilize this TypeVar as to not reduce the variable type to Renderer[Any]