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