Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e81b10a
docstring for core shiny.modules.ui and shiny.module.server
chendaniely Apr 14, 2025
a577ab7
add link to correspoing module function
chendaniely Apr 14, 2025
18d6d9c
extra statement about ui function and id namespacing
chendaniely Apr 14, 2025
efb815e
remove no_example()
chendaniely Apr 14, 2025
d8a9b94
use @add_example(ex_dir="../api-examples/Module")
chendaniely Apr 14, 2025
c39de9b
move, edit, add examples for core module.py docs (with express example)
chendaniely Apr 16, 2025
8b5ac9f
docstring for core shiny.modules.ui and shiny.module.server
chendaniely Apr 14, 2025
7babeea
add link to correspoing module function
chendaniely Apr 14, 2025
ec17f94
extra statement about ui function and id namespacing
chendaniely Apr 14, 2025
c901f4f
remove no_example()
chendaniely Apr 14, 2025
b78eef5
use @add_example(ex_dir="../api-examples/Module")
chendaniely Apr 14, 2025
24cf775
move, edit, add examples for core module.py docs (with express example)
chendaniely Apr 16, 2025
328de17
add missing function import
chendaniely Apr 16, 2025
4c9f450
api-examples/Module folder has core and express apps
chendaniely Apr 18, 2025
607cfb2
docstring fixes
chendaniely Apr 18, 2025
c86a827
fix conflict
chendaniely Apr 25, 2025
572c992
fix conflict
chendaniely Apr 25, 2025
0a985b2
fix rendering issues and links
chendaniely Apr 25, 2025
7605f52
Update shiny/module.py
chendaniely Apr 28, 2025
50f62b8
Update shiny/module.py
chendaniely Apr 28, 2025
381764f
Update shiny/api-examples/Module/app-express.py
chendaniely Apr 28, 2025
94a9024
use :func: and :func: to show links
chendaniely Apr 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion shiny/api-examples/Module/app-core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
return ui.card(
ui.h2("This is " + label),
ui.input_action_button(id="button", label=label),
ui.output_text_verbatim(id="out"),
ui.output_text(id="out"),
)


Expand Down
30 changes: 30 additions & 0 deletions shiny/api-examples/Module/app-express.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from shiny import reactive
from shiny.express import module, render, ui


# ============================================================
# Counter module
# ============================================================
@module
def counter(input, output, session, label, starting_value: int = 0):
count = reactive.value(starting_value)
with ui.card():
ui.h2(f"This is {label}")
ui.input_action_button("button", f"{label}")

@render.text
def out():
return f"Click count is {count()}"

@reactive.effect
@reactive.event(input.button)
def _():
count.set(count() + 1)


# =============================================================================
# App that uses module
# =============================================================================
counter("counter1", "Counter 1", starting_value=0)
ui.hr()
counter("counter2", "Counter 2", starting_value=0)
25 changes: 0 additions & 25 deletions shiny/api-examples/express_module/app-express.py

This file was deleted.

2 changes: 1 addition & 1 deletion shiny/express/_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
__all__ = ("module",)


@add_example(ex_dir="../api-examples/express_module")
@add_example(ex_dir="../api-examples/Module")
def module(
fn: Callable[Concatenate[Inputs, Outputs, Session, P], R],
) -> Callable[Concatenate[Id, P], R]:
Expand Down
70 changes: 66 additions & 4 deletions shiny/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from typing import TYPE_CHECKING, Callable, TypeVar

from ._docstring import no_example
from ._docstring import add_example
from ._namespaces import (
Id,
ResolvedId,
Expand All @@ -24,25 +24,87 @@
_: Id # type: ignore


@no_example()
@add_example(ex_dir="api-examples/Module")
def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]:
"""
Decorator for defining a Shiny module UI function.

This decorator allows you to write the UI portion of a Shiny module.
When your decorated `ui` function is called with an `id`,
the UI elements defined within will automatically be namespaced using that `id`.
This enables reuse of UI components and consistent input/output handling
when paired with a :func:`shiny.module.server` function.

Parameters
----------
fn
A function that returns a Shiny UI element or layout (e.g., a `ui.panel_*` component).
This function should **not** accept an `id` parameter itself; the decorator injects it.

Returns
-------
:
A function that takes a `str` `id` as its first argument, followed by any additional
parameters accepted by `fn`. When called, it returns UI elements with input/output
IDs automatically namespaced using the provided module `id`.

See Also
--------
* Shiny Modules documentation: <https://shiny.posit.co/py/docs/modules.html>
* :func:`shiny.module.server`
"""

def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
with namespace_context(id):
return fn(*args, **kwargs)

return wrapper


@no_example()
@add_example(ex_dir="api-examples/Module")
def server(
fn: Callable[Concatenate[Inputs, Outputs, Session, P], R],
) -> Callable[Concatenate[str, P], R]:
"""
Decorator for defining a Shiny module server function.

This decorator is used to encapsulate the server logic for a Shiny module.
It automatically creates a namespaced child `Session` using the provided module `id`,
and passes the appropriate `input`, `output`, and `session` objects to your server function.

This ensures that the server logic is scoped correctly for each module instance and
allows for reuse of logic across multiple instances of the same module.

Parameters
----------
fn
A server function that takes `input`, `output`, and `session` as its first
three arguments, followed by any additional arguments defined by the user.

Returns
-------
:
A function that takes a module `id` (as a string) as its first argument,
followed by any arguments expected by `fn`. When called, it will register
the module's server logic in a namespaced context.

See Also
--------
* Shiny Modules documentation: <https://shiny.posit.co/py/docs/modules.html>
* :func:`shiny.module.ui`
"""
from .session import require_active_session, session_context

def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R:
sess = require_active_session(None)
child_sess = sess.make_scope(id)
with session_context(child_sess):
return fn(child_sess.input, child_sess.output, child_sess, *args, **kwargs)
return fn(
child_sess.input,
child_sess.output,
child_sess,
*args,
**kwargs,
)

return wrapper
Loading