From e81b10ac4d6043daaffa620f39372359524601b8 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 09:25:27 -0700 Subject: [PATCH 01/21] docstring for core shiny.modules.ui and shiny.module.server --- shiny/module.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/shiny/module.py b/shiny/module.py index a47e52e5ee..18a0ea58f0 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -26,6 +26,76 @@ @no_example() 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. + This enables reuse of UI components and consistent input/output handling + when paired with a `@module.server` function. + + Parameters + ---------- + fn : Callable[..., R] + 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 + ------- + Callable[[str, ...], R] + 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`. + + + Example + ------- + + ```python + from shiny import App, module, reactive, render, ui + + + @module.ui + 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_code(id="out"), + ) + + + @module.server + def counter_server(input, output, session, starting_value: int = 0): + count: reactive.value[int] = reactive.value(starting_value) + + @reactive.effect + @reactive.event(input.button) + def _(): + count.set(count() + 1) + + @render.code + def out() -> str: + return f"Click count is {count()}" + + + app_ui = ui.page_fluid( + counter_ui("counter1", "Counter 1"), + counter_ui("counter2", "Counter 2"), + ) + + + def server(input, output, session): + counter_server("counter1") + counter_server("counter2") + + + app = App(app_ui, server) + ``` + + See Also + -------- + Shiny Modules documentation: + https://shiny.posit.co/py/docs/modules.html + """ + def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: with namespace_context(id): return fn(*args, **kwargs) @@ -37,12 +107,89 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: 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 : Callable[[Inputs, Outputs, Session, ...], R] + A server function that takes `input`, `output`, and `session` as its first + three arguments, followed by any additional arguments defined by the user. + + Returns + ------- + Callable[[str, ...], R] + 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. + + Example + ------- + + ```python + from shiny import App, module, reactive, render, ui + + + @module.ui + 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_code(id="out"), + ) + + + @module.server + def counter_server(input, output, session, starting_value: int = 0): + count: reactive.value[int] = reactive.value(starting_value) + + @reactive.effect + @reactive.event(input.button) + def _(): + count.set(count() + 1) + + @render.code + def out() -> str: + return f"Click count is {count()}" + + + app_ui = ui.page_fluid( + counter_ui("counter1", "Counter 1"), + counter_ui("counter2", "Counter 2"), + ) + + + def server(input, output, session): + counter_server("counter1") + counter_server("counter2") + + + app = App(app_ui, server) + ``` + + See Also + -------- + Shiny Modules documentation: + https://shiny.posit.co/py/docs/modules.html + """ 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 From a577ab7d0bfd0e47007bfd46d43c55cd77965f9d Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 09:50:44 -0700 Subject: [PATCH 02/21] add link to correspoing module function --- shiny/module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 18a0ea58f0..4886f9dd53 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -92,8 +92,8 @@ def server(input, output, session): See Also -------- - Shiny Modules documentation: - https://shiny.posit.co/py/docs/modules.html + * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html + * ~shiny.module.server """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: @@ -175,8 +175,8 @@ def server(input, output, session): See Also -------- - Shiny Modules documentation: - https://shiny.posit.co/py/docs/modules.html + * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html + * ~shiny.module.ui """ from .session import require_active_session, session_context From 18d6d9ce30f90011b16bcb893379d8b86caea31f Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 10:14:40 -0700 Subject: [PATCH 03/21] extra statement about ui function and id namespacing --- shiny/module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shiny/module.py b/shiny/module.py index 4886f9dd53..122a5973c6 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -29,6 +29,8 @@ 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 `@module.server` function. From efb815e1f6f894656ccf6c7275649c37dedecf1e Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 10:15:16 -0700 Subject: [PATCH 04/21] remove no_example() --- shiny/module.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 122a5973c6..b2e1f2c630 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -24,7 +24,6 @@ _: Id # type: ignore -@no_example() def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: """Decorator for defining a Shiny module UI function. @@ -105,7 +104,6 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper -@no_example() def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: From d8a9b947103d62448f4a0574cde09c708e5bcb07 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 14:54:08 -0700 Subject: [PATCH 05/21] use @add_example(ex_dir="../api-examples/Module") --- shiny/module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shiny/module.py b/shiny/module.py index b2e1f2c630..4e872944f6 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -24,6 +24,7 @@ _: Id # type: ignore +@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. @@ -104,6 +105,7 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper +@add_example(ex_dir="../api-examples/Module") def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: From c39de9bd3d59add0cb5ec3ac08a188ca15ae8784 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Wed, 16 Apr 2025 09:58:01 -0700 Subject: [PATCH 06/21] move, edit, add examples for core module.py docs (with express example) --- shiny/api-examples/Module/app-core.py | 2 +- shiny/api-examples/Module/app-express.py | 31 +++++++++ shiny/module.py | 89 ------------------------ 3 files changed, 32 insertions(+), 90 deletions(-) create mode 100644 shiny/api-examples/Module/app-express.py diff --git a/shiny/api-examples/Module/app-core.py b/shiny/api-examples/Module/app-core.py index e5fe83d20e..3c4e8d5f5f 100644 --- a/shiny/api-examples/Module/app-core.py +++ b/shiny/api-examples/Module/app-core.py @@ -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"), ) diff --git a/shiny/api-examples/Module/app-express.py b/shiny/api-examples/Module/app-express.py new file mode 100644 index 0000000000..ffa4ea3232 --- /dev/null +++ b/shiny/api-examples/Module/app-express.py @@ -0,0 +1,31 @@ +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}") + + with ui.div(): + @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) diff --git a/shiny/module.py b/shiny/module.py index 4e872944f6..194e2e949b 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -47,51 +47,6 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: parameters accepted by `fn`. When called, it returns UI elements with input/output IDs automatically namespaced using the provided module `id`. - - Example - ------- - - ```python - from shiny import App, module, reactive, render, ui - - - @module.ui - 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_code(id="out"), - ) - - - @module.server - def counter_server(input, output, session, starting_value: int = 0): - count: reactive.value[int] = reactive.value(starting_value) - - @reactive.effect - @reactive.event(input.button) - def _(): - count.set(count() + 1) - - @render.code - def out() -> str: - return f"Click count is {count()}" - - - app_ui = ui.page_fluid( - counter_ui("counter1", "Counter 1"), - counter_ui("counter2", "Counter 2"), - ) - - - def server(input, output, session): - counter_server("counter1") - counter_server("counter2") - - - app = App(app_ui, server) - ``` - See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html @@ -131,50 +86,6 @@ def server( followed by any arguments expected by `fn`. When called, it will register the module's server logic in a namespaced context. - Example - ------- - - ```python - from shiny import App, module, reactive, render, ui - - - @module.ui - 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_code(id="out"), - ) - - - @module.server - def counter_server(input, output, session, starting_value: int = 0): - count: reactive.value[int] = reactive.value(starting_value) - - @reactive.effect - @reactive.event(input.button) - def _(): - count.set(count() + 1) - - @render.code - def out() -> str: - return f"Click count is {count()}" - - - app_ui = ui.page_fluid( - counter_ui("counter1", "Counter 1"), - counter_ui("counter2", "Counter 2"), - ) - - - def server(input, output, session): - counter_server("counter1") - counter_server("counter2") - - - app = App(app_ui, server) - ``` - See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html From 8b5ac9fc463bbd6095520dfd69e4c458724e6792 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 09:25:27 -0700 Subject: [PATCH 07/21] docstring for core shiny.modules.ui and shiny.module.server --- shiny/module.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/shiny/module.py b/shiny/module.py index a47e52e5ee..18a0ea58f0 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -26,6 +26,76 @@ @no_example() 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. + This enables reuse of UI components and consistent input/output handling + when paired with a `@module.server` function. + + Parameters + ---------- + fn : Callable[..., R] + 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 + ------- + Callable[[str, ...], R] + 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`. + + + Example + ------- + + ```python + from shiny import App, module, reactive, render, ui + + + @module.ui + 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_code(id="out"), + ) + + + @module.server + def counter_server(input, output, session, starting_value: int = 0): + count: reactive.value[int] = reactive.value(starting_value) + + @reactive.effect + @reactive.event(input.button) + def _(): + count.set(count() + 1) + + @render.code + def out() -> str: + return f"Click count is {count()}" + + + app_ui = ui.page_fluid( + counter_ui("counter1", "Counter 1"), + counter_ui("counter2", "Counter 2"), + ) + + + def server(input, output, session): + counter_server("counter1") + counter_server("counter2") + + + app = App(app_ui, server) + ``` + + See Also + -------- + Shiny Modules documentation: + https://shiny.posit.co/py/docs/modules.html + """ + def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: with namespace_context(id): return fn(*args, **kwargs) @@ -37,12 +107,89 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: 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 : Callable[[Inputs, Outputs, Session, ...], R] + A server function that takes `input`, `output`, and `session` as its first + three arguments, followed by any additional arguments defined by the user. + + Returns + ------- + Callable[[str, ...], R] + 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. + + Example + ------- + + ```python + from shiny import App, module, reactive, render, ui + + + @module.ui + 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_code(id="out"), + ) + + + @module.server + def counter_server(input, output, session, starting_value: int = 0): + count: reactive.value[int] = reactive.value(starting_value) + + @reactive.effect + @reactive.event(input.button) + def _(): + count.set(count() + 1) + + @render.code + def out() -> str: + return f"Click count is {count()}" + + + app_ui = ui.page_fluid( + counter_ui("counter1", "Counter 1"), + counter_ui("counter2", "Counter 2"), + ) + + + def server(input, output, session): + counter_server("counter1") + counter_server("counter2") + + + app = App(app_ui, server) + ``` + + See Also + -------- + Shiny Modules documentation: + https://shiny.posit.co/py/docs/modules.html + """ 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 From 7babeea76b38ba274bb678bba9e5a3038b84e4e1 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 09:50:44 -0700 Subject: [PATCH 08/21] add link to correspoing module function --- shiny/module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 18a0ea58f0..4886f9dd53 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -92,8 +92,8 @@ def server(input, output, session): See Also -------- - Shiny Modules documentation: - https://shiny.posit.co/py/docs/modules.html + * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html + * ~shiny.module.server """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: @@ -175,8 +175,8 @@ def server(input, output, session): See Also -------- - Shiny Modules documentation: - https://shiny.posit.co/py/docs/modules.html + * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html + * ~shiny.module.ui """ from .session import require_active_session, session_context From ec17f9426a421523de20e266e280b2423b20db18 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 10:14:40 -0700 Subject: [PATCH 09/21] extra statement about ui function and id namespacing --- shiny/module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shiny/module.py b/shiny/module.py index 4886f9dd53..122a5973c6 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -29,6 +29,8 @@ 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 `@module.server` function. From c901f4f5f537b098df1a286a9ceb9e5e23082f6b Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 10:15:16 -0700 Subject: [PATCH 10/21] remove no_example() --- shiny/module.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 122a5973c6..b2e1f2c630 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -24,7 +24,6 @@ _: Id # type: ignore -@no_example() def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: """Decorator for defining a Shiny module UI function. @@ -105,7 +104,6 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper -@no_example() def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: From b78eef5061ebfd063d5b26f564ab1f0b44d69ff6 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 14 Apr 2025 14:54:08 -0700 Subject: [PATCH 11/21] use @add_example(ex_dir="../api-examples/Module") --- shiny/module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shiny/module.py b/shiny/module.py index b2e1f2c630..4e872944f6 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -24,6 +24,7 @@ _: Id # type: ignore +@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. @@ -104,6 +105,7 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper +@add_example(ex_dir="../api-examples/Module") def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: From 24cf775bcec8a321cd69dc24ed11c9983eb6356b Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Wed, 16 Apr 2025 09:58:01 -0700 Subject: [PATCH 12/21] move, edit, add examples for core module.py docs (with express example) --- shiny/api-examples/Module/app-core.py | 2 +- shiny/api-examples/Module/app-express.py | 31 +++++++++ shiny/module.py | 89 ------------------------ 3 files changed, 32 insertions(+), 90 deletions(-) create mode 100644 shiny/api-examples/Module/app-express.py diff --git a/shiny/api-examples/Module/app-core.py b/shiny/api-examples/Module/app-core.py index e5fe83d20e..3c4e8d5f5f 100644 --- a/shiny/api-examples/Module/app-core.py +++ b/shiny/api-examples/Module/app-core.py @@ -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"), ) diff --git a/shiny/api-examples/Module/app-express.py b/shiny/api-examples/Module/app-express.py new file mode 100644 index 0000000000..ffa4ea3232 --- /dev/null +++ b/shiny/api-examples/Module/app-express.py @@ -0,0 +1,31 @@ +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}") + + with ui.div(): + @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) diff --git a/shiny/module.py b/shiny/module.py index 4e872944f6..194e2e949b 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -47,51 +47,6 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: parameters accepted by `fn`. When called, it returns UI elements with input/output IDs automatically namespaced using the provided module `id`. - - Example - ------- - - ```python - from shiny import App, module, reactive, render, ui - - - @module.ui - 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_code(id="out"), - ) - - - @module.server - def counter_server(input, output, session, starting_value: int = 0): - count: reactive.value[int] = reactive.value(starting_value) - - @reactive.effect - @reactive.event(input.button) - def _(): - count.set(count() + 1) - - @render.code - def out() -> str: - return f"Click count is {count()}" - - - app_ui = ui.page_fluid( - counter_ui("counter1", "Counter 1"), - counter_ui("counter2", "Counter 2"), - ) - - - def server(input, output, session): - counter_server("counter1") - counter_server("counter2") - - - app = App(app_ui, server) - ``` - See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html @@ -131,50 +86,6 @@ def server( followed by any arguments expected by `fn`. When called, it will register the module's server logic in a namespaced context. - Example - ------- - - ```python - from shiny import App, module, reactive, render, ui - - - @module.ui - 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_code(id="out"), - ) - - - @module.server - def counter_server(input, output, session, starting_value: int = 0): - count: reactive.value[int] = reactive.value(starting_value) - - @reactive.effect - @reactive.event(input.button) - def _(): - count.set(count() + 1) - - @render.code - def out() -> str: - return f"Click count is {count()}" - - - app_ui = ui.page_fluid( - counter_ui("counter1", "Counter 1"), - counter_ui("counter2", "Counter 2"), - ) - - - def server(input, output, session): - counter_server("counter1") - counter_server("counter2") - - - app = App(app_ui, server) - ``` - See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html From 328de17807e9120760e97f5b36103f254b7961ba Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Wed, 16 Apr 2025 09:59:38 -0700 Subject: [PATCH 13/21] add missing function import --- shiny/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/module.py b/shiny/module.py index 194e2e949b..0772505f22 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -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, From 4c9f45003b5da4e733d82852c85c12bacab42021 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Fri, 18 Apr 2025 14:18:43 -0700 Subject: [PATCH 14/21] api-examples/Module folder has core and express apps --- .../express_module/app-express.py | 25 ------------------- shiny/express/_module.py | 2 +- 2 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 shiny/api-examples/express_module/app-express.py diff --git a/shiny/api-examples/express_module/app-express.py b/shiny/api-examples/express_module/app-express.py deleted file mode 100644 index 75550445d8..0000000000 --- a/shiny/api-examples/express_module/app-express.py +++ /dev/null @@ -1,25 +0,0 @@ -from shiny import reactive -from shiny.express import module, render, ui - - -@module -def counter(input, output, session, starting_value: int = 0): - count = reactive.value(starting_value) - - ui.input_action_button("btn", "Increment") - - with ui.div(): - - @render.express - def current_count(): - count() - - @reactive.effect - @reactive.event(input.btn) - def increment(): - count.set(count() + 1) - - -counter("one") -ui.hr() -counter("two") diff --git a/shiny/express/_module.py b/shiny/express/_module.py index 9036e287a8..7791568746 100644 --- a/shiny/express/_module.py +++ b/shiny/express/_module.py @@ -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]: From 607cfb22a4368541069443a1f97db5330cd7f893 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Fri, 18 Apr 2025 14:20:07 -0700 Subject: [PATCH 15/21] docstring fixes --- shiny/module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 0772505f22..241a2a66bc 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -24,7 +24,7 @@ _: Id # type: ignore -@add_example(ex_dir="../api-examples/Module") +@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. @@ -32,7 +32,7 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: 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 `@module.server` function. + when paired with a :func:`~shiny.module.server()` function. Parameters ---------- @@ -50,7 +50,7 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html - * ~shiny.module.server + * :func:`~shiny.module.server` """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: @@ -60,7 +60,7 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper -@add_example(ex_dir="../api-examples/Module") +@add_example(ex_dir="api-examples/Module") def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: From 572c99251c342d14b60c4350c9c1ef00a5341daf Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Thu, 24 Apr 2025 19:05:50 -0700 Subject: [PATCH 16/21] fix conflict --- shiny/module.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 8aa319c6c7..241a2a66bc 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -50,11 +50,7 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: See Also -------- * Shiny Modules documentation: https://shiny.posit.co/py/docs/modules.html -<<<<<<< HEAD * :func:`~shiny.module.server` -======= - * ~shiny.module.server ->>>>>>> c39de9bd3d59add0cb5ec3ac08a188ca15ae8784 """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: From 0a985b219a6ea9a702b31bf9bb46938843b1d5cf Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Thu, 24 Apr 2025 20:06:16 -0700 Subject: [PATCH 17/21] fix rendering issues and links --- shiny/module.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index 241a2a66bc..a7ec461664 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -26,31 +26,32 @@ @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. + """ + 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. + when paired with a :func:`shiny.module.server` function. Parameters ---------- - fn : Callable[..., R] + 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 ------- - Callable[[str, ...], R] + : 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` + * Shiny Modules documentation: + * ~shiny.module.server """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: @@ -64,7 +65,8 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R], ) -> Callable[Concatenate[str, P], R]: - """Decorator for defining a Shiny module server function. + """ + 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`, @@ -75,20 +77,20 @@ def server( Parameters ---------- - fn : Callable[[Inputs, Outputs, Session, ...], R] + 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 ------- - Callable[[str, ...], R] + : 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 + * Shiny Modules documentation: * ~shiny.module.ui """ from .session import require_active_session, session_context From 7605f52b8553dc97dcc1ed262ddebaae6e5c96bd Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 28 Apr 2025 16:33:00 -0700 Subject: [PATCH 18/21] Update shiny/module.py Co-authored-by: Carson Sievert --- shiny/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/module.py b/shiny/module.py index a7ec461664..9b3181bd0b 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -51,7 +51,7 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: See Also -------- * Shiny Modules documentation: - * ~shiny.module.server + * shiny.module.server """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: From 50f62b89ff938a6c7f01a7b6b480af35500306e5 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 28 Apr 2025 16:33:08 -0700 Subject: [PATCH 19/21] Update shiny/module.py Co-authored-by: Carson Sievert --- shiny/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/module.py b/shiny/module.py index 9b3181bd0b..e60e66d33a 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -91,7 +91,7 @@ def server( See Also -------- * Shiny Modules documentation: - * ~shiny.module.ui + * shiny.module.ui """ from .session import require_active_session, session_context From 381764f0d624a417e0712fa51061ca0169184510 Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 28 Apr 2025 16:33:44 -0700 Subject: [PATCH 20/21] Update shiny/api-examples/Module/app-express.py Co-authored-by: Carson Sievert --- shiny/api-examples/Module/app-express.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/shiny/api-examples/Module/app-express.py b/shiny/api-examples/Module/app-express.py index ffa4ea3232..57e16dbfe1 100644 --- a/shiny/api-examples/Module/app-express.py +++ b/shiny/api-examples/Module/app-express.py @@ -12,10 +12,9 @@ def counter(input, output, session, label, starting_value: int = 0): ui.h2(f"This is {label}") ui.input_action_button("button", f"{label}") - with ui.div(): - @render.text - def out(): - return f"Click count is {count()}" + @render.text + def out(): + return f"Click count is {count()}" @reactive.effect @reactive.event(input.button) From 94a90245bf33c23b9f6ed18e8a80c9629c24466f Mon Sep 17 00:00:00 2001 From: Daniel Chen Date: Mon, 28 Apr 2025 17:09:40 -0700 Subject: [PATCH 21/21] use :func: and :func: to show links --- shiny/module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shiny/module.py b/shiny/module.py index e60e66d33a..3ebbbc4a20 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -51,7 +51,7 @@ def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: See Also -------- * Shiny Modules documentation: - * shiny.module.server + * :func:`shiny.module.server` """ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: @@ -91,7 +91,7 @@ def server( See Also -------- * Shiny Modules documentation: - * shiny.module.ui + * :func:`shiny.module.ui` """ from .session import require_active_session, session_context