Skip to content

Commit 2740257

Browse files
docs: docstring for core shiny.modules.ui and shiny.module.server (#1974)
Co-authored-by: Daniel Chen <[email protected]> Co-authored-by: Carson Sievert <[email protected]>
1 parent 4d7a3f5 commit 2740257

File tree

5 files changed

+98
-31
lines changed

5 files changed

+98
-31
lines changed

shiny/api-examples/Module/app-core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
99
return ui.card(
1010
ui.h2("This is " + label),
1111
ui.input_action_button(id="button", label=label),
12-
ui.output_text_verbatim(id="out"),
12+
ui.output_text(id="out"),
1313
)
1414

1515

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from shiny import reactive
2+
from shiny.express import module, render, ui
3+
4+
5+
# ============================================================
6+
# Counter module
7+
# ============================================================
8+
@module
9+
def counter(input, output, session, label, starting_value: int = 0):
10+
count = reactive.value(starting_value)
11+
with ui.card():
12+
ui.h2(f"This is {label}")
13+
ui.input_action_button("button", f"{label}")
14+
15+
@render.text
16+
def out():
17+
return f"Click count is {count()}"
18+
19+
@reactive.effect
20+
@reactive.event(input.button)
21+
def _():
22+
count.set(count() + 1)
23+
24+
25+
# =============================================================================
26+
# App that uses module
27+
# =============================================================================
28+
counter("counter1", "Counter 1", starting_value=0)
29+
ui.hr()
30+
counter("counter2", "Counter 2", starting_value=0)

shiny/api-examples/express_module/app-express.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

shiny/express/_module.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
__all__ = ("module",)
1616

1717

18-
@add_example(ex_dir="../api-examples/express_module")
18+
@add_example(ex_dir="../api-examples/Module")
1919
def module(
2020
fn: Callable[Concatenate[Inputs, Outputs, Session, P], R],
2121
) -> Callable[Concatenate[Id, P], R]:

shiny/module.py

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from typing import TYPE_CHECKING, Callable, TypeVar
66

7-
from ._docstring import no_example
7+
from ._docstring import add_example
88
from ._namespaces import (
99
Id,
1010
ResolvedId,
@@ -24,25 +24,87 @@
2424
_: Id # type: ignore
2525

2626

27-
@no_example()
27+
@add_example(ex_dir="api-examples/Module")
2828
def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]:
29+
"""
30+
Decorator for defining a Shiny module UI function.
31+
32+
This decorator allows you to write the UI portion of a Shiny module.
33+
When your decorated `ui` function is called with an `id`,
34+
the UI elements defined within will automatically be namespaced using that `id`.
35+
This enables reuse of UI components and consistent input/output handling
36+
when paired with a :func:`shiny.module.server` function.
37+
38+
Parameters
39+
----------
40+
fn
41+
A function that returns a Shiny UI element or layout (e.g., a `ui.panel_*` component).
42+
This function should **not** accept an `id` parameter itself; the decorator injects it.
43+
44+
Returns
45+
-------
46+
:
47+
A function that takes a `str` `id` as its first argument, followed by any additional
48+
parameters accepted by `fn`. When called, it returns UI elements with input/output
49+
IDs automatically namespaced using the provided module `id`.
50+
51+
See Also
52+
--------
53+
* Shiny Modules documentation: <https://shiny.posit.co/py/docs/modules.html>
54+
* :func:`shiny.module.server`
55+
"""
56+
2957
def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
3058
with namespace_context(id):
3159
return fn(*args, **kwargs)
3260

3361
return wrapper
3462

3563

36-
@no_example()
64+
@add_example(ex_dir="api-examples/Module")
3765
def server(
3866
fn: Callable[Concatenate[Inputs, Outputs, Session, P], R],
3967
) -> Callable[Concatenate[str, P], R]:
68+
"""
69+
Decorator for defining a Shiny module server function.
70+
71+
This decorator is used to encapsulate the server logic for a Shiny module.
72+
It automatically creates a namespaced child `Session` using the provided module `id`,
73+
and passes the appropriate `input`, `output`, and `session` objects to your server function.
74+
75+
This ensures that the server logic is scoped correctly for each module instance and
76+
allows for reuse of logic across multiple instances of the same module.
77+
78+
Parameters
79+
----------
80+
fn
81+
A server function that takes `input`, `output`, and `session` as its first
82+
three arguments, followed by any additional arguments defined by the user.
83+
84+
Returns
85+
-------
86+
:
87+
A function that takes a module `id` (as a string) as its first argument,
88+
followed by any arguments expected by `fn`. When called, it will register
89+
the module's server logic in a namespaced context.
90+
91+
See Also
92+
--------
93+
* Shiny Modules documentation: <https://shiny.posit.co/py/docs/modules.html>
94+
* :func:`shiny.module.ui`
95+
"""
4096
from .session import require_active_session, session_context
4197

4298
def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
4399
sess = require_active_session(None)
44100
child_sess = sess.make_scope(id)
45101
with session_context(child_sess):
46-
return fn(child_sess.input, child_sess.output, child_sess, *args, **kwargs)
102+
return fn(
103+
child_sess.input,
104+
child_sess.output,
105+
child_sess,
106+
*args,
107+
**kwargs,
108+
)
47109

48110
return wrapper

0 commit comments

Comments
 (0)