Skip to content

Commit e81b10a

Browse files
committed
docstring for core shiny.modules.ui and shiny.module.server
1 parent 646e69f commit e81b10a

File tree

1 file changed

+148
-1
lines changed

1 file changed

+148
-1
lines changed

shiny/module.py

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,76 @@
2626

2727
@no_example()
2828
def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]:
29+
"""Decorator for defining a Shiny module UI function.
30+
31+
This decorator allows you to write the UI portion of a Shiny module.
32+
This enables reuse of UI components and consistent input/output handling
33+
when paired with a `@module.server` function.
34+
35+
Parameters
36+
----------
37+
fn : Callable[..., R]
38+
A function that returns a Shiny UI element or layout (e.g., a `ui.panel_*` component).
39+
This function should **not** accept an `id` parameter itself; the decorator injects it.
40+
41+
Returns
42+
-------
43+
Callable[[str, ...], R]
44+
A function that takes a `str` `id` as its first argument, followed by any additional
45+
parameters accepted by `fn`. When called, it returns UI elements with input/output
46+
IDs automatically namespaced using the provided module `id`.
47+
48+
49+
Example
50+
-------
51+
52+
```python
53+
from shiny import App, module, reactive, render, ui
54+
55+
56+
@module.ui
57+
def counter_ui(label: str = "Increment counter") -> ui.TagChild:
58+
return ui.card(
59+
ui.h2("This is " + label),
60+
ui.input_action_button(id="button", label=label),
61+
ui.output_code(id="out"),
62+
)
63+
64+
65+
@module.server
66+
def counter_server(input, output, session, starting_value: int = 0):
67+
count: reactive.value[int] = reactive.value(starting_value)
68+
69+
@reactive.effect
70+
@reactive.event(input.button)
71+
def _():
72+
count.set(count() + 1)
73+
74+
@render.code
75+
def out() -> str:
76+
return f"Click count is {count()}"
77+
78+
79+
app_ui = ui.page_fluid(
80+
counter_ui("counter1", "Counter 1"),
81+
counter_ui("counter2", "Counter 2"),
82+
)
83+
84+
85+
def server(input, output, session):
86+
counter_server("counter1")
87+
counter_server("counter2")
88+
89+
90+
app = App(app_ui, server)
91+
```
92+
93+
See Also
94+
--------
95+
Shiny Modules documentation:
96+
https://shiny.posit.co/py/docs/modules.html
97+
"""
98+
2999
def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
30100
with namespace_context(id):
31101
return fn(*args, **kwargs)
@@ -37,12 +107,89 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
37107
def server(
38108
fn: Callable[Concatenate[Inputs, Outputs, Session, P], R],
39109
) -> Callable[Concatenate[str, P], R]:
110+
"""Decorator for defining a Shiny module server function.
111+
112+
This decorator is used to encapsulate the server logic for a Shiny module.
113+
It automatically creates a namespaced child `Session` using the provided module `id`,
114+
and passes the appropriate `input`, `output`, and `session` objects to your server function.
115+
116+
This ensures that the server logic is scoped correctly for each module instance and
117+
allows for reuse of logic across multiple instances of the same module.
118+
119+
Parameters
120+
----------
121+
fn : Callable[[Inputs, Outputs, Session, ...], R]
122+
A server function that takes `input`, `output`, and `session` as its first
123+
three arguments, followed by any additional arguments defined by the user.
124+
125+
Returns
126+
-------
127+
Callable[[str, ...], R]
128+
A function that takes a module `id` (as a string) as its first argument,
129+
followed by any arguments expected by `fn`. When called, it will register
130+
the module's server logic in a namespaced context.
131+
132+
Example
133+
-------
134+
135+
```python
136+
from shiny import App, module, reactive, render, ui
137+
138+
139+
@module.ui
140+
def counter_ui(label: str = "Increment counter") -> ui.TagChild:
141+
return ui.card(
142+
ui.h2("This is " + label),
143+
ui.input_action_button(id="button", label=label),
144+
ui.output_code(id="out"),
145+
)
146+
147+
148+
@module.server
149+
def counter_server(input, output, session, starting_value: int = 0):
150+
count: reactive.value[int] = reactive.value(starting_value)
151+
152+
@reactive.effect
153+
@reactive.event(input.button)
154+
def _():
155+
count.set(count() + 1)
156+
157+
@render.code
158+
def out() -> str:
159+
return f"Click count is {count()}"
160+
161+
162+
app_ui = ui.page_fluid(
163+
counter_ui("counter1", "Counter 1"),
164+
counter_ui("counter2", "Counter 2"),
165+
)
166+
167+
168+
def server(input, output, session):
169+
counter_server("counter1")
170+
counter_server("counter2")
171+
172+
173+
app = App(app_ui, server)
174+
```
175+
176+
See Also
177+
--------
178+
Shiny Modules documentation:
179+
https://shiny.posit.co/py/docs/modules.html
180+
"""
40181
from .session import require_active_session, session_context
41182

42183
def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
43184
sess = require_active_session(None)
44185
child_sess = sess.make_scope(id)
45186
with session_context(child_sess):
46-
return fn(child_sess.input, child_sess.output, child_sess, *args, **kwargs)
187+
return fn(
188+
child_sess.input,
189+
child_sess.output,
190+
child_sess,
191+
*args,
192+
**kwargs,
193+
)
47194

48195
return wrapper

0 commit comments

Comments
 (0)