diff --git a/docs/.gitignore b/docs/.gitignore index a3135790a..b65bacee6 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -5,3 +5,5 @@ _sidebar.yml /.quarto/ objects.json site_libs/ +_objects_core.json +_objects_express.json diff --git a/docs/Makefile b/docs/Makefile index 583ca792c..2de2d63cf 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -46,12 +46,39 @@ deps: $(PYBIN) ## Install build dependencies $(PYBIN)/pip install pip --upgrade $(PYBIN)/pip install -e ..[doc] -quartodoc: $(PYBIN) ## Build qmd files for API docs +quartodoc: quartodoc_build_core quartodoc_build_express quartodoc_post ## Build quartodocs for express and core + +## Build interlinks for API docs +quartodoc_interlinks: $(PYBIN) + . $(PYBIN)/activate \ + && quartodoc interlinks + +## Build core API docs +quartodoc_build_core: $(PYBIN) quartodoc_interlinks $(eval export SHINY_ADD_EXAMPLES=true) $(eval export IN_QUARTODOC=true) + $(eval export SHINY_MODE=core) + . $(PYBIN)/activate \ + && echo "::group::quartodoc build core docs" \ + && quartodoc build --config _quartodoc-core.yml --verbose \ + && mv objects.json _objects_core.json \ + && echo "::endgroup::" + +## Build express API docs +quartodoc_build_express: $(PYBIN) quartodoc_interlinks + $(eval export SHINY_ADD_EXAMPLES=true) + $(eval export IN_QUARTODOC=true) + $(eval export SHINY_MODE=express) + . $(PYBIN)/activate \ + && echo "::group::quartodoc build express docs" \ + && quartodoc build --config _quartodoc-express.yml --verbose \ + && mv objects.json _objects_express.json \ + && echo "::endgroup::" + +## Clean up after quartodoc build +quartodoc_post: $(PYBIN) . $(PYBIN)/activate \ - && quartodoc interlinks \ - && quartodoc build --config _quartodoc.yml --verbose + && python _combine_objects_json.py site: ## Build website . $(PYBIN)/activate \ diff --git a/docs/_combine_objects_json.py b/docs/_combine_objects_json.py new file mode 100644 index 000000000..fbb2a2215 --- /dev/null +++ b/docs/_combine_objects_json.py @@ -0,0 +1,64 @@ +import json +from dataclasses import asdict, dataclass +from typing import Literal, TypedDict + + +@dataclass +class QuartodocObject: + project: str + version: str + count: int + items: list["QuartodocObjectItem"] + + +@dataclass +class QuartodocObjectItem: + name: str + domain: str + + # function: "shiny.ui.page_sidebar" + # class: "shiny.render.renderer._renderer.Renderer" + # attribute: "shiny.render.renderer._renderer.Renderer.output_id" + role: Literal["function", "class", "attribute", "module"] + priority: str + uri: str + dispname: str + + +def read_objects_file(path: str) -> QuartodocObject: + with open(path) as file: + content = json.load(file) + items = [QuartodocObjectItem(**item) for item in content.pop("items")] + return QuartodocObject(**content, items=items) + + +def write_objects_file(objects: QuartodocObject, path: str) -> None: + with open(path, "w") as file: + json.dump(objects, file, indent=4, default=lambda dc: dc.__dict__) + + +print("\nCombining objects json files...") +objects_core = read_objects_file("_objects_core.json") +objects_express = read_objects_file("_objects_express.json") + +items_map: dict[str, QuartodocObjectItem] = {} + +for item in [*objects_core.items, *objects_express.items]: + if item.name in items_map: + continue + items_map[item.name] = item + +objects_ret = QuartodocObject( + project="shiny", + version="1", + count=len(items_map.values()), + items=[*items_map.values()], +) + + +print("Core:", objects_core.count) +print("Express:", objects_express.count) +print("Combined:", objects_ret.count) + +# Save combined objects file info +write_objects_file(objects_ret, "objects.json") diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 6da6e7571..2d6b65308 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -18,8 +18,10 @@ website: pinned: true search: true left: - - text: "API" - file: api/index.qmd + - text: "Express API" + file: api/express/index.qmd + - text: "Core API" + file: api/core/index.qmd right: - icon: github href: https://github.com/posit-dev/py-shiny diff --git a/docs/_quartodoc.yml b/docs/_quartodoc-core.yml similarity index 99% rename from docs/_quartodoc.yml rename to docs/_quartodoc-core.yml index 68d24050f..404f8cd97 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc-core.yml @@ -1,10 +1,10 @@ quartodoc: style: pkgdown - dir: api + dir: api/core out_index: index.qmd package: shiny rewrite_all_pages: false - sidebar: api/_sidebar.yml + sidebar: api/core/_sidebar.yml dynamic: true renderer: style: _renderer.py @@ -374,6 +374,7 @@ quartodoc: - ui.panel_main - ui.panel_sidebar - ui.nav + - render.transformer.output_transformer - render.transformer.resolve_value_fn - title: Experimental desc: "These methods are under consideration and are considered unstable. However, if there is a method you are excited about, please let us know!" diff --git a/docs/_quartodoc-express.yml b/docs/_quartodoc-express.yml new file mode 100644 index 000000000..443044fba --- /dev/null +++ b/docs/_quartodoc-express.yml @@ -0,0 +1,177 @@ +quartodoc: + style: pkgdown + dir: api/express + out_index: index.qmd + package: shiny + rewrite_all_pages: false + sidebar: api/express/_sidebar.yml + dynamic: true + renderer: + style: _renderer.py + show_signature_annotations: false + sections: + - title: Input components + desc: Gather user input. + contents: + - express.ui.input_select + - express.ui.input_selectize + - express.ui.input_slider + - express.ui.input_date + - express.ui.input_date_range + - express.ui.input_checkbox + - express.ui.input_checkbox_group + - express.ui.input_switch + - express.ui.input_radio_buttons + - express.ui.input_numeric + - express.ui.input_text + - express.ui.input_text_area + - express.ui.input_password + - express.ui.input_action_button + - express.ui.input_action_link + - title: Output components + desc: Reactively render output. + contents: + - express.render.plot + - express.render.table + - express.render.DataTable + - express.render.data_frame + - express.render.DataGrid + - express.render.text + - express.render.ui + - express.render.download + - express.render.image + - express.render.express + - title: Layouts and other UI tools + desc: Tools for creating, arranging, and styling UI components. + contents: + - express.ui.page_opts + - express.ui.sidebar + - express.ui.layout_columns + - express.ui.layout_column_wrap + - express.ui.card + - express.ui.card_header + - express.ui.card_footer + - express.ui.value_box + - express.ui.value_box_theme + - express.ui.popover + - express.ui.tooltip + - express.ui.accordion + - express.ui.accordion_panel + - express.ui.layout_sidebar + - title: Navigate multiple panels + desc: Create a set of panels that can be navigated between. + contents: + - express.ui.nav_panel + - express.ui.navset_card_underline + - express.ui.navset_card_tab + - express.ui.navset_card_pill + - express.ui.nav_spacer + - express.ui.nav_menu + - express.ui.nav_control + - express.ui.navset_bar + - express.ui.navset_tab + - express.ui.navset_pill + - express.ui.navset_underline + - express.ui.navset_pill_list + - express.ui.navset_hidden + - title: Reactive programming + desc: Create reactive functions and dependencies. + contents: + - reactive.calc + - reactive.effect + - reactive.value + - reactive.event + - reactive.isolate + - reactive.invalidate_later + - reactive.flush + - reactive.poll + - reactive.file_reader + - reactive.lock + - req + - title: Reusable Express code + desc: Create reusable Express code. + contents: + - express.ui.hold + - express.expressify + - title: Update inputs + desc: Programmatically update input values. + contents: + - name: express.ui.update_select + dynamic: true + - name: express.ui.update_selectize + dynamic: true + - name: express.ui.update_slider + dynamic: true + - ui.update_date + - name: express.ui.update_date_range + dynamic: true + - name: express.ui.update_checkbox + dynamic: true + - name: express.ui.update_checkbox_group + dynamic: true + - name: express.ui.update_switch + dynamic: true + - name: express.ui.update_radio_buttons + dynamic: true + - name: express.ui.update_numeric + dynamic: true + - ui.update_text + - name: express.ui.update_text_area + dynamic: "shiny.ui.update_text" + - name: express.ui.update_navs + dynamic: true + - title: Update UI Layouts + desc: "" + contents: + - express.ui.update_sidebar + - express.ui.update_tooltip + - express.ui.update_popover + - express.ui.update_accordion + - express.ui.update_accordion_panel + - express.ui.insert_accordion_panel + - express.ui.remove_accordion_panel + - title: Display messages + desc: Display messages to the user. + contents: + - express.ui.help_text + - express.ui.notification_show + - express.ui.notification_remove + - express.ui.modal + - express.ui.modal_show + - express.ui.modal_remove + - express.ui.modal_button + - express.ui.Progress # uses class.rst + - title: UI panels + desc: Visually group together a section of UI components. + contents: + - express.ui.panel_absolute + - express.ui.panel_fixed + - express.ui.panel_title + - express.ui.panel_well + - title: Dynamic UI + desc: Dynamically show/hide UI elements. + contents: + - express.ui.panel_conditional + - express.ui.insert_ui + - express.ui.remove_ui + - title: UI as HTML + desc: Tools for creating HTML/CSS/JS + contents: + - express.ui.markdown + - express.ui.include_css + - express.ui.include_js + - express.ui.HTML # uses justattributes.rst template + - name: express.ui.tags # uses tags.rst template + children: embedded + - express.ui.TagList # uses class.rst template + # TODO: should these be included? + # - express.ui.fill.as_fillable_container + # - express.ui.fill.as_fill_item + # - express.ui.fill.remove_all_fill + # - express.ui.css.as_css_unit + # - express.ui.css.as_css_padding + - title: Express developer tooling + desc: + contents: + - express.is_express_app + - express.wrap_express_app diff --git a/docs/_renderer.py b/docs/_renderer.py index 47c47f7e5..92930c8ee 100644 --- a/docs/_renderer.py +++ b/docs/_renderer.py @@ -2,6 +2,7 @@ import base64 import html +import os import re from importlib.resources import files from pathlib import Path @@ -30,6 +31,7 @@ class FileContentJson(TypedDict): class Renderer(MdRenderer): style = "shiny" + express_api = os.environ.get("SHINY_MODE", "core") == "express" @dispatch def render(self, el: qast.DocstringSectionSeeAlso): @@ -51,6 +53,24 @@ def render(self, el: Union[dc.Object, dc.Alias]): converted = convert_rst_link_to_md(rendered) + # If we're rendering the API reference for Express, try our best to + # keep you in the Express site. For example, something like shiny.ui.input_text() + # simply gets re-exported as shiny.express.ui.input_text(), but it's docstrings + # will link to shiny.ui, not shiny.express.ui. This fixes that. + if self.express_api: + converted = converted.replace("shiny.ui.", "shiny.express.ui.") + # If this el happens to point to itself, it's probably intentionally + # pointing to Core (i.e., express context managers mention that they + # wrap Core functions), so don't change that. + # TODO: we want to be more aggressive about context managers always + # pointing to the Core docs? + if f"shiny.express.ui.{el.name}" in converted: + print(f"Changing Express link to Core for: {el.name}") + converted = converted.replace( + f"shiny.express.ui.{el.name}", f"shiny.ui.{el.name}" + ) + converted = converted.replace("shiny.render.", "shiny.express.render.") + check_if_missing_expected_example(el, converted) assert_no_sphinx_comments(el, converted) @@ -115,19 +135,30 @@ def summarize(self, obj: Union[dc.Object, dc.Alias]) -> str: ): description = docstring_parts[0].value - # ## Approach: Always return the full description! - return description + # # ## Approach: Always return the full description! + # return description - # ## Alternative: Add ellipsis if the lines are cut off + parts = description.split("\n") + # # Alternative: Add ellipsis if the lines are cut off # # If the description is more than one line, only show the first line. # # Add `...` to indicate the description was truncated - # parts = description.split("\n") # short = parts[0] - # if len(parts) > 1: + # if len(parts) > 1 and parts[1].strip() != "": # short += "…" - # return short + # Alternative: Add take the first paragraph as the description summary + short_parts: list[str] = [] + # Capture the first paragraph (lines until first empty line) + for part in parts: + if part.strip() == "": + break + short_parts.append(part) + + short = " ".join(short_parts) + short = convert_rst_link_to_md(short) + + return short return "" @@ -240,6 +271,10 @@ def read_file(file: str | Path, root_dir: str | Path | None = None) -> FileConte def check_if_missing_expected_example(el, converted): + if os.environ.get("SHINY_MODE", "core") == "express": + # TODO: remove once we are done porting express examples + return + if re.search(r"(^|\n)#{2,6} Examples\n", converted): # Manually added examples are fine return @@ -248,9 +283,15 @@ def check_if_missing_expected_example(el, converted): # Only check Shiny objects for examples return - if hasattr(el, "decorators") and "no_example" in [ - d.value.canonical_name for d in el.decorators - ]: + def is_no_ex_decorator(x): + if x == "no_example()": + return True + + return x == f'no_example("{os.environ.get("SHINY_MODE", "core")}")' + + if hasattr(el, "decorators") and any( + [is_no_ex_decorator(d.value.canonical_name) for d in el.decorators] + ): # When an example is intentionally omitted, we mark the fn with `@no_example` return diff --git a/shiny/_docstring.py b/shiny/_docstring.py index a45a4edce..10281861f 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -2,7 +2,7 @@ import os import sys -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, TypeVar def find_api_examples_dir(start_dir: str) -> Optional[str]: @@ -25,8 +25,29 @@ def find_api_examples_dir(start_dir: str) -> Optional[str]: F = TypeVar("F", bound=FuncType) -def no_example(func: F) -> F: - return func +def no_example(mode: Optional[Literal["express", "core"]] = None) -> Callable[[F], F]: + """ + Prevent ``@add_example()`` from throwing an error about missing examples. + + Parameters + ---------- + mode: + If ``"express"``, ``@add_example()`` will not throw an error if the current + mode is Express. If ``"core"``, ``@add_example()`` will not throw an error if + the current mode is Core. If ``None``, ``@add_example()`` will not throw an + error in either mode. + """ + + def decorator(func: F) -> F: + current = getattr(func, "__no_example", []) + if mode is None: + current.extend(["express", "core"]) + else: + current.append(mode) + setattr(func, "__no_example", current) # noqa: B010 + return func + + return decorator # This class is used to mark docstrings when @add_example() is used, so that an error @@ -50,7 +71,7 @@ def write_example(self, app_files: list[str]) -> str: def add_example( - app_file: str = "app.py", + app_file: Optional[str] = None, ex_dir: Optional[str] = None, ) -> Callable[[F], F]: """ @@ -60,6 +81,16 @@ def add_example( ``__name__`` matches the name of directory under a ``api-examples/`` directory in the current or any parent directory. + * Examples for the ``shiny`` package are in ``shiny/api-examples/``. We also place + Express examples in this directory adjacent to their Core counterparts. + * Examples for the ``shiny.experimental`` subpackage are in + ``shiny/experimental/api-examples/``. + + Functions that can be used in Express or Core and whose canonical implementation is + in the ``shiny`` package should have examples in ``shiny/api-examples``. In this + case, the express variant should include an ``-express`` suffix and the core + variation can be named with a ``-core`` suffix or ``app.py``. + Parameters ---------- app_file: @@ -81,6 +112,10 @@ def _(func: F) -> F: func.__doc__ = DocStringWithExample(func.__doc__) return func + current_mode = os.getenv("SHINY_MODE", "core") + if current_mode in getattr(func, "__no_example", []): + return func + func_dir = get_decorated_source_directory(func) fn_name = func.__name__ @@ -88,25 +123,40 @@ def _(func: F) -> F: ex_dir_found = find_api_examples_dir(func_dir) if ex_dir_found is None: - raise ValueError( + raise FileNotFoundError( f"No example directory found for {fn_name} in {func_dir} or its parent directories." ) example_dir = os.path.join(ex_dir_found, fn_name) else: - example_dir = os.path.join(func_dir, ex_dir) + example_dir = os.path.abspath(os.path.join(func_dir, ex_dir)) - example_file = os.path.join(example_dir, app_file) - if not os.path.exists(example_file): - raise ValueError( - f"No example for {fn_name} found in '{os.path.abspath(example_dir)}'." + if not os.path.exists(example_dir): + raise FileNotFoundError( + f"Example directory '{example_dir}' does not exist for {fn_name}." + ) + + app_file_name = app_file or "app.py" + try: + example_file = app_choose_core_or_express( + os.path.join(example_dir, app_file_name) ) + except ExampleNotFoundException as e: + file = "shiny/" + func_dir.split("shiny/")[1] + if "__code__" in dir(func): + print( + f"::warning file={file},line={func.__code__.co_firstlineno}::{fn_name} - {e}" + ) + else: + print(f"::warning file={file}::{fn_name} - {e}") + + return func other_files: list[str] = [] for f in os.listdir(example_dir): abs_f = os.path.join(example_dir, f) is_support_file = ( os.path.isfile(abs_f) - and f != app_file + and f != app_file_name and f != "app.py" and not f.startswith("app-") and not f.startswith("__") @@ -150,6 +200,86 @@ def _(func: F) -> F: return _ +def is_express_app(app_path: str) -> bool: + # We can't use .shiny.express._is_express.is_express_app() here because that would + # create a circular import. + if not os.path.exists(app_path): + return False + + with open(app_path) as f: + for line in f: + if "from shiny.express" in line: + return True + elif "import shiny.express" in line: + return True + return False + + +class ExampleNotFoundException(FileNotFoundError): + def __init__( + self, + file_names: list[str] | str, + dir: str, + type: Optional[Literal["core", "express"]] = None, + ) -> None: + self.type = type or os.environ.get("SHINY_MODE", "core") + self.file_names = [file_names] if isinstance(file_names, str) else file_names + self.dir = dir + + def __str__(self): + if self.type in ("core", "express"): + # Capitalize first letter + type = "a Shiny Express" if self.type == "express" else "a Shiny Core" + else: + type = "an" + + return ( + f"Could not find {type} example file named " + + f"{' or '.join(self.file_names)} in {self.dir}." + ) + + +class ExpressExampleNotFoundException(ExampleNotFoundException): + def __init__( + self, + file_names: list[str] | str, + dir: str, + ) -> None: + super().__init__(file_names, dir, "express") + + +def app_choose_core_or_express(app_path: Optional[str] = None) -> str: + app_path = app_path or "app.py" + + if os.environ.get("SHINY_MODE") == "express": + if is_express_app(app_path): + return app_path + + app_path = app_path.replace("-core.py", ".py") + + path, ext = os.path.splitext(app_path) + app_path_express = f"{path}-express{ext}" + + if not is_express_app(app_path_express): + raise ExpressExampleNotFoundException( + [os.path.basename(app_path), os.path.basename(app_path_express)], + os.path.dirname(app_path), + ) + + return app_path_express + + if os.path.basename(app_path) == "app.py" and not os.path.exists(app_path): + app_path = app_path.replace("app.py", "app-core.py") + + if not os.path.exists(app_path): + raise ExampleNotFoundException( + os.path.basename(app_path), + os.path.dirname(app_path), + ) + + return app_path + + def get_decorated_source_directory(func: FuncType) -> str: if hasattr(func, "__module__"): path = os.path.abspath(str(sys.modules[func.__module__].__file__)) diff --git a/shiny/_main.py b/shiny/_main.py index 56180606c..caf9212ba 100644 --- a/shiny/_main.py +++ b/shiny/_main.py @@ -143,7 +143,7 @@ def main() -> None: help="Launch app browser after app starts, using the Python webbrowser module.", show_default=True, ) -@no_example +@no_example() def run( app: str | shiny.App, host: str, diff --git a/shiny/api-examples/Module/app.py b/shiny/api-examples/Module/app-core.py similarity index 100% rename from shiny/api-examples/Module/app.py rename to shiny/api-examples/Module/app-core.py diff --git a/shiny/api-examples/Progress/app.py b/shiny/api-examples/Progress/app-core.py similarity index 100% rename from shiny/api-examples/Progress/app.py rename to shiny/api-examples/Progress/app-core.py diff --git a/shiny/api-examples/Progress/app-express.py b/shiny/api-examples/Progress/app-express.py new file mode 100644 index 000000000..b4bde03f6 --- /dev/null +++ b/shiny/api-examples/Progress/app-express.py @@ -0,0 +1,21 @@ +import asyncio + +from shiny import reactive +from shiny.express import input, render, ui + +ui.input_action_button("button", "Compute") + + +@render.text +@reactive.event(input.button) +async def compute(): + with ui.Progress(min=1, max=15) as p: + p.set(message="Calculation in progress", detail="This may take a while...") + + for i in range(1, 15): + p.set(i, message="Computing") + await asyncio.sleep(0.1) + # Normally use time.sleep() instead, but it doesn't yet work in Pyodide. + # https://github.com/pyodide/pyodide/issues/2354 + + return "Done computing!" diff --git a/shiny/api-examples/Renderer/app.py b/shiny/api-examples/Renderer/app-core.py similarity index 100% rename from shiny/api-examples/Renderer/app.py rename to shiny/api-examples/Renderer/app-core.py diff --git a/shiny/api-examples/SafeException/app.py b/shiny/api-examples/SafeException/app-core.py similarity index 100% rename from shiny/api-examples/SafeException/app.py rename to shiny/api-examples/SafeException/app-core.py diff --git a/shiny/api-examples/SilentCancelOutputException/app.py b/shiny/api-examples/SilentCancelOutputException/app-core.py similarity index 100% rename from shiny/api-examples/SilentCancelOutputException/app.py rename to shiny/api-examples/SilentCancelOutputException/app-core.py diff --git a/shiny/api-examples/SilentException/app.py b/shiny/api-examples/SilentException/app-core.py similarity index 100% rename from shiny/api-examples/SilentException/app.py rename to shiny/api-examples/SilentException/app-core.py diff --git a/shiny/api-examples/Value/app.py b/shiny/api-examples/Value/app-core.py similarity index 72% rename from shiny/api-examples/Value/app.py rename to shiny/api-examples/Value/app-core.py index fe0112954..04479492a 100644 --- a/shiny/api-examples/Value/app.py +++ b/shiny/api-examples/Value/app-core.py @@ -1,10 +1,10 @@ from shiny import App, Inputs, Outputs, Session, reactive, render, ui -app_ui = ui.page_fluid( - ui.input_action_button("minus", "-1"), - " ", - ui.input_action_button("plus", "+1"), - ui.br(), +app_ui = ui.page_sidebar( + ui.sidebar( + ui.input_action_button("minus", "-1"), + ui.input_action_button("plus", "+1"), + ), ui.output_text("value"), ) @@ -12,13 +12,13 @@ def server(input: Inputs, output: Outputs, session: Session): val = reactive.Value(0) - @reactive.Effect + @reactive.effect @reactive.event(input.minus) def _(): newVal = val.get() - 1 val.set(newVal) - @reactive.Effect + @reactive.effect @reactive.event(input.plus) def _(): newVal = val.get() + 1 diff --git a/shiny/api-examples/Value/app-express.py b/shiny/api-examples/Value/app-express.py new file mode 100644 index 000000000..80a55e165 --- /dev/null +++ b/shiny/api-examples/Value/app-express.py @@ -0,0 +1,28 @@ +from shiny import reactive +from shiny.express import input, render, ui + +val = reactive.Value(0) + + +@reactive.effect +@reactive.event(input.minus) +def _(): + newVal = val.get() - 1 + val.set(newVal) + + +@reactive.effect +@reactive.event(input.plus) +def _(): + newVal = val.get() + 1 + val.set(newVal) + + +with ui.sidebar(): + ui.input_action_button("minus", "-1") + ui.input_action_button("plus", "+1") + + +@render.text +def value(): + return str(val.get()) diff --git a/shiny/api-examples/accordion/app.py b/shiny/api-examples/accordion/app-core.py similarity index 100% rename from shiny/api-examples/accordion/app.py rename to shiny/api-examples/accordion/app-core.py diff --git a/shiny/api-examples/accordion_panel/app.py b/shiny/api-examples/accordion_panel/app-core.py similarity index 100% rename from shiny/api-examples/accordion_panel/app.py rename to shiny/api-examples/accordion_panel/app-core.py diff --git a/shiny/api-examples/as_fill_item/app.py b/shiny/api-examples/as_fill_item/app-core.py similarity index 100% rename from shiny/api-examples/as_fill_item/app.py rename to shiny/api-examples/as_fill_item/app-core.py diff --git a/shiny/api-examples/as_fillable_container/app.py b/shiny/api-examples/as_fillable_container/app-core.py similarity index 100% rename from shiny/api-examples/as_fillable_container/app.py rename to shiny/api-examples/as_fillable_container/app-core.py diff --git a/shiny/api-examples/calc/app.py b/shiny/api-examples/calc/app-core.py similarity index 64% rename from shiny/api-examples/calc/app.py rename to shiny/api-examples/calc/app-core.py index 603313593..fe2620253 100644 --- a/shiny/api-examples/calc/app.py +++ b/shiny/api-examples/calc/app-core.py @@ -4,16 +4,18 @@ from shiny import App, Inputs, Outputs, Session, reactive, render, ui app_ui = ui.page_fluid( - ui.input_action_button("first", "Invalidate first (slow) computation"), - " ", - ui.input_action_button("second", "Invalidate second (fast) computation"), - ui.br(), - ui.output_ui("result"), + ui.card( + ui.layout_columns( + ui.input_action_button("first", "Invalidate first (slow) computation"), + ui.input_action_button("second", "Invalidate second (fast) computation"), + ), + ui.output_text_verbatim("result"), + ) ) def server(input: Inputs, output: Outputs, session: Session): - @reactive.Calc + @reactive.calc def first(): input.first() p = ui.Progress() @@ -23,12 +25,12 @@ def first(): p.close() return random.randint(1, 1000) - @reactive.Calc + @reactive.calc def second(): input.second() return random.randint(1, 1000) - @render.ui + @render.text def result(): return first() + second() diff --git a/shiny/api-examples/calc/app-express.py b/shiny/api-examples/calc/app-express.py new file mode 100644 index 000000000..2361cea80 --- /dev/null +++ b/shiny/api-examples/calc/app-express.py @@ -0,0 +1,32 @@ +import random +import time + +from shiny import reactive +from shiny.express import input, render, ui + + +@reactive.calc +def first(): + input.first() + p = ui.Progress() + for i in range(30): + p.set(i / 30, message="Computing, please wait...") + time.sleep(0.1) + p.close() + return random.randint(1, 1000) + + +@reactive.calc +def second(): + input.second() + return random.randint(1, 1000) + + +with ui.card(): + with ui.layout_columns(): + ui.input_action_button("first", "Invalidate first (slow) computation") + ui.input_action_button("second", "Invalidate second (fast) computation") + + @render.text + def result(): + return first() + second() diff --git a/shiny/api-examples/card/app.py b/shiny/api-examples/card/app-core.py similarity index 100% rename from shiny/api-examples/card/app.py rename to shiny/api-examples/card/app-core.py diff --git a/shiny/api-examples/card_body/app.py b/shiny/api-examples/card_body/app-core.py similarity index 100% rename from shiny/api-examples/card_body/app.py rename to shiny/api-examples/card_body/app-core.py diff --git a/shiny/api-examples/card_footer/app.py b/shiny/api-examples/card_footer/app-core.py similarity index 100% rename from shiny/api-examples/card_footer/app.py rename to shiny/api-examples/card_footer/app-core.py diff --git a/shiny/api-examples/card_header/app.py b/shiny/api-examples/card_header/app-core.py similarity index 100% rename from shiny/api-examples/card_header/app.py rename to shiny/api-examples/card_header/app-core.py diff --git a/shiny/api-examples/close/app.py b/shiny/api-examples/close/app-core.py similarity index 100% rename from shiny/api-examples/close/app.py rename to shiny/api-examples/close/app-core.py diff --git a/shiny/api-examples/data_frame/app.py b/shiny/api-examples/data_frame/app-core.py similarity index 100% rename from shiny/api-examples/data_frame/app.py rename to shiny/api-examples/data_frame/app-core.py diff --git a/shiny/api-examples/download/app.py b/shiny/api-examples/download/app-core.py similarity index 100% rename from shiny/api-examples/download/app.py rename to shiny/api-examples/download/app-core.py diff --git a/shiny/api-examples/download_button/app.py b/shiny/api-examples/download_button/app-core.py similarity index 100% rename from shiny/api-examples/download_button/app.py rename to shiny/api-examples/download_button/app-core.py diff --git a/shiny/api-examples/download_link/app.py b/shiny/api-examples/download_link/app-core.py similarity index 100% rename from shiny/api-examples/download_link/app.py rename to shiny/api-examples/download_link/app-core.py diff --git a/shiny/api-examples/dynamic_route/app.py b/shiny/api-examples/dynamic_route/app-core.py similarity index 100% rename from shiny/api-examples/dynamic_route/app.py rename to shiny/api-examples/dynamic_route/app-core.py diff --git a/shiny/api-examples/effect/app.py b/shiny/api-examples/effect/app-core.py similarity index 100% rename from shiny/api-examples/effect/app.py rename to shiny/api-examples/effect/app-core.py diff --git a/shiny/api-examples/effect/app-express.py b/shiny/api-examples/effect/app-express.py new file mode 100644 index 000000000..363ed50cd --- /dev/null +++ b/shiny/api-examples/effect/app-express.py @@ -0,0 +1,15 @@ +from shiny import reactive +from shiny.express import input, ui + +ui.input_action_button("show", "Show modal dialog") + + +@reactive.effect +@reactive.event(input.show) +def show_important_message(): + m = ui.modal( + "This is a somewhat important message.", + easy_close=True, + footer=None, + ) + ui.modal_show(m) diff --git a/shiny/api-examples/event/app.py b/shiny/api-examples/event/app-core.py similarity index 100% rename from shiny/api-examples/event/app.py rename to shiny/api-examples/event/app-core.py diff --git a/shiny/api-examples/extended_task/app.py b/shiny/api-examples/extended_task/app-core.py similarity index 100% rename from shiny/api-examples/extended_task/app.py rename to shiny/api-examples/extended_task/app-core.py diff --git a/shiny/api-examples/file_reader/app.py b/shiny/api-examples/file_reader/app-core.py similarity index 100% rename from shiny/api-examples/file_reader/app.py rename to shiny/api-examples/file_reader/app-core.py diff --git a/shiny/api-examples/include_css/app.py b/shiny/api-examples/include_css/app-core.py similarity index 100% rename from shiny/api-examples/include_css/app.py rename to shiny/api-examples/include_css/app-core.py diff --git a/shiny/api-examples/include_js/app.py b/shiny/api-examples/include_js/app-core.py similarity index 100% rename from shiny/api-examples/include_js/app.py rename to shiny/api-examples/include_js/app-core.py diff --git a/shiny/api-examples/input_action_button/app.py b/shiny/api-examples/input_action_button/app-core.py similarity index 100% rename from shiny/api-examples/input_action_button/app.py rename to shiny/api-examples/input_action_button/app-core.py diff --git a/shiny/api-examples/input_action_link/app.py b/shiny/api-examples/input_action_link/app-core.py similarity index 100% rename from shiny/api-examples/input_action_link/app.py rename to shiny/api-examples/input_action_link/app-core.py diff --git a/shiny/api-examples/input_action_link/app-express.py b/shiny/api-examples/input_action_link/app-express.py new file mode 100644 index 000000000..39939d756 --- /dev/null +++ b/shiny/api-examples/input_action_link/app-express.py @@ -0,0 +1,20 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny import reactive +from shiny.express import input, render, ui + +ui.input_slider("n", "Number of observations", min=0, max=1000, value=500) +ui.input_action_link("go", "Go!") + + +@render.plot(alt="A histogram") +# reactive.event() to invalidate the plot when the button is pressed but not when +# the slider is changed +@reactive.event(input.go, ignore_none=False) +def plot(): + np.random.seed(19680801) + x = 100 + 15 * np.random.randn(input.n()) + fig, ax = plt.subplots() + ax.hist(x, bins=30, density=True) + return fig diff --git a/shiny/api-examples/input_checkbox/app.py b/shiny/api-examples/input_checkbox/app-core.py similarity index 100% rename from shiny/api-examples/input_checkbox/app.py rename to shiny/api-examples/input_checkbox/app-core.py diff --git a/shiny/api-examples/input_checkbox/app-express.py b/shiny/api-examples/input_checkbox/app-express.py new file mode 100644 index 000000000..1c7e87f7e --- /dev/null +++ b/shiny/api-examples/input_checkbox/app-express.py @@ -0,0 +1,8 @@ +from shiny.express import input, render, ui + +ui.input_checkbox("somevalue", "Some value", False) + + +@render.ui +def value(): + return input.somevalue() diff --git a/shiny/api-examples/input_checkbox_group/app.py b/shiny/api-examples/input_checkbox_group/app-core.py similarity index 100% rename from shiny/api-examples/input_checkbox_group/app.py rename to shiny/api-examples/input_checkbox_group/app-core.py diff --git a/shiny/api-examples/input_checkbox_group/app-express.py b/shiny/api-examples/input_checkbox_group/app-express.py new file mode 100644 index 000000000..9f3b9652b --- /dev/null +++ b/shiny/api-examples/input_checkbox_group/app-express.py @@ -0,0 +1,18 @@ +from shiny import req +from shiny.express import input, render, ui + +ui.input_checkbox_group( + "colors", + "Choose color(s):", + { + "red": ui.span("Red", style="color: #FF0000;"), + "green": ui.span("Green", style="color: #00AA00;"), + "blue": ui.span("Blue", style="color: #0000AA;"), + }, +) + + +@render.ui +def val(): + req(input.colors()) + return "You chose " + ", ".join(input.colors()) diff --git a/shiny/api-examples/input_date/app.py b/shiny/api-examples/input_date/app-core.py similarity index 100% rename from shiny/api-examples/input_date/app.py rename to shiny/api-examples/input_date/app-core.py diff --git a/shiny/api-examples/input_date/app-express.py b/shiny/api-examples/input_date/app-express.py new file mode 100644 index 000000000..bd696beb3 --- /dev/null +++ b/shiny/api-examples/input_date/app-express.py @@ -0,0 +1,24 @@ +from datetime import date + +from shiny.express import ui + +ui.input_date("date1", "Date:", value="2016-02-29") +# Default value is the date in client's time zone +ui.input_date("date2", "Date:") +# value is always yyyy-mm-dd, even if the display format is different +ui.input_date("date3", "Date:", value="2016-02-29", format="mm/dd/yy") +# Pass in a Date object +ui.input_date("date4", "Date:", value=date(2016, 2, 29)) +# Use different language and different first day of week +ui.input_date("date5", "Date:", language="ru", weekstart=1) +# Start with decade view instead of default month view +ui.input_date("date6", "Date:", startview="decade") +# Disable Mondays and Tuesdays. +ui.input_date("date7", "Date:", daysofweekdisabled=[1, 2]) +# Disable specific dates. +ui.input_date( + "date8", + "Date:", + value="2016-02-29", + datesdisabled=["2016-03-01", "2016-03-02"], +) diff --git a/shiny/api-examples/input_date_range/app.py b/shiny/api-examples/input_date_range/app-core.py similarity index 100% rename from shiny/api-examples/input_date_range/app.py rename to shiny/api-examples/input_date_range/app-core.py diff --git a/shiny/api-examples/input_date_range/app-express.py b/shiny/api-examples/input_date_range/app-express.py new file mode 100644 index 000000000..66f79c5c4 --- /dev/null +++ b/shiny/api-examples/input_date_range/app-express.py @@ -0,0 +1,27 @@ +from datetime import date + +from shiny.express import ui + +ui.input_date_range("daterange1", "Date range:", start="2001-01-01", end="2010-12-31") +# Default start and end is the current date in the client's time zone +ui.input_date_range("daterange2", "Date range:") +# start and end are always specified in yyyy-mm-dd, even if the display +# format is different +ui.input_date_range( + "daterange3", + "Date range:", + start="2001-01-01", + end="2010-12-31", + min="2001-01-01", + max="2012-12-21", + format="mm/dd/yy", + separator=" - ", +) +# Pass in Date objects +ui.input_date_range( + "daterange4", "Date range:", start=date(2001, 1, 1), end=date(2010, 12, 31) +) +# Use different language and different first day of week +ui.input_date_range("daterange5", "Date range:", language="de", weekstart=1) +# Start with decade view instead of default month view +ui.input_date_range("daterange6", "Date range:", startview="decade") diff --git a/shiny/api-examples/input_file/app.py b/shiny/api-examples/input_file/app-core.py similarity index 100% rename from shiny/api-examples/input_file/app.py rename to shiny/api-examples/input_file/app-core.py diff --git a/shiny/api-examples/input_file/app-express.py b/shiny/api-examples/input_file/app-express.py new file mode 100644 index 000000000..81222e4f7 --- /dev/null +++ b/shiny/api-examples/input_file/app-express.py @@ -0,0 +1,48 @@ +import pandas as pd + +from shiny import reactive +from shiny.express import input, render, ui +from shiny.types import FileInfo + +ui.input_file("file1", "Choose CSV File", accept=[".csv"], multiple=False) +ui.input_checkbox_group( + "stats", + "Summary Stats", + choices=["Row Count", "Column Count", "Column Names"], + selected=["Row Count", "Column Count", "Column Names"], +) + + +@reactive.Calc +def parsed_file(): + file: list[FileInfo] | None = input.file1() + if file is None: + return pd.DataFrame() + return pd.read_csv(file[0]["datapath"]) # pyright: ignore[reportUnknownMemberType] + + +@render.table +def summary(): + df = parsed_file() + + if df.empty: + return pd.DataFrame() + + # Get the row count, column count, and column names of the DataFrame + row_count = df.shape[0] + column_count = df.shape[1] + names = df.columns.tolist() + column_names = ", ".join(str(name) for name in names) + + # Create a new DataFrame to display the information + info_df = pd.DataFrame( + { + "Row Count": [row_count], + "Column Count": [column_count], + "Column Names": [column_names], + } + ) + + # input.stats() is a list of strings; subset the columns based on the selected + # checkboxes + return info_df.loc[:, input.stats()] diff --git a/shiny/api-examples/input_numeric/app.py b/shiny/api-examples/input_numeric/app-core.py similarity index 100% rename from shiny/api-examples/input_numeric/app.py rename to shiny/api-examples/input_numeric/app-core.py diff --git a/shiny/api-examples/input_numeric/app-express.py b/shiny/api-examples/input_numeric/app-express.py new file mode 100644 index 000000000..7e06f1c0a --- /dev/null +++ b/shiny/api-examples/input_numeric/app-express.py @@ -0,0 +1,8 @@ +from shiny.express import input, render, ui + +ui.input_numeric("obs", "Observations:", 10, min=1, max=100) + + +@render.code +def value(): + return input.obs() diff --git a/shiny/api-examples/input_password/app.py b/shiny/api-examples/input_password/app-core.py similarity index 100% rename from shiny/api-examples/input_password/app.py rename to shiny/api-examples/input_password/app-core.py diff --git a/shiny/api-examples/input_password/app-express.py b/shiny/api-examples/input_password/app-express.py new file mode 100644 index 000000000..4366c5303 --- /dev/null +++ b/shiny/api-examples/input_password/app-express.py @@ -0,0 +1,11 @@ +from shiny import reactive +from shiny.express import input, render, ui + +ui.input_password("password", "Password:") +ui.input_action_button("go", "Go") + + +@render.code +@reactive.event(input.go) +def value(): + return input.password() diff --git a/shiny/api-examples/input_radio_buttons/app.py b/shiny/api-examples/input_radio_buttons/app-core.py similarity index 100% rename from shiny/api-examples/input_radio_buttons/app.py rename to shiny/api-examples/input_radio_buttons/app-core.py diff --git a/shiny/api-examples/input_radio_buttons/app-express.py b/shiny/api-examples/input_radio_buttons/app-express.py new file mode 100644 index 000000000..5354f4134 --- /dev/null +++ b/shiny/api-examples/input_radio_buttons/app-express.py @@ -0,0 +1,15 @@ +from shiny.express import input, render, ui + +ui.input_radio_buttons( + "rb", + "Choose one:", + { + "html": ui.HTML("Red Text"), + "text": "Normal text", + }, +) + + +@render.express +def val(): + "You chose " + input.rb() diff --git a/shiny/api-examples/input_select/app.py b/shiny/api-examples/input_select/app-core.py similarity index 100% rename from shiny/api-examples/input_select/app.py rename to shiny/api-examples/input_select/app-core.py diff --git a/shiny/api-examples/input_select/app-express.py b/shiny/api-examples/input_select/app-express.py new file mode 100644 index 000000000..c45482f0c --- /dev/null +++ b/shiny/api-examples/input_select/app-express.py @@ -0,0 +1,16 @@ +from shiny.express import input, render, ui + +ui.input_select( + "state", + "Choose a state:", + { + "East Coast": {"NY": "New York", "NJ": "New Jersey", "CT": "Connecticut"}, + "West Coast": {"WA": "Washington", "OR": "Oregon", "CA": "California"}, + "Midwest": {"MN": "Minnesota", "WI": "Wisconsin", "IA": "Iowa"}, + }, +) + + +@render.text +def value(): + return "You choose: " + str(input.state()) diff --git a/shiny/api-examples/input_selectize/app.py b/shiny/api-examples/input_selectize/app-core.py similarity index 97% rename from shiny/api-examples/input_selectize/app.py rename to shiny/api-examples/input_selectize/app-core.py index bc2dd9bd9..23a63d376 100644 --- a/shiny/api-examples/input_selectize/app.py +++ b/shiny/api-examples/input_selectize/app-core.py @@ -17,7 +17,7 @@ ), ui.output_text("value"), ui.input_selectize( - "state", + "state2", "Selectize Options", states, multiple=True, @@ -32,7 +32,7 @@ ), ), ui.input_selectize( - "state", + "state3", "Selectize plugins", states, multiple=True, diff --git a/shiny/api-examples/input_selectize/app-express.py b/shiny/api-examples/input_selectize/app-express.py new file mode 100644 index 000000000..d58e04154 --- /dev/null +++ b/shiny/api-examples/input_selectize/app-express.py @@ -0,0 +1,45 @@ +from html import escape # noqa: F401 + +from shiny.express import input, render, ui + +states = { + "East Coast": {"NY": "New York", "NJ": "New Jersey", "CT": "Connecticut"}, + "West Coast": {"WA": "Washington", "OR": "Oregon", "CA": "California"}, + "Midwest": {"MN": "Minnesota", "WI": "Wisconsin", "IA": "Iowa"}, +} + +ui.input_selectize( + "state", + "Choose a state:", + states, + multiple=True, +) + + +@render.text +def value(): + return "You choose: " + str(input.state()) + + +ui.input_selectize( + "state2", + "Selectize Options", + states, + multiple=True, + options=( + { + "placeholder": "Enter text", + "render": ui.js_eval( + '{option: function(item, escape) {return "
Select " + escape(item.label) + "
";}}' + ), + "create": True, + } + ), +) +ui.input_selectize( + "state3", + "Selectize plugins", + states, + multiple=True, + options={"plugins": ["clear_button"]}, +) diff --git a/shiny/api-examples/input_slider/app.py b/shiny/api-examples/input_slider/app-core.py similarity index 100% rename from shiny/api-examples/input_slider/app.py rename to shiny/api-examples/input_slider/app-core.py diff --git a/shiny/api-examples/input_slider/app-express.py b/shiny/api-examples/input_slider/app-express.py new file mode 100644 index 000000000..e6e701594 --- /dev/null +++ b/shiny/api-examples/input_slider/app-express.py @@ -0,0 +1,16 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny.express import input, render, ui + +ui.input_slider("obs", "Number of bins:", min=10, max=100, value=30) + + +@render.plot +def distPlot(): + np.random.seed(19680801) + x = 100 + 15 * np.random.randn(437) + + fig, ax = plt.subplots() + ax.hist(x, input.obs(), density=True) + return fig diff --git a/shiny/api-examples/input_switch/app.py b/shiny/api-examples/input_switch/app-core.py similarity index 86% rename from shiny/api-examples/input_switch/app.py rename to shiny/api-examples/input_switch/app-core.py index a438d8195..f05cba431 100644 --- a/shiny/api-examples/input_switch/app.py +++ b/shiny/api-examples/input_switch/app-core.py @@ -2,12 +2,12 @@ app_ui = ui.page_fluid( ui.input_switch("somevalue", "Some value", False), - ui.output_ui("value"), + ui.output_text("value"), ) def server(input: Inputs, output: Outputs, session: Session): - @render.ui + @render.text def value(): return input.somevalue() diff --git a/shiny/api-examples/input_switch/app-express.py b/shiny/api-examples/input_switch/app-express.py new file mode 100644 index 000000000..efd623850 --- /dev/null +++ b/shiny/api-examples/input_switch/app-express.py @@ -0,0 +1,8 @@ +from shiny.express import input, render, ui + +ui.input_switch("somevalue", "Some value", False) + + +@render.text +def value(): + return input.somevalue() diff --git a/shiny/api-examples/input_text/app.py b/shiny/api-examples/input_text/app-core.py similarity index 100% rename from shiny/api-examples/input_text/app.py rename to shiny/api-examples/input_text/app-core.py diff --git a/shiny/api-examples/input_text/app-express.py b/shiny/api-examples/input_text/app-express.py new file mode 100644 index 000000000..8c0c30d69 --- /dev/null +++ b/shiny/api-examples/input_text/app-express.py @@ -0,0 +1,8 @@ +from shiny.express import input, render, ui + +ui.input_text("caption", "Caption:", "Data summary") + + +@render.code +def value(): + return input.caption() diff --git a/shiny/api-examples/input_text_area/app.py b/shiny/api-examples/input_text_area/app-core.py similarity index 100% rename from shiny/api-examples/input_text_area/app.py rename to shiny/api-examples/input_text_area/app-core.py diff --git a/shiny/api-examples/input_text_area/app-express.py b/shiny/api-examples/input_text_area/app-express.py new file mode 100644 index 000000000..4841f56ee --- /dev/null +++ b/shiny/api-examples/input_text_area/app-express.py @@ -0,0 +1,25 @@ +from shiny.express import input, render, ui + +ui.input_text_area( + "caption_regular", + "Caption:", + "Data summary\nwith\nmultiple\nlines", +) + + +@render.text +def value_regular(): + return input.caption_regular() + + +ui.input_text_area( + "caption_autoresize", + ui.markdown("Caption (w/ `autoresize=True`):"), + "Data summary\nwith\nmultiple\nlines", + autoresize=True, +) + + +@render.text +def value_autoresize(): + return input.caption_autoresize() diff --git a/shiny/api-examples/insert_accordion_panel/app.py b/shiny/api-examples/insert_accordion_panel/app-core.py similarity index 100% rename from shiny/api-examples/insert_accordion_panel/app.py rename to shiny/api-examples/insert_accordion_panel/app-core.py diff --git a/shiny/api-examples/insert_ui/app.py b/shiny/api-examples/insert_ui/app-core.py similarity index 100% rename from shiny/api-examples/insert_ui/app.py rename to shiny/api-examples/insert_ui/app-core.py diff --git a/shiny/api-examples/invalidate_later/app.py b/shiny/api-examples/invalidate_later/app-core.py similarity index 60% rename from shiny/api-examples/invalidate_later/app.py rename to shiny/api-examples/invalidate_later/app-core.py index 7966d01f0..66de8233d 100644 --- a/shiny/api-examples/invalidate_later/app.py +++ b/shiny/api-examples/invalidate_later/app-core.py @@ -2,16 +2,11 @@ from shiny import App, Inputs, Outputs, Session, reactive, render, ui -app_ui = ui.page_fluid(ui.output_ui("value")) +app_ui = ui.page_fluid(ui.output_text("value")) def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - def _(): - reactive.invalidate_later(0.5) - print("Random int: ", random.randint(0, 10000)) - - @render.ui + @render.text def value(): reactive.invalidate_later(0.5) return "Random int: " + str(random.randint(0, 10000)) diff --git a/shiny/api-examples/invalidate_later/app-express.py b/shiny/api-examples/invalidate_later/app-express.py new file mode 100644 index 000000000..270c5e889 --- /dev/null +++ b/shiny/api-examples/invalidate_later/app-express.py @@ -0,0 +1,10 @@ +import random + +from shiny import reactive +from shiny.express import render + + +@render.text +def value(): + reactive.invalidate_later(0.5) + return "Random int: " + str(random.randint(0, 10000)) diff --git a/shiny/api-examples/isolate/app.py b/shiny/api-examples/isolate/app-core.py similarity index 100% rename from shiny/api-examples/isolate/app.py rename to shiny/api-examples/isolate/app-core.py diff --git a/shiny/api-examples/isolate/app-express.py b/shiny/api-examples/isolate/app-express.py new file mode 100644 index 000000000..52f8b9c38 --- /dev/null +++ b/shiny/api-examples/isolate/app-express.py @@ -0,0 +1,23 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny import reactive +from shiny.express import input, render, ui + +ui.input_slider("n", "Number of observations", min=0, max=1000, value=500) +ui.input_action_button("go", "Go!", class_="btn-success") + + +@render.plot(alt="A histogram") +def plot(): + # Take a reactive dependency on the action button... + input.go() + + # ...but don't take a reactive dependency on the slider + with reactive.isolate(): + np.random.seed(19680801) + x = 100 + 15 * np.random.randn(input.n()) + + fig, ax = plt.subplots() + ax.hist(x, bins=30, density=True) + return fig diff --git a/shiny/api-examples/layout_column_wrap/app.py b/shiny/api-examples/layout_column_wrap/app-core.py similarity index 100% rename from shiny/api-examples/layout_column_wrap/app.py rename to shiny/api-examples/layout_column_wrap/app-core.py diff --git a/shiny/api-examples/layout_column_wrap/app-express.py b/shiny/api-examples/layout_column_wrap/app-express.py new file mode 100644 index 000000000..0754d8b74 --- /dev/null +++ b/shiny/api-examples/layout_column_wrap/app-express.py @@ -0,0 +1,19 @@ +from shiny.express import ui + +with ui.hold() as a_card: + with ui.card(): + "A simple card" + +# Always has 2 columns (on non-mobile) +with ui.layout_column_wrap(width=1 / 2): + a_card + a_card + a_card + +ui.hr() + +# Has three columns when viewport is wider than 750px +with ui.layout_column_wrap(width="250px"): + a_card + a_card + a_card diff --git a/shiny/api-examples/layout_columns/app.py b/shiny/api-examples/layout_columns/app-core.py similarity index 100% rename from shiny/api-examples/layout_columns/app.py rename to shiny/api-examples/layout_columns/app-core.py diff --git a/shiny/api-examples/layout_columns/app-express.py b/shiny/api-examples/layout_columns/app-express.py new file mode 100644 index 000000000..768056d5c --- /dev/null +++ b/shiny/api-examples/layout_columns/app-express.py @@ -0,0 +1,38 @@ +from model_plots import ( + plot_accuracy_over_time, + plot_feature_importance, + plot_loss_over_time, +) + +from shiny.express import render, ui + +ui.page_opts(title="Model Dashboard") + +ui.markdown("Using `ui.layout_columns()` for the layout.") + + +with ui.layout_columns( + col_widths={"sm": (5, 7, 12)}, + # row_heights=(2, 3), + # height="700px", +): + with ui.card(full_screen=True): + ui.card_header("Loss Over Time") + + @render.plot + def loss_over_time(): + return plot_loss_over_time() + + with ui.card(full_screen=True): + ui.card_header("Accuracy Over Time") + + @render.plot + def accuracy_over_time(): + return plot_accuracy_over_time() + + with ui.card(full_screen=True): + ui.card_header("Feature Importance") + + @render.plot + def feature_importance(): + return plot_feature_importance() diff --git a/shiny/api-examples/layout_sidebar/app.py b/shiny/api-examples/layout_sidebar/app-core.py similarity index 100% rename from shiny/api-examples/layout_sidebar/app.py rename to shiny/api-examples/layout_sidebar/app-core.py diff --git a/shiny/api-examples/layout_sidebar/app-express.py b/shiny/api-examples/layout_sidebar/app-express.py new file mode 100644 index 000000000..10482a396 --- /dev/null +++ b/shiny/api-examples/layout_sidebar/app-express.py @@ -0,0 +1,17 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny.express import input, render, ui + +with ui.layout_sidebar(): + with ui.sidebar(): + ui.input_slider("n", "N", min=0, max=100, value=20) + + @render.plot(alt="A histogram") + def plot() -> object: + np.random.seed(19680801) + x = 100 + 15 * np.random.randn(437) + + fig, ax = plt.subplots() + ax.hist(x, input.n(), density=True) + return fig diff --git a/shiny/api-examples/markdown/app.py b/shiny/api-examples/markdown/app-core.py similarity index 100% rename from shiny/api-examples/markdown/app.py rename to shiny/api-examples/markdown/app-core.py diff --git a/shiny/api-examples/markdown/app-express.py b/shiny/api-examples/markdown/app-express.py new file mode 100644 index 000000000..f1db29501 --- /dev/null +++ b/shiny/api-examples/markdown/app-express.py @@ -0,0 +1,13 @@ +from shiny.express import ui + +ui.markdown( + """ + # Hello World + + This is **markdown** and here is some `code`: + + ```python + print('Hello world!') + ``` + """ +) diff --git a/shiny/api-examples/modal/app.py b/shiny/api-examples/modal/app-core.py similarity index 100% rename from shiny/api-examples/modal/app.py rename to shiny/api-examples/modal/app-core.py diff --git a/shiny/api-examples/nav_panel/app.py b/shiny/api-examples/nav_panel/app-core.py similarity index 100% rename from shiny/api-examples/nav_panel/app.py rename to shiny/api-examples/nav_panel/app-core.py diff --git a/shiny/api-examples/navset_hidden/app.py b/shiny/api-examples/navset_hidden/app-core.py similarity index 100% rename from shiny/api-examples/navset_hidden/app.py rename to shiny/api-examples/navset_hidden/app-core.py diff --git a/shiny/api-examples/notification_show/app.py b/shiny/api-examples/notification_show/app-core.py similarity index 100% rename from shiny/api-examples/notification_show/app.py rename to shiny/api-examples/notification_show/app-core.py diff --git a/shiny/api-examples/on_ended/app.py b/shiny/api-examples/on_ended/app-core.py similarity index 100% rename from shiny/api-examples/on_ended/app.py rename to shiny/api-examples/on_ended/app-core.py diff --git a/shiny/api-examples/on_flush/app.py b/shiny/api-examples/on_flush/app-core.py similarity index 100% rename from shiny/api-examples/on_flush/app.py rename to shiny/api-examples/on_flush/app-core.py diff --git a/shiny/api-examples/on_flushed/app.py b/shiny/api-examples/on_flushed/app-core.py similarity index 100% rename from shiny/api-examples/on_flushed/app.py rename to shiny/api-examples/on_flushed/app-core.py diff --git a/shiny/api-examples/output_image/app.py b/shiny/api-examples/output_image/app-core.py similarity index 100% rename from shiny/api-examples/output_image/app.py rename to shiny/api-examples/output_image/app-core.py diff --git a/shiny/api-examples/output_plot/app.py b/shiny/api-examples/output_plot/app-core.py similarity index 100% rename from shiny/api-examples/output_plot/app.py rename to shiny/api-examples/output_plot/app-core.py diff --git a/shiny/api-examples/output_table/app.py b/shiny/api-examples/output_table/app-core.py similarity index 100% rename from shiny/api-examples/output_table/app.py rename to shiny/api-examples/output_table/app-core.py diff --git a/shiny/api-examples/output_text/app.py b/shiny/api-examples/output_text/app-core.py similarity index 100% rename from shiny/api-examples/output_text/app.py rename to shiny/api-examples/output_text/app-core.py diff --git a/shiny/api-examples/output_transformer/app.py b/shiny/api-examples/output_transformer/app-core.py similarity index 100% rename from shiny/api-examples/output_transformer/app.py rename to shiny/api-examples/output_transformer/app-core.py diff --git a/shiny/api-examples/output_ui/app.py b/shiny/api-examples/output_ui/app-core.py similarity index 100% rename from shiny/api-examples/output_ui/app.py rename to shiny/api-examples/output_ui/app-core.py diff --git a/shiny/api-examples/page_fixed/app.py b/shiny/api-examples/page_fixed/app-core.py similarity index 100% rename from shiny/api-examples/page_fixed/app.py rename to shiny/api-examples/page_fixed/app-core.py diff --git a/shiny/api-examples/page_fluid/app.py b/shiny/api-examples/page_fluid/app-core.py similarity index 100% rename from shiny/api-examples/page_fluid/app.py rename to shiny/api-examples/page_fluid/app-core.py diff --git a/shiny/api-examples/page_sidebar/app.py b/shiny/api-examples/page_sidebar/app-core.py similarity index 98% rename from shiny/api-examples/page_sidebar/app.py rename to shiny/api-examples/page_sidebar/app-core.py index ba84c3fe8..8ce58f8b8 100644 --- a/shiny/api-examples/page_sidebar/app.py +++ b/shiny/api-examples/page_sidebar/app-core.py @@ -14,7 +14,6 @@ def server(input: Inputs, output: Outputs, session: Session): - @output @render.plot(alt="A histogram") def plot() -> object: np.random.seed(19680801) diff --git a/shiny/api-examples/page_sidebar/app-express.py b/shiny/api-examples/page_sidebar/app-express.py new file mode 100644 index 000000000..4781f851c --- /dev/null +++ b/shiny/api-examples/page_sidebar/app-express.py @@ -0,0 +1,17 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny.express import input, render, ui + +with ui.sidebar(): + ui.input_slider("n", "N", min=0, max=100, value=20) + + +@render.plot(alt="A histogram") +def plot() -> object: + np.random.seed(19680801) + x = 100 + 15 * np.random.randn(437) + + fig, ax = plt.subplots() + ax.hist(x, input.n(), density=True) + return fig diff --git a/shiny/api-examples/panel_absolute/app.py b/shiny/api-examples/panel_absolute/app-core.py similarity index 96% rename from shiny/api-examples/panel_absolute/app.py rename to shiny/api-examples/panel_absolute/app-core.py index 10fab125e..3340e5f93 100644 --- a/shiny/api-examples/panel_absolute/app.py +++ b/shiny/api-examples/panel_absolute/app-core.py @@ -9,7 +9,7 @@ draggable=True, width="300px", right="50px", - top="50%", + top="25%", ), ) diff --git a/shiny/api-examples/panel_absolute/app-express.py b/shiny/api-examples/panel_absolute/app-express.py new file mode 100644 index 000000000..8cf254ecb --- /dev/null +++ b/shiny/api-examples/panel_absolute/app-express.py @@ -0,0 +1,8 @@ +from shiny.express import ui + +ui.h2("A basic absolute panel example") + +with ui.panel_absolute(draggable=True, width="300px", right="50px", top="25%"): + with ui.panel_well(): + "Drag me around!" + ui.input_slider("n", "N", min=0, max=100, value=20) diff --git a/shiny/api-examples/panel_conditional/app.py b/shiny/api-examples/panel_conditional/app-core.py similarity index 100% rename from shiny/api-examples/panel_conditional/app.py rename to shiny/api-examples/panel_conditional/app-core.py diff --git a/shiny/api-examples/panel_conditional/app-express.py b/shiny/api-examples/panel_conditional/app-express.py new file mode 100644 index 000000000..8f5fbb229 --- /dev/null +++ b/shiny/api-examples/panel_conditional/app-express.py @@ -0,0 +1,12 @@ +from shiny.express import ui + +ui.input_checkbox("show", "Show radio buttons", False) + +with ui.panel_conditional("input.show"): + ui.input_radio_buttons("radio", "Choose ", ["slider", "select"]) + +with ui.panel_conditional("input.show && input.radio === 'slider'"): + ui.input_slider("slider", None, min=0, max=100, value=50) + +with ui.panel_conditional("input.show && input.radio === 'select'"): + ui.input_select("slider", None, ["A", "B", "C"]) diff --git a/shiny/api-examples/panel_title/app.py b/shiny/api-examples/panel_title/app-core.py similarity index 100% rename from shiny/api-examples/panel_title/app.py rename to shiny/api-examples/panel_title/app-core.py diff --git a/shiny/api-examples/poll/app.py b/shiny/api-examples/poll/app-core.py similarity index 80% rename from shiny/api-examples/poll/app.py rename to shiny/api-examples/poll/app-core.py index c9411d28a..6079a0be4 100644 --- a/shiny/api-examples/poll/app.py +++ b/shiny/api-examples/poll/app-core.py @@ -89,22 +89,21 @@ def stock_quotes() -> pd.DataFrame: # === Define the Shiny UI and server =============================== app_ui = ui.page_fluid( - ui.row( - ui.column( - 8, - ui.markdown( - """ - # `shiny.reactive.poll` demo - - This example app shows how to stream results from a database (in this - case, an in-memory sqlite3) with the help of `shiny.reactive.poll`. - """ - ), - class_="mb-3", + ui.card( + ui.markdown( + """ + # `shiny.reactive.poll` demo + + This example app shows how to stream results from a database (in this + case, an in-memory sqlite3) with the help of `shiny.reactive.poll`. + """ ), - ), - ui.input_selectize("symbols", "Filter by symbol", [""] + SYMBOLS, multiple=True), - ui.output_ui("table"), + ui.input_selectize( + "symbols", "Filter by symbol", [""] + SYMBOLS, multiple=True + ), + ui.output_data_frame("table"), + fill=False, + ) ) @@ -115,13 +114,9 @@ def filtered_quotes(): df = df[df["symbol"].isin(input.symbols())] return df - @render.ui + @render.data_frame def table(): - return ui.HTML( - filtered_quotes().to_html( - index=False, classes="table font-monospace w-auto" - ) - ) + return filtered_quotes() app = App(app_ui, server) diff --git a/shiny/api-examples/popover/app.py b/shiny/api-examples/popover/app-core.py similarity index 100% rename from shiny/api-examples/popover/app.py rename to shiny/api-examples/popover/app-core.py diff --git a/shiny/api-examples/remove_accordion_panel/app.py b/shiny/api-examples/remove_accordion_panel/app-core.py similarity index 100% rename from shiny/api-examples/remove_accordion_panel/app.py rename to shiny/api-examples/remove_accordion_panel/app-core.py diff --git a/shiny/api-examples/remove_ui/app.py b/shiny/api-examples/remove_ui/app-core.py similarity index 100% rename from shiny/api-examples/remove_ui/app.py rename to shiny/api-examples/remove_ui/app-core.py diff --git a/shiny/api-examples/render_express/app.py b/shiny/api-examples/render_express/app-core.py similarity index 100% rename from shiny/api-examples/render_express/app.py rename to shiny/api-examples/render_express/app-core.py diff --git a/shiny/api-examples/render_image/app.py b/shiny/api-examples/render_image/app-core.py similarity index 100% rename from shiny/api-examples/render_image/app.py rename to shiny/api-examples/render_image/app-core.py diff --git a/shiny/api-examples/req/app.py b/shiny/api-examples/req/app-core.py similarity index 100% rename from shiny/api-examples/req/app.py rename to shiny/api-examples/req/app-core.py diff --git a/shiny/api-examples/row/app.py b/shiny/api-examples/row/app-core.py similarity index 100% rename from shiny/api-examples/row/app.py rename to shiny/api-examples/row/app-core.py diff --git a/shiny/api-examples/send_custom_message/app.py b/shiny/api-examples/send_custom_message/app-core.py similarity index 100% rename from shiny/api-examples/send_custom_message/app.py rename to shiny/api-examples/send_custom_message/app-core.py diff --git a/shiny/api-examples/showcase_bottom/app.py b/shiny/api-examples/showcase_bottom/app-core.py similarity index 100% rename from shiny/api-examples/showcase_bottom/app.py rename to shiny/api-examples/showcase_bottom/app-core.py diff --git a/shiny/api-examples/showcase_left_center/app.py b/shiny/api-examples/showcase_left_center/app-core.py similarity index 100% rename from shiny/api-examples/showcase_left_center/app.py rename to shiny/api-examples/showcase_left_center/app-core.py diff --git a/shiny/api-examples/showcase_top_right/app.py b/shiny/api-examples/showcase_top_right/app-core.py similarity index 100% rename from shiny/api-examples/showcase_top_right/app.py rename to shiny/api-examples/showcase_top_right/app-core.py diff --git a/shiny/api-examples/sidebar/app.py b/shiny/api-examples/sidebar/app-core.py similarity index 100% rename from shiny/api-examples/sidebar/app.py rename to shiny/api-examples/sidebar/app-core.py diff --git a/shiny/api-examples/sidebar/app-express.py b/shiny/api-examples/sidebar/app-express.py new file mode 100644 index 000000000..17e80bb47 --- /dev/null +++ b/shiny/api-examples/sidebar/app-express.py @@ -0,0 +1,42 @@ +from shiny.express import input, render, ui + +ui.page_opts(fillable=True) + +with ui.card(): + with ui.layout_sidebar(): + with ui.sidebar(id="sidebar_left", open="desktop"): + "Left sidebar content" + + @render.text + def state_left(): + return f"input.sidebar_left(): {input.sidebar_left()}" + + +with ui.card(): + with ui.layout_sidebar(): + with ui.sidebar(id="sidebar_right", position="right", open="desktop"): + "Right sidebar content" + + @render.text + def state_right(): + return f"input.sidebar_right(): {input.sidebar_right()}" + + +with ui.card(): + with ui.layout_sidebar(): + with ui.sidebar(id="sidebar_closed", open="closed"): + "Closed sidebar content" + + @render.text + def state_closed(): + return f"input.sidebar_closed(): {input.sidebar_closed()}" + + +with ui.card(): + with ui.layout_sidebar(): + with ui.sidebar(id="sidebar_always", open="always"): + "Always sidebar content" + + @render.text + def state_always(): + return f"input.sidebar_always(): {input.sidebar_always()}" diff --git a/shiny/api-examples/template/app.py b/shiny/api-examples/template/app-core.py similarity index 100% rename from shiny/api-examples/template/app.py rename to shiny/api-examples/template/app-core.py diff --git a/shiny/api-examples/todo_list/app.py b/shiny/api-examples/todo_list/app-core.py similarity index 100% rename from shiny/api-examples/todo_list/app.py rename to shiny/api-examples/todo_list/app-core.py diff --git a/shiny/api-examples/tooltip/app.py b/shiny/api-examples/tooltip/app-core.py similarity index 100% rename from shiny/api-examples/tooltip/app.py rename to shiny/api-examples/tooltip/app-core.py diff --git a/shiny/api-examples/update_accordion/app.py b/shiny/api-examples/update_accordion/app-core.py similarity index 100% rename from shiny/api-examples/update_accordion/app.py rename to shiny/api-examples/update_accordion/app-core.py diff --git a/shiny/api-examples/update_accordion_panel/app.py b/shiny/api-examples/update_accordion_panel/app-core.py similarity index 100% rename from shiny/api-examples/update_accordion_panel/app.py rename to shiny/api-examples/update_accordion_panel/app-core.py diff --git a/shiny/api-examples/update_action_button/app.py b/shiny/api-examples/update_action_button/app-core.py similarity index 100% rename from shiny/api-examples/update_action_button/app.py rename to shiny/api-examples/update_action_button/app-core.py diff --git a/shiny/api-examples/update_checkbox/app.py b/shiny/api-examples/update_checkbox/app-core.py similarity index 100% rename from shiny/api-examples/update_checkbox/app.py rename to shiny/api-examples/update_checkbox/app-core.py diff --git a/shiny/api-examples/update_checkbox_group/app.py b/shiny/api-examples/update_checkbox_group/app-core.py similarity index 100% rename from shiny/api-examples/update_checkbox_group/app.py rename to shiny/api-examples/update_checkbox_group/app-core.py diff --git a/shiny/api-examples/update_date/app.py b/shiny/api-examples/update_date/app-core.py similarity index 100% rename from shiny/api-examples/update_date/app.py rename to shiny/api-examples/update_date/app-core.py diff --git a/shiny/api-examples/update_date_range/app.py b/shiny/api-examples/update_date_range/app-core.py similarity index 100% rename from shiny/api-examples/update_date_range/app.py rename to shiny/api-examples/update_date_range/app-core.py diff --git a/shiny/api-examples/update_navs/app.py b/shiny/api-examples/update_navs/app-core.py similarity index 100% rename from shiny/api-examples/update_navs/app.py rename to shiny/api-examples/update_navs/app-core.py diff --git a/shiny/api-examples/update_numeric/app.py b/shiny/api-examples/update_numeric/app-core.py similarity index 100% rename from shiny/api-examples/update_numeric/app.py rename to shiny/api-examples/update_numeric/app-core.py diff --git a/shiny/api-examples/update_popover/app.py b/shiny/api-examples/update_popover/app-core.py similarity index 100% rename from shiny/api-examples/update_popover/app.py rename to shiny/api-examples/update_popover/app-core.py diff --git a/shiny/api-examples/update_radio_buttons/app.py b/shiny/api-examples/update_radio_buttons/app-core.py similarity index 100% rename from shiny/api-examples/update_radio_buttons/app.py rename to shiny/api-examples/update_radio_buttons/app-core.py diff --git a/shiny/api-examples/update_select/app.py b/shiny/api-examples/update_select/app-core.py similarity index 100% rename from shiny/api-examples/update_select/app.py rename to shiny/api-examples/update_select/app-core.py diff --git a/shiny/api-examples/update_selectize/app.py b/shiny/api-examples/update_selectize/app-core.py similarity index 100% rename from shiny/api-examples/update_selectize/app.py rename to shiny/api-examples/update_selectize/app-core.py diff --git a/shiny/api-examples/update_sidebar/app.py b/shiny/api-examples/update_sidebar/app-core.py similarity index 100% rename from shiny/api-examples/update_sidebar/app.py rename to shiny/api-examples/update_sidebar/app-core.py diff --git a/shiny/api-examples/update_slider/app.py b/shiny/api-examples/update_slider/app-core.py similarity index 100% rename from shiny/api-examples/update_slider/app.py rename to shiny/api-examples/update_slider/app-core.py diff --git a/shiny/api-examples/update_text/app.py b/shiny/api-examples/update_text/app-core.py similarity index 100% rename from shiny/api-examples/update_text/app.py rename to shiny/api-examples/update_text/app-core.py diff --git a/shiny/api-examples/update_tooltip/app.py b/shiny/api-examples/update_tooltip/app-core.py similarity index 100% rename from shiny/api-examples/update_tooltip/app.py rename to shiny/api-examples/update_tooltip/app-core.py diff --git a/shiny/api-examples/value_box/app.py b/shiny/api-examples/value_box/app-core.py similarity index 100% rename from shiny/api-examples/value_box/app.py rename to shiny/api-examples/value_box/app-core.py diff --git a/shiny/api-examples/www_dir/app.py b/shiny/api-examples/www_dir/app-core.py similarity index 100% rename from shiny/api-examples/www_dir/app.py rename to shiny/api-examples/www_dir/app-core.py diff --git a/shiny/module.py b/shiny/module.py index 2045e3a93..b3e0643a4 100644 --- a/shiny/module.py +++ b/shiny/module.py @@ -15,7 +15,7 @@ R = TypeVar("R") -@no_example +@no_example() def ui(fn: Callable[P, R]) -> Callable[Concatenate[str, P], R]: def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: with namespace_context(id): @@ -24,7 +24,7 @@ def wrapper(id: Id, *args: P.args, **kwargs: P.kwargs) -> R: return wrapper -@no_example +@no_example() def server( fn: Callable[Concatenate[Inputs, Outputs, Session, P], R] ) -> Callable[Concatenate[str, P], R]: diff --git a/shiny/reactive/_core.py b/shiny/reactive/_core.py index d7f0219c8..9dfa64156 100644 --- a/shiny/reactive/_core.py +++ b/shiny/reactive/_core.py @@ -200,6 +200,7 @@ def isolate(self) -> Generator[None, None, None]: @add_example() +@no_example("express") @contextlib.contextmanager def isolate() -> Generator[None, None, None]: """ @@ -247,7 +248,7 @@ def get_current_context() -> Context: return _reactive_environment.current_context() -@no_example +@no_example() async def flush() -> None: """ Run any pending invalidations (i.e., flush the reactive environment). @@ -260,7 +261,7 @@ async def flush() -> None: await _reactive_environment.flush() -@no_example +@no_example() def on_flushed( func: Callable[[], Awaitable[None]], once: bool = False ) -> Callable[[], None]: @@ -288,7 +289,7 @@ def on_flushed( return _reactive_environment.on_flushed(func, once) -@no_example +@no_example() def lock() -> asyncio.Lock: """ A lock that should be held whenever manipulating the reactive graph. diff --git a/shiny/reactive/_reactives.py b/shiny/reactive/_reactives.py index 10061a2e1..60c003dfa 100644 --- a/shiny/reactive/_reactives.py +++ b/shiny/reactive/_reactives.py @@ -30,7 +30,7 @@ ) from .. import _utils -from .._docstring import add_example +from .._docstring import add_example, no_example from .._utils import is_async_callable, run_coro_sync from .._validation import req from ..types import MISSING, MISSING_TYPE, ActionButtonValue, SilentException @@ -679,6 +679,7 @@ def effect( @add_example() +@no_example("express") def effect( fn: Optional[EffectFunction | EffectFunctionAsync] = None, *, diff --git a/shiny/render/_dataframe.py b/shiny/render/_dataframe.py index 3a5d0343f..a24996c77 100644 --- a/shiny/render/_dataframe.py +++ b/shiny/render/_dataframe.py @@ -108,7 +108,7 @@ def to_payload(self) -> Jsonifiable: return res -@no_example +@no_example() class DataTable(AbstractTabularData): """ Holds the data and options for a :class:`~shiny.render.data_frame` output, for a diff --git a/shiny/render/_render.py b/shiny/render/_render.py index 067ce0369..adb9cf6df 100644 --- a/shiny/render/_render.py +++ b/shiny/render/_render.py @@ -27,7 +27,7 @@ from .. import _utils from .. import ui as _ui -from .._docstring import add_example +from .._docstring import add_example, no_example from .._namespaces import ResolvedId from .._typing_extensions import Self from ..session import get_current_session, require_active_session @@ -61,6 +61,7 @@ @add_example(ex_dir="../api-examples/output_text") +@no_example("express") class text(Renderer[str]): """ Reactively render text. @@ -188,6 +189,7 @@ async def transform(self, value: str) -> Jsonifiable: @add_example(ex_dir="../api-examples/output_plot") +@no_example("express") class plot(Renderer[object]): """ Reactively render a plot object as an HTML image. diff --git a/shiny/render/renderer/_renderer.py b/shiny/render/renderer/_renderer.py index 5367ac299..ba4b28d88 100644 --- a/shiny/render/renderer/_renderer.py +++ b/shiny/render/renderer/_renderer.py @@ -122,10 +122,10 @@ class Renderer(Generic[IT]): * In Express mode, the output renderer will automatically render its UI via `.auto_output_ui(self, id: str)`. 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, `@suspend_display` can be used to - suppress the auto rendering of the UI. When using `@suspend_display` 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. + 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 diff --git a/shiny/render/transformer/_transformer.py b/shiny/render/transformer/_transformer.py index 5c95a74c7..7af1a68b9 100644 --- a/shiny/render/transformer/_transformer.py +++ b/shiny/render/transformer/_transformer.py @@ -590,7 +590,9 @@ def output_transformer( | Callable[[TransformFn[IT, P, OT]], OutputTransformer[IT, OT, P]] ): """ - Output transformer decorator + Deprecated. Please use :class:`~shiny.render.renderer.Renderer` instead. + + Output transformer decorator. This decorator method is a convenience method to generate the appropriate types and internal implementation for an overloaded renderer method. This method will provide diff --git a/shiny/session/_utils.py b/shiny/session/_utils.py index 2d399cb2d..2bbaaf1fd 100644 --- a/shiny/session/_utils.py +++ b/shiny/session/_utils.py @@ -34,7 +34,7 @@ class RenderedDeps(TypedDict): _default_session: Optional[Session] = None -@no_example +@no_example() def get_current_session() -> Optional[Session]: """ Get the current user session. @@ -76,7 +76,7 @@ def session_context(session: Optional[Session]): _current_session.reset(token) -@no_example +@no_example() def require_active_session(session: Optional[Session]) -> Session: """ Raise an exception if no Shiny session is currently active. diff --git a/shiny/types.py b/shiny/types.py index e116e8454..09b722492 100644 --- a/shiny/types.py +++ b/shiny/types.py @@ -16,7 +16,7 @@ from htmltools import TagChild -from ._docstring import add_example +from ._docstring import add_example, no_example from ._typing_extensions import NotRequired, TypedDict if TYPE_CHECKING: @@ -56,6 +56,7 @@ class FileInfo(TypedDict): @add_example(ex_dir="./api-examples/output_image") +@no_example("express") class ImgData(TypedDict): """ Return type for :class:`~shiny.render.image`. @@ -80,6 +81,7 @@ class ImgData(TypedDict): @add_example() +@no_example("express") class SafeException(Exception): """ Throw a safe exception. @@ -94,6 +96,7 @@ class SafeException(Exception): @add_example() +@no_example("express") class SilentException(Exception): """ Throw a silent exception. @@ -117,6 +120,7 @@ class SilentException(Exception): @add_example() +@no_example("express") class SilentCancelOutputException(Exception): """ Throw a silent exception and don't clear output diff --git a/shiny/ui/_bootstrap.py b/shiny/ui/_bootstrap.py index 35184f3b7..dc767442d 100644 --- a/shiny/ui/_bootstrap.py +++ b/shiny/ui/_bootstrap.py @@ -109,7 +109,7 @@ def column( return div({"class": cls}, *args, **kwargs) -@no_example +@no_example() def panel_well(*args: TagChild | TagAttrs, **kwargs: TagAttrValue) -> Tag: """ Create a well panel. @@ -233,7 +233,7 @@ def panel_title( return TagList(get_window_title(title, window_title), title) -@no_example +@no_example() def panel_fixed( *args: TagChild | TagAttrs, top: Optional[str] = None, @@ -385,7 +385,7 @@ def panel_absolute( return TagList(deps, divTag, tags.script(f'$(".draggable").draggable({dragOpts});')) -@no_example +@no_example() def help_text(*args: TagChild | TagAttrs, **kwargs: TagAttrValue) -> Tag: """ Create a help text element diff --git a/shiny/ui/_input_update.py b/shiny/ui/_input_update.py index 703db03c0..c979876c1 100644 --- a/shiny/ui/_input_update.py +++ b/shiny/ui/_input_update.py @@ -113,7 +113,7 @@ def update_action_button( # ----------------------------------------------------------------------------- # input_task_button.py # ----------------------------------------------------------------------------- -@no_example +@no_example() def update_task_button( id: str, *, @@ -213,7 +213,7 @@ def update_checkbox( session.send_input_message(id, drop_none(msg)) -@no_example +@no_example() @doc_format(note=_note) def update_switch( id: str, diff --git a/shiny/ui/_modal.py b/shiny/ui/_modal.py index 8ea91a78d..c24d164ab 100644 --- a/shiny/ui/_modal.py +++ b/shiny/ui/_modal.py @@ -20,7 +20,9 @@ @add_example(ex_dir="../api-examples/modal") def modal_button(label: TagChild, icon: TagChild = None, **kwargs: TagAttrValue) -> Tag: """ - Creates a button that will dismiss a :func:`~shiny.ui.modal`. :func:`~shiny.ui.modal_button` is usually + Creates a button that will dismiss a :func:`~shiny.ui.modal`. + + :func:`~shiny.ui.modal_button` is usually passed to the `footer` of a :func:`~shiny.ui.modal` to add a button to the footer that will close the :func:`~shiny.ui.modal`. diff --git a/shiny/ui/_navs.py b/shiny/ui/_navs.py index a692e4aaa..507218091 100644 --- a/shiny/ui/_navs.py +++ b/shiny/ui/_navs.py @@ -97,7 +97,7 @@ def tagify(self) -> None: ) -@add_example(app_file="app-basic.py") +@add_example() def nav_panel( title: TagChild, *args: TagChild, @@ -157,7 +157,7 @@ def nav_panel( ) -@no_example +@no_example() def nav_control(*args: TagChild) -> NavPanel: """ Place a control in the navigation container. @@ -189,7 +189,7 @@ def nav_control(*args: TagChild) -> NavPanel: return NavPanel(tags.li(*args)) -@no_example +@no_example() def nav_spacer() -> NavPanel: """ Create space between nav items. @@ -296,7 +296,7 @@ def menu_string_as_nav(x: str | NavSetArg) -> NavSetArg: return NavPanel(nav) -@no_example +@no_example() def nav_menu( title: TagChild, *args: NavPanel | str, @@ -404,7 +404,7 @@ def layout(self, nav: Tag, content: Tag) -> TagList | Tag: # ----------------------------------------------------------------------------- # Navigation containers # ----------------------------------------------------------------------------- -@no_example +@no_example() def navset_tab( *args: NavSetArg, id: Optional[str] = None, @@ -461,7 +461,7 @@ def navset_tab( ) -@no_example +@no_example() def navset_pill( *args: NavSetArg, id: Optional[str] = None, @@ -517,7 +517,7 @@ def navset_pill( ) -@no_example +@no_example() def navset_underline( *args: NavSetArg, id: Optional[str] = None, @@ -684,7 +684,7 @@ def layout(self, nav: Tag, content: Tag) -> Tag: ) -@no_example +@no_example() def navset_card_tab( *args: NavSetArg, id: Optional[str] = None, @@ -747,7 +747,7 @@ def navset_card_tab( ) -@no_example +@no_example() def navset_card_pill( *args: NavSetArg, id: Optional[str] = None, @@ -813,7 +813,7 @@ def navset_card_pill( ) -@no_example +@no_example() def navset_card_underline( *args: NavSetArg, id: Optional[str] = None, @@ -912,7 +912,7 @@ def layout(self, nav: TagChild, content: TagChild) -> Tag: ) -@no_example +@no_example() def navset_pill_list( *args: NavSetArg | MetadataNode, id: Optional[str] = None, @@ -1148,7 +1148,7 @@ def _make_tabs_fillable( # TODO-future; Content should not be indented unless when called from `page_navbar()` -@no_example +@no_example() def navset_bar( *args: NavSetArg | MetadataNode | Sequence[MetadataNode], title: TagChild, @@ -1393,7 +1393,7 @@ def navset_tab_card( # Deprecated 2023-12-07 -@no_example +@no_example() def nav( title: TagChild, *args: TagChild, diff --git a/shiny/ui/_notification.py b/shiny/ui/_notification.py index 033909397..33593dce2 100644 --- a/shiny/ui/_notification.py +++ b/shiny/ui/_notification.py @@ -93,7 +93,7 @@ def notification_show( return id -@no_example +@no_example() def notification_remove(id: str, *, session: Optional[Session] = None) -> str: """ Remove a notification. diff --git a/shiny/ui/_output.py b/shiny/ui/_output.py index bd4119068..2b21ae193 100644 --- a/shiny/ui/_output.py +++ b/shiny/ui/_output.py @@ -271,7 +271,7 @@ def output_text( return container(id=resolve_id(id), class_="shiny-text-output") -@no_example +@no_example() def output_code(id: str, placeholder: bool = True) -> Tag: """ Create a output container for code (monospaced text). diff --git a/shiny/ui/_page.py b/shiny/ui/_page.py index a9e4125d9..707884ab4 100644 --- a/shiny/ui/_page.py +++ b/shiny/ui/_page.py @@ -110,7 +110,7 @@ def page_sidebar( ) -@no_example +@no_example() def page_navbar( *args: NavSetArg | MetadataNode | Sequence[MetadataNode], title: Optional[str | Tag | TagList] = None, @@ -255,7 +255,7 @@ def page_navbar( ) -@no_example +@no_example() def page_fillable( *args: TagChild | TagAttrs, padding: Optional[CssUnit | list[CssUnit]] = None, @@ -407,7 +407,7 @@ def page_fixed( # TODO: implement theme (just Bootswatch for now?) -@no_example +@no_example() def page_bootstrap( *args: TagChild | TagAttrs, title: Optional[str] = None, @@ -449,7 +449,7 @@ def page_bootstrap( ) -@no_example +@no_example() def page_auto( *args: TagChild | TagAttrs, title: str | MISSING_TYPE = MISSING, @@ -633,7 +633,7 @@ def _page_auto_fixed( ) -@no_example +@no_example() def page_output(id: str) -> Tag: """ Create a page container where the entire body is a UI output. diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index fb2e0b8fe..90de9507a 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -41,7 +41,7 @@ ) -@no_example +@no_example() class Sidebar: """ A sidebar object @@ -579,7 +579,7 @@ def _sidebar_init_js() -> Tag: # Deprecated 2023-06-13 # Includes: DeprecatedPanelSidebar -@no_example +@no_example() def panel_sidebar( *args: TagChild | TagAttrs, width: int = 4, @@ -602,7 +602,7 @@ def panel_sidebar( # Deprecated 2023-06-13 # Includes: DeprecatedPanelMain -@no_example +@no_example() def panel_main( *args: TagChild | TagAttrs, width: int = 8, diff --git a/shiny/ui/_valuebox.py b/shiny/ui/_valuebox.py index ef35ec55f..b4bc7a8f1 100644 --- a/shiny/ui/_valuebox.py +++ b/shiny/ui/_valuebox.py @@ -227,7 +227,7 @@ class ValueBoxTheme: bg: str | None -@no_example +@no_example() def value_box_theme( name: Optional[str] = None, *, diff --git a/shiny/ui/fill/_fill.py b/shiny/ui/fill/_fill.py index d1c26ac99..6c210c038 100644 --- a/shiny/ui/fill/_fill.py +++ b/shiny/ui/fill/_fill.py @@ -34,6 +34,33 @@ def as_fillable_container( tag: TagT, ) -> TagT: + """ + Coerce a tag to a fillable container. + + Filling layouts are built on the foundation of _fillable containers_ and _fill + items_ (_fill carriers_ are both _fillable containers_ and _fill items_). This is + why most UI components (e.g., :func:`~shiny.ui.card`, + :func:`~shiny.ui.layout_sidebar`) possess both `fillable` and `fill` arguments (to + control their fill behavior). However, sometimes it's useful to add, remove, and/or + test fillable/fill properties on arbitrary :class:`~htmltools.Tag`, which these + functions are designed to do. + + Parameters + ---------- + tag + a Tag object. + + Returns + ------- + : + A copy of the original :class:`~htmltools.Tag` object (`tag`) with additional + attributes (and an :class:`~htmltools.HTMLDependency`). + + See Also + -------- + * :func:`~shiny.ui.fill.as_fill_item` + * :func:`~shiny.ui.fill.remove_all_fill` + """ res = copy(tag) res.add_class(FILL_CONTAINER_CLASS) res.append(fill_dependency()) @@ -77,7 +104,7 @@ def as_fill_item( return res -@no_example +@no_example() def remove_all_fill( tag: TagT, ) -> TagT: diff --git a/tests/playwright/conftest.py b/tests/playwright/conftest.py index ce7de4cfd..3554b6595 100644 --- a/tests/playwright/conftest.py +++ b/tests/playwright/conftest.py @@ -20,7 +20,7 @@ __all__ = ( "ShinyAppProc", "create_app_fixture", - "create_doc_example_fixture", + "create_doc_example_core_fixture", "create_example_fixture", "local_app", "run_shiny_app", @@ -191,18 +191,44 @@ def fixture_func(): )(fixture_func) -def create_example_fixture(example_name: str, scope: str = "module"): +def create_example_fixture( + example_name: str, + example_file: str = "app.py", + scope: str = "module", +): """Used to create app fixtures from apps in py-shiny/examples""" - return create_app_fixture(here_root / "examples" / example_name / "app.py", scope) + return create_app_fixture( + here_root / "examples" / example_name / example_file, scope + ) -def create_doc_example_fixture(example_name: str, scope: str = "module"): +def create_doc_example_fixture( + example_name: str, + example_file: str = "app.py", + scope: str = "module", +): """Used to create app fixtures from apps in py-shiny/shiny/api-examples""" return create_app_fixture( - here_root / "shiny/api-examples" / example_name / "app.py", scope + here_root / "shiny/api-examples" / example_name / example_file, scope ) +def create_doc_example_core_fixture( + example_name: str, + scope: str = "module", +): + """Used to create app fixtures from ``app-core.py`` example apps in py-shiny/shiny/api-examples""" + return create_doc_example_fixture(example_name, "app-core.py", scope) + + +def create_doc_example_express_fixture( + example_name: str, + scope: str = "module", +): + """Used to create app fixtures from ``app-express.py`` example apps in py-shiny/shiny/api-examples""" + return create_doc_example_fixture(example_name, "app-express.py", scope) + + def create_deploys_fixture(app: Union[PurePath, str], scope: str = "module"): """Used to create app fixtures from apps in tests/playwright/deploys/apps""" return create_app_fixture( diff --git a/tests/playwright/examples/example_apps.py b/tests/playwright/examples/example_apps.py index d09007e97..a2e56fdd3 100644 --- a/tests/playwright/examples/example_apps.py +++ b/tests/playwright/examples/example_apps.py @@ -20,10 +20,15 @@ def get_apps(path: str) -> typing.List[str]: for folder in os.listdir(full_path): folder_path = os.path.join(full_path, folder) if os.path.isdir(folder_path): - app_path = os.path.join(folder_path, "app.py") - if os.path.isfile(app_path): - # Return relative app path - app_paths.append(os.path.join(path, folder, "app.py")) + folder_files = os.listdir(folder_path) + for file in folder_files: + if os.path.isdir(os.path.join(folder_path, file)): + continue + if not file.endswith(".py"): + continue + if file == "app.py" or file.startswith("app-"): + # Return relative app path + app_paths.append(os.path.join(path, folder, file)) return app_paths @@ -69,6 +74,9 @@ def get_apps(path: str) -> typing.List[str]: "render_express": [*express_warnings], } app_allow_external_errors: typing.List[str] = [ + # TODO-garrick-future: Remove after fixing sidebar max_height_mobile warning + "UserWarning: The `shiny.ui.sidebar(max_height_mobile=)`", + "res = self.fn(*self.args, **self.kwargs)", # if shiny express app detected "Detected Shiny Express app", # pandas >= 2.2.0 diff --git a/tests/playwright/shiny/components/layout_columns/test_layout_columns.py b/tests/playwright/shiny/components/layout_columns/test_layout_columns.py index 97848e683..38f421c22 100644 --- a/tests/playwright/shiny/components/layout_columns/test_layout_columns.py +++ b/tests/playwright/shiny/components/layout_columns/test_layout_columns.py @@ -2,12 +2,12 @@ from typing import TypeVar -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from playwright.sync_api import Page T = TypeVar("T") -app = create_doc_example_fixture("layout_columns") +app = create_doc_example_core_fixture("layout_columns") def not_null(x: T | None) -> T: diff --git a/tests/playwright/shiny/components/test_sidebar.py b/tests/playwright/shiny/components/test_sidebar.py index a7c6eed01..3c0eebd34 100644 --- a/tests/playwright/shiny/components/test_sidebar.py +++ b/tests/playwright/shiny/components/test_sidebar.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import OutputTextVerbatim, Sidebar from playwright.sync_api import Page -app = create_doc_example_fixture("sidebar") +app = create_doc_example_core_fixture("sidebar") def test_sidebar_position_and_open(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_action_button_link.py b/tests/playwright/shiny/inputs/test_input_action_button_link.py index ae70bc5bb..66edf7682 100644 --- a/tests/playwright/shiny/inputs/test_input_action_button_link.py +++ b/tests/playwright/shiny/inputs/test_input_action_button_link.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputActionButton, InputActionLink from playwright.sync_api import Page, expect -app = create_doc_example_fixture("update_action_button") +app = create_doc_example_core_fixture("update_action_button") def test_input_action_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_checkbox.py b/tests/playwright/shiny/inputs/test_input_checkbox.py index 178b6a80c..687c5aca2 100644 --- a/tests/playwright/shiny/inputs/test_input_checkbox.py +++ b/tests/playwright/shiny/inputs/test_input_checkbox.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputCheckbox, OutputUi from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_checkbox") +app = create_doc_example_core_fixture("input_checkbox") def test_input_checkbox_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_checkbox_group.py b/tests/playwright/shiny/inputs/test_input_checkbox_group.py index b887856c4..aab1262c2 100644 --- a/tests/playwright/shiny/inputs/test_input_checkbox_group.py +++ b/tests/playwright/shiny/inputs/test_input_checkbox_group.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputCheckboxGroup from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_checkbox_group") +app = create_doc_example_core_fixture("input_checkbox_group") def test_input_checkbox_group_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_date.py b/tests/playwright/shiny/inputs/test_input_date.py index 4f3180120..ceac65dd1 100644 --- a/tests/playwright/shiny/inputs/test_input_date.py +++ b/tests/playwright/shiny/inputs/test_input_date.py @@ -4,11 +4,11 @@ import typing from typing import Literal -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputDate from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_date") +app = create_doc_example_core_fixture("input_date") def expect_date( diff --git a/tests/playwright/shiny/inputs/test_input_date_range.py b/tests/playwright/shiny/inputs/test_input_date_range.py index 368099da8..778f6d1d7 100644 --- a/tests/playwright/shiny/inputs/test_input_date_range.py +++ b/tests/playwright/shiny/inputs/test_input_date_range.py @@ -4,11 +4,11 @@ import typing from typing import Literal -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputDateRange from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_date_range") +app = create_doc_example_core_fixture("input_date_range") def expect_date_range( diff --git a/tests/playwright/shiny/inputs/test_input_numeric.py b/tests/playwright/shiny/inputs/test_input_numeric.py index 9bcd828bc..503be64c3 100644 --- a/tests/playwright/shiny/inputs/test_input_numeric.py +++ b/tests/playwright/shiny/inputs/test_input_numeric.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputNumeric, OutputTextVerbatim from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_numeric") +app = create_doc_example_core_fixture("input_numeric") # def shinypage(page: Page) -> Page: diff --git a/tests/playwright/shiny/inputs/test_input_password.py b/tests/playwright/shiny/inputs/test_input_password.py index 6f747a504..ea83fe17d 100644 --- a/tests/playwright/shiny/inputs/test_input_password.py +++ b/tests/playwright/shiny/inputs/test_input_password.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputActionButton, InputPassword, OutputTextVerbatim from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_password") +app = create_doc_example_core_fixture("input_password") def test_input_password_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_radio_buttons.py b/tests/playwright/shiny/inputs/test_input_radio_buttons.py index 5a0b0b650..b19d888b0 100644 --- a/tests/playwright/shiny/inputs/test_input_radio_buttons.py +++ b/tests/playwright/shiny/inputs/test_input_radio_buttons.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputRadioButtons from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_radio_buttons") +app = create_doc_example_core_fixture("input_radio_buttons") def test_input_checkbox_group_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_select.py b/tests/playwright/shiny/inputs/test_input_select.py index eb2a66728..9d4316912 100644 --- a/tests/playwright/shiny/inputs/test_input_select.py +++ b/tests/playwright/shiny/inputs/test_input_select.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputSelect from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_select") +app = create_doc_example_core_fixture("input_select") def test_input_select_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_selectize.py b/tests/playwright/shiny/inputs/test_input_selectize.py index aeddc7918..a99d2c361 100644 --- a/tests/playwright/shiny/inputs/test_input_selectize.py +++ b/tests/playwright/shiny/inputs/test_input_selectize.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputSelectize from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_select") +app = create_doc_example_core_fixture("input_select") def test_input_selectize_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_slider.py b/tests/playwright/shiny/inputs/test_input_slider.py index 8303ca71a..5da4cfaac 100644 --- a/tests/playwright/shiny/inputs/test_input_slider.py +++ b/tests/playwright/shiny/inputs/test_input_slider.py @@ -1,9 +1,9 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputSlider, OutputTextVerbatim from playwright.sync_api import Page, expect -slider_app = create_doc_example_fixture("input_slider") -template_app = create_doc_example_fixture("template") +slider_app = create_doc_example_core_fixture("input_slider") +template_app = create_doc_example_core_fixture("template") def test_input_slider_kitchen(page: Page, slider_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_switch.py b/tests/playwright/shiny/inputs/test_input_switch.py index 50d38398b..39034bf2f 100644 --- a/tests/playwright/shiny/inputs/test_input_switch.py +++ b/tests/playwright/shiny/inputs/test_input_switch.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputSwitch, OutputUi from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_switch") +app = create_doc_example_core_fixture("input_switch") def test_input_switch_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_text.py b/tests/playwright/shiny/inputs/test_input_text.py index bdd29f203..d2b045c69 100644 --- a/tests/playwright/shiny/inputs/test_input_text.py +++ b/tests/playwright/shiny/inputs/test_input_text.py @@ -1,10 +1,10 @@ import re -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputText, OutputTextVerbatim from playwright.sync_api import Page, expect -app = create_doc_example_fixture("input_text") +app = create_doc_example_core_fixture("input_text") def test_input_text_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/test_input_text_area.py b/tests/playwright/shiny/inputs/test_input_text_area.py index 97addf556..40ab6ccd0 100644 --- a/tests/playwright/shiny/inputs/test_input_text_area.py +++ b/tests/playwright/shiny/inputs/test_input_text_area.py @@ -1,10 +1,10 @@ import re -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputTextArea, OutputTextVerbatim from playwright.sync_api import Locator, Page, expect -app = create_doc_example_fixture("input_text_area") +app = create_doc_example_core_fixture("input_text_area") default_txt = "Data summary\nwith\nmultiple\nlines" diff --git a/tests/playwright/shiny/outputs/test_output_image.py b/tests/playwright/shiny/outputs/test_output_image.py index c2051263e..76c18a3b9 100644 --- a/tests/playwright/shiny/outputs/test_output_image.py +++ b/tests/playwright/shiny/outputs/test_output_image.py @@ -1,10 +1,10 @@ import re -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import OutputImage from playwright.sync_api import Page -app = create_doc_example_fixture("output_image") +app = create_doc_example_core_fixture("output_image") def test_output_image_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/outputs/test_output_plot.py b/tests/playwright/shiny/outputs/test_output_plot.py index df166aef8..5999badf5 100644 --- a/tests/playwright/shiny/outputs/test_output_plot.py +++ b/tests/playwright/shiny/outputs/test_output_plot.py @@ -1,10 +1,10 @@ import re -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import OutputPlot from playwright.sync_api import Page -app = create_doc_example_fixture("output_plot") +app = create_doc_example_core_fixture("output_plot") def test_output_plot_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/outputs/test_output_table.py b/tests/playwright/shiny/outputs/test_output_table.py index c773ce911..72e55efdb 100644 --- a/tests/playwright/shiny/outputs/test_output_table.py +++ b/tests/playwright/shiny/outputs/test_output_table.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import OutputTable from playwright.sync_api import Page -app = create_doc_example_fixture("output_table") +app = create_doc_example_core_fixture("output_table") def test_output_plot_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/outputs/test_output_text.py b/tests/playwright/shiny/outputs/test_output_text.py index 72a16c434..bca59a0a4 100644 --- a/tests/playwright/shiny/outputs/test_output_text.py +++ b/tests/playwright/shiny/outputs/test_output_text.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputText, OutputText, OutputTextVerbatim from playwright.sync_api import Page -app = create_doc_example_fixture("output_text") +app = create_doc_example_core_fixture("output_text") def test_output_text_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/outputs/test_output_ui.py b/tests/playwright/shiny/outputs/test_output_ui.py index fc5a93804..44508b6d7 100644 --- a/tests/playwright/shiny/outputs/test_output_ui.py +++ b/tests/playwright/shiny/outputs/test_output_ui.py @@ -1,8 +1,8 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from controls import InputActionButton, InputSlider, InputText, OutputUi from playwright.sync_api import Page, expect -app = create_doc_example_fixture("output_ui") +app = create_doc_example_core_fixture("output_ui") def test_output_ui_kitchen(page: Page, app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/shiny-express/render_express/test_render_express.py b/tests/playwright/shiny/shiny-express/render_express/test_render_express.py index f60f31478..5abd75a4e 100644 --- a/tests/playwright/shiny/shiny-express/render_express/test_render_express.py +++ b/tests/playwright/shiny/shiny-express/render_express/test_render_express.py @@ -1,7 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_fixture +from conftest import ShinyAppProc, create_doc_example_core_fixture from playwright.sync_api import Page, expect -app = create_doc_example_fixture("render_express") +app = create_doc_example_core_fixture("render_express") EXPECT_TIMEOUT = 30 * 1000