From 45d35a6fa2b7c0732dd25b26efeea7f52148688a Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:06:09 -0500 Subject: [PATCH 01/23] ex(page_sidebar): remove `@output` decorator --- shiny/api-examples/page_sidebar/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shiny/api-examples/page_sidebar/app.py b/shiny/api-examples/page_sidebar/app.py index ba84c3fe8..8ce58f8b8 100644 --- a/shiny/api-examples/page_sidebar/app.py +++ b/shiny/api-examples/page_sidebar/app.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) From b17f0f6400b835e80e59f002e9f5da15496ff07e Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:06:42 -0500 Subject: [PATCH 02/23] expressify: api-examples/page_sidebar --- .../api-examples/page_sidebar/app-express.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 shiny/api-examples/page_sidebar/app-express.py 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..54696d6fd --- /dev/null +++ b/shiny/api-examples/page_sidebar/app-express.py @@ -0,0 +1,20 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny import render +from shiny.express import input, ui + +ui.page_opts(page_fn=page_fixed) + +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 From 37f1fe9e561c893fe66eae9ca79a4560032b99d6 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:10:36 -0500 Subject: [PATCH 03/23] expressify: api-examples/sidebar --- shiny/api-examples/sidebar/app-express.py | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 shiny/api-examples/sidebar/app-express.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..16665fd13 --- /dev/null +++ b/shiny/api-examples/sidebar/app-express.py @@ -0,0 +1,43 @@ +from shiny import render +from shiny.express import input, 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()}" From 5ff52bbfd34eedba34d574996c0e71971a893fad Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:38:20 -0500 Subject: [PATCH 04/23] expressify: api-examples/layout_column_wrap --- .../layout_column_wrap/app-express.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 shiny/api-examples/layout_column_wrap/app-express.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..a5731350d --- /dev/null +++ b/shiny/api-examples/layout_column_wrap/app-express.py @@ -0,0 +1,18 @@ +import shiny +from shiny.express import ui + +a_card = shiny.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 From efd57b234b6a1301946de35dec30005e1c2c5a17 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:43:21 -0500 Subject: [PATCH 05/23] expressify: api-examples/layout_columns --- .../layout_columns/app-express.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 shiny/api-examples/layout_columns/app-express.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..1362b01eb --- /dev/null +++ b/shiny/api-examples/layout_columns/app-express.py @@ -0,0 +1,35 @@ +from model_plots import * # model plots and cards + +from shiny import render +from shiny.express import input, 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() From 6c09496f75f81b849e7df0a3eeaf29668cc6334a Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:44:39 -0500 Subject: [PATCH 06/23] expressify: api-examples/layout_sidebar --- .../api-examples/layout_sidebar/app-express.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 shiny/api-examples/layout_sidebar/app-express.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..93e80442f --- /dev/null +++ b/shiny/api-examples/layout_sidebar/app-express.py @@ -0,0 +1,18 @@ +import matplotlib.pyplot as plt +import numpy as np + +from shiny import render +from shiny.express import input, 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 From d941199bf4e8f716377644aa84e6bf947afc0982 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 10:45:26 -0500 Subject: [PATCH 07/23] expressify: api-examples/markdown --- shiny/api-examples/markdown/app-express.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 shiny/api-examples/markdown/app-express.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..11cb38c87 --- /dev/null +++ b/shiny/api-examples/markdown/app-express.py @@ -0,0 +1,13 @@ +from shiny.express import input, ui + +ui.markdown( + """ + # Hello World + + This is **markdown** and here is some `code`: + + ```python + print('Hello world!') + ``` + """ +) From 930759c16480ca3c1a2bd3d69b1445f1a0fc61f7 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 12:16:43 -0500 Subject: [PATCH 08/23] expressify: api-examples/panel_absolute --- shiny/api-examples/panel_absolute/app-express.py | 8 ++++++++ shiny/api-examples/panel_absolute/app.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 shiny/api-examples/panel_absolute/app-express.py 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_absolute/app.py b/shiny/api-examples/panel_absolute/app.py index 10fab125e..3340e5f93 100644 --- a/shiny/api-examples/panel_absolute/app.py +++ b/shiny/api-examples/panel_absolute/app.py @@ -9,7 +9,7 @@ draggable=True, width="300px", right="50px", - top="50%", + top="25%", ), ) From 81f0314b3186609f8403123256be6e6245008d78 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Tue, 16 Jan 2024 12:18:28 -0500 Subject: [PATCH 09/23] expressify: api-examples/panel_conditional --- shiny/api-examples/panel_conditional/app-express.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 shiny/api-examples/panel_conditional/app-express.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..8c81ea531 --- /dev/null +++ b/shiny/api-examples/panel_conditional/app-express.py @@ -0,0 +1,12 @@ +from shiny.express import input, 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"]) From 40233974805715f8050d420a8f14ead299e3028a Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Wed, 24 Jan 2024 17:21:31 -0500 Subject: [PATCH 10/23] Apply suggestions from code review Co-authored-by: Carson Sievert --- shiny/api-examples/layout_column_wrap/app-express.py | 5 +++-- shiny/api-examples/layout_columns/app-express.py | 3 +-- shiny/api-examples/layout_sidebar/app-express.py | 3 +-- shiny/api-examples/markdown/app-express.py | 2 +- shiny/api-examples/page_sidebar/app-express.py | 3 +-- shiny/api-examples/sidebar/app-express.py | 3 +-- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/shiny/api-examples/layout_column_wrap/app-express.py b/shiny/api-examples/layout_column_wrap/app-express.py index a5731350d..0754d8b74 100644 --- a/shiny/api-examples/layout_column_wrap/app-express.py +++ b/shiny/api-examples/layout_column_wrap/app-express.py @@ -1,7 +1,8 @@ -import shiny from shiny.express import ui -a_card = shiny.ui.card("A simple card") +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): diff --git a/shiny/api-examples/layout_columns/app-express.py b/shiny/api-examples/layout_columns/app-express.py index 1362b01eb..593ba321d 100644 --- a/shiny/api-examples/layout_columns/app-express.py +++ b/shiny/api-examples/layout_columns/app-express.py @@ -1,7 +1,6 @@ from model_plots import * # model plots and cards -from shiny import render -from shiny.express import input, ui +from shiny.express import input, render, ui ui.page_opts(title="Model Dashboard") diff --git a/shiny/api-examples/layout_sidebar/app-express.py b/shiny/api-examples/layout_sidebar/app-express.py index 93e80442f..10482a396 100644 --- a/shiny/api-examples/layout_sidebar/app-express.py +++ b/shiny/api-examples/layout_sidebar/app-express.py @@ -1,8 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import render -from shiny.express import input, ui +from shiny.express import input, render, ui with ui.layout_sidebar(): with ui.sidebar(): diff --git a/shiny/api-examples/markdown/app-express.py b/shiny/api-examples/markdown/app-express.py index 11cb38c87..f1db29501 100644 --- a/shiny/api-examples/markdown/app-express.py +++ b/shiny/api-examples/markdown/app-express.py @@ -1,4 +1,4 @@ -from shiny.express import input, ui +from shiny.express import ui ui.markdown( """ diff --git a/shiny/api-examples/page_sidebar/app-express.py b/shiny/api-examples/page_sidebar/app-express.py index 54696d6fd..1b0beab3d 100644 --- a/shiny/api-examples/page_sidebar/app-express.py +++ b/shiny/api-examples/page_sidebar/app-express.py @@ -1,8 +1,7 @@ import matplotlib.pyplot as plt import numpy as np -from shiny import render -from shiny.express import input, ui +from shiny.express import input, render, ui ui.page_opts(page_fn=page_fixed) diff --git a/shiny/api-examples/sidebar/app-express.py b/shiny/api-examples/sidebar/app-express.py index 16665fd13..17e80bb47 100644 --- a/shiny/api-examples/sidebar/app-express.py +++ b/shiny/api-examples/sidebar/app-express.py @@ -1,5 +1,4 @@ -from shiny import render -from shiny.express import input, ui +from shiny.express import input, render, ui ui.page_opts(fillable=True) From 4f602f8e24530959b2ef52c8ebf486578621aadf Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Wed, 24 Jan 2024 17:21:47 -0500 Subject: [PATCH 11/23] Update shiny/api-examples/panel_conditional/app-express.py Co-authored-by: Carson Sievert --- shiny/api-examples/panel_conditional/app-express.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/api-examples/panel_conditional/app-express.py b/shiny/api-examples/panel_conditional/app-express.py index 8c81ea531..8f5fbb229 100644 --- a/shiny/api-examples/panel_conditional/app-express.py +++ b/shiny/api-examples/panel_conditional/app-express.py @@ -1,4 +1,4 @@ -from shiny.express import input, ui +from shiny.express import ui ui.input_checkbox("show", "Show radio buttons", False) From 9c5a87c76d03387d979108723b1f395af85b8a34 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Thu, 25 Jan 2024 08:41:56 -0400 Subject: [PATCH 12/23] Linting --- shiny/api-examples/layout_columns/app-express.py | 8 ++++++-- shiny/api-examples/page_sidebar/app-express.py | 2 -- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/shiny/api-examples/layout_columns/app-express.py b/shiny/api-examples/layout_columns/app-express.py index 593ba321d..768056d5c 100644 --- a/shiny/api-examples/layout_columns/app-express.py +++ b/shiny/api-examples/layout_columns/app-express.py @@ -1,6 +1,10 @@ -from model_plots import * # model plots and cards +from model_plots import ( + plot_accuracy_over_time, + plot_feature_importance, + plot_loss_over_time, +) -from shiny.express import input, render, ui +from shiny.express import render, ui ui.page_opts(title="Model Dashboard") diff --git a/shiny/api-examples/page_sidebar/app-express.py b/shiny/api-examples/page_sidebar/app-express.py index 1b0beab3d..4781f851c 100644 --- a/shiny/api-examples/page_sidebar/app-express.py +++ b/shiny/api-examples/page_sidebar/app-express.py @@ -3,8 +3,6 @@ from shiny.express import input, render, ui -ui.page_opts(page_fn=page_fixed) - with ui.sidebar(): ui.input_slider("n", "N", min=0, max=100, value=20) From e24d44e60493a7923efad7a37ae265142dd1cd44 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Thu, 25 Jan 2024 08:59:47 -0400 Subject: [PATCH 13/23] expressify-input-examples (#1057) --- .../input_action_link/app-express.py | 20 ++++++++ .../input_checkbox/app-express.py | 8 ++++ .../input_checkbox_group/app-express.py | 18 +++++++ shiny/api-examples/input_date/app-express.py | 24 ++++++++++ .../input_date_range/app-express.py | 27 +++++++++++ shiny/api-examples/input_file/app-express.py | 48 +++++++++++++++++++ .../api-examples/input_numeric/app-express.py | 8 ++++ .../input_password/app-express.py | 11 +++++ .../input_radio_buttons/app-express.py | 15 ++++++ .../api-examples/input_select/app-express.py | 16 +++++++ .../input_selectize/app-express.py | 45 +++++++++++++++++ shiny/api-examples/input_selectize/app.py | 4 +- .../api-examples/input_slider/app-express.py | 16 +++++++ .../api-examples/input_switch/app-express.py | 8 ++++ shiny/api-examples/input_switch/app.py | 4 +- shiny/api-examples/input_text/app-express.py | 8 ++++ .../input_text_area/app-express.py | 25 ++++++++++ 17 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 shiny/api-examples/input_action_link/app-express.py create mode 100644 shiny/api-examples/input_checkbox/app-express.py create mode 100644 shiny/api-examples/input_checkbox_group/app-express.py create mode 100644 shiny/api-examples/input_date/app-express.py create mode 100644 shiny/api-examples/input_date_range/app-express.py create mode 100644 shiny/api-examples/input_file/app-express.py create mode 100644 shiny/api-examples/input_numeric/app-express.py create mode 100644 shiny/api-examples/input_password/app-express.py create mode 100644 shiny/api-examples/input_radio_buttons/app-express.py create mode 100644 shiny/api-examples/input_select/app-express.py create mode 100644 shiny/api-examples/input_selectize/app-express.py create mode 100644 shiny/api-examples/input_slider/app-express.py create mode 100644 shiny/api-examples/input_switch/app-express.py create mode 100644 shiny/api-examples/input_text/app-express.py create mode 100644 shiny/api-examples/input_text_area/app-express.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-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-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-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-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-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-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-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-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-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-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_selectize/app.py b/shiny/api-examples/input_selectize/app.py index bc2dd9bd9..23a63d376 100644 --- a/shiny/api-examples/input_selectize/app.py +++ b/shiny/api-examples/input_selectize/app.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_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-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_switch/app.py b/shiny/api-examples/input_switch/app.py index a438d8195..f05cba431 100644 --- a/shiny/api-examples/input_switch/app.py +++ b/shiny/api-examples/input_switch/app.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_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-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() From 3dd82579456e5e7891d9924bcbffc64463825d96 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Thu, 25 Jan 2024 11:34:15 -0500 Subject: [PATCH 14/23] Update `@add_examples()` to work with split core/express docs --- docs/Makefile | 4 +-- shiny/_docstring.py | 77 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 77694d6f9..f1f9e0360 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -51,8 +51,8 @@ quartodoc: $(PYBIN) ## Build qmd files for API docs $(eval export IN_QUARTODOC=true) . $(PYBIN)/activate \ && quartodoc interlinks \ - && quartodoc build --config _quartodoc-core.yml --verbose \ - && quartodoc build --config _quartodoc-express.yml --verbose + && SHINY_MODE="core" quartodoc build --config _quartodoc-core.yml --verbose \ + && SHINY_MODE="express" quartodoc build --config _quartodoc-express.yml --verbose site: ## Build website . $(PYBIN)/activate \ diff --git a/shiny/_docstring.py b/shiny/_docstring.py index a45a4edce..cac1688a4 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -50,7 +50,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 +60,14 @@ 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/``. + * Examples for the ``shiny.express`` subpackage are in ``shiny/express/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: @@ -88,25 +96,29 @@ 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" + example_file = app_choose_core_or_express( + os.path.join(example_dir, app_file_name) + ) 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 +162,53 @@ 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 + + +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 FileNotFoundError( + f"Could not find an Express app file named either '{os.path.basename(app_path)}' " + + f"or '{os.path.basename(app_path_express)}' in {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 FileNotFoundError( + f"Could not find an example app file named '{os.path.basename(app_path)}' " + + f"in {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__)) From c2c3f4306aca7ea6195f4fa1fe125267f5c304c7 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Thu, 25 Jan 2024 17:30:00 -0500 Subject: [PATCH 15/23] Add `@no_example_express()` re-decorator --- shiny/_docstring.py | 67 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/shiny/_docstring.py b/shiny/_docstring.py index cac1688a4..fec1df04f 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -2,7 +2,8 @@ import os import sys -from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, TypeVar def find_api_examples_dir(start_dir: str) -> Optional[str]: @@ -29,6 +30,25 @@ def no_example(func: F) -> F: return func +def no_example_express(decorator: Callable[..., F]) -> F | Callable[..., F]: + """ + Prevent ``@add_example()`` from throwing an error about missing Express examples. + """ + + @wraps(decorator) + def wrapper_decorator(*args: Any, **kwargs: Any) -> F: + try: + # Apply the potentially problematic decorator + return decorator(*args, **kwargs) + except ExpressExampleNotFoundException: + # If an error occurs, return the original function + if args and callable(args[0]): + return args[0] + raise + + return wrapper_decorator + + # This class is used to mark docstrings when @add_example() is used, so that an error # will be thrown if @doc_format() is used afterward. This is to avoid an error when # the example contains curly braces -- the @doc_format() decorator will try to evaluate @@ -177,6 +197,39 @@ def is_express_app(app_path: str) -> bool: 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") or "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" @@ -190,9 +243,9 @@ def app_choose_core_or_express(app_path: Optional[str] = None) -> str: app_path_express = f"{path}-express{ext}" if not is_express_app(app_path_express): - raise FileNotFoundError( - f"Could not find an Express app file named either '{os.path.basename(app_path)}' " - + f"or '{os.path.basename(app_path_express)}' in {os.path.dirname(app_path)}." + raise ExpressExampleNotFoundException( + [os.path.basename(app_path), os.path.basename(app_path_express)], + os.path.dirname(app_path), ) return app_path_express @@ -201,9 +254,9 @@ def app_choose_core_or_express(app_path: Optional[str] = None) -> str: app_path = app_path.replace("app.py", "app-core.py") if not os.path.exists(app_path): - raise FileNotFoundError( - f"Could not find an example app file named '{os.path.basename(app_path)}' " - + f"in {os.path.dirname(app_path)}." + raise ExampleNotFoundException( + os.path.basename(app_path), + os.path.dirname(app_path), ) return app_path From 14b0836dda092a6c3b67ea4c15fc08f718200f2c Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Thu, 25 Jan 2024 17:30:15 -0500 Subject: [PATCH 16/23] Use `@no_example_express()` in a few places --- shiny/reactive/_core.py | 4 ++-- shiny/types.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shiny/reactive/_core.py b/shiny/reactive/_core.py index d7f0219c8..24c9de1ae 100644 --- a/shiny/reactive/_core.py +++ b/shiny/reactive/_core.py @@ -22,7 +22,7 @@ from .. import _utils from .._datastructures import PriorityQueueFIFO -from .._docstring import add_example, no_example +from .._docstring import add_example, no_example, no_example_express from ..types import MISSING, MISSING_TYPE if TYPE_CHECKING: @@ -199,7 +199,7 @@ def isolate(self) -> Generator[None, None, None]: _reactive_environment = ReactiveEnvironment() -@add_example() +@no_example_express(add_example()) @contextlib.contextmanager def isolate() -> Generator[None, None, None]: """ diff --git a/shiny/types.py b/shiny/types.py index e116e8454..963144412 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_express from ._typing_extensions import NotRequired, TypedDict if TYPE_CHECKING: @@ -55,7 +55,7 @@ class FileInfo(TypedDict): """The path to the file on the server.""" -@add_example(ex_dir="./api-examples/output_image") +@no_example_express(add_example(ex_dir="./api-examples/output_image")) class ImgData(TypedDict): """ Return type for :class:`~shiny.render.image`. @@ -79,7 +79,7 @@ class ImgData(TypedDict): """TODO """ -@add_example() +@no_example_express(add_example()) class SafeException(Exception): """ Throw a safe exception. @@ -93,7 +93,7 @@ class SafeException(Exception): pass -@add_example() +@no_example_express(add_example()) class SilentException(Exception): """ Throw a silent exception. @@ -116,7 +116,7 @@ class SilentException(Exception): pass -@add_example() +@no_example_express(add_example()) class SilentCancelOutputException(Exception): """ Throw a silent exception and don't clear output From f9c9ddd403be4ebed2ccbb8374664dc8f9042468 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 26 Jan 2024 11:41:51 -0400 Subject: [PATCH 17/23] Rename app.py examples to app-core.py (#1076) --- shiny/api-examples/Module/{app.py => app-core.py} | 0 shiny/api-examples/Progress/{app.py => app-core.py} | 0 shiny/api-examples/Renderer/{app.py => app-core.py} | 0 shiny/api-examples/SafeException/{app.py => app-core.py} | 0 .../SilentCancelOutputException/{app.py => app-core.py} | 0 shiny/api-examples/SilentException/{app.py => app-core.py} | 0 shiny/api-examples/Value/{app.py => app-core.py} | 0 shiny/api-examples/accordion/{app.py => app-core.py} | 0 shiny/api-examples/accordion_panel/{app.py => app-core.py} | 0 shiny/api-examples/as_fill_item/{app.py => app-core.py} | 0 shiny/api-examples/as_fillable_container/{app.py => app-core.py} | 0 shiny/api-examples/calc/{app.py => app-core.py} | 0 shiny/api-examples/card/{app.py => app-core.py} | 0 shiny/api-examples/card_body/{app.py => app-core.py} | 0 shiny/api-examples/card_footer/{app.py => app-core.py} | 0 shiny/api-examples/card_header/{app.py => app-core.py} | 0 shiny/api-examples/close/{app.py => app-core.py} | 0 shiny/api-examples/data_frame/{app.py => app-core.py} | 0 shiny/api-examples/download/{app.py => app-core.py} | 0 shiny/api-examples/download_button/{app.py => app-core.py} | 0 shiny/api-examples/download_link/{app.py => app-core.py} | 0 shiny/api-examples/dynamic_route/{app.py => app-core.py} | 0 shiny/api-examples/effect/{app.py => app-core.py} | 0 shiny/api-examples/event/{app.py => app-core.py} | 0 shiny/api-examples/extended_task/{app.py => app-core.py} | 0 shiny/api-examples/file_reader/{app.py => app-core.py} | 0 shiny/api-examples/include_css/{app.py => app-core.py} | 0 shiny/api-examples/include_js/{app.py => app-core.py} | 0 shiny/api-examples/input_action_button/{app.py => app-core.py} | 0 shiny/api-examples/input_action_link/{app.py => app-core.py} | 0 shiny/api-examples/input_checkbox/{app.py => app-core.py} | 0 shiny/api-examples/input_checkbox_group/{app.py => app-core.py} | 0 shiny/api-examples/input_date/{app.py => app-core.py} | 0 shiny/api-examples/input_date_range/{app.py => app-core.py} | 0 shiny/api-examples/input_file/{app.py => app-core.py} | 0 shiny/api-examples/input_numeric/{app.py => app-core.py} | 0 shiny/api-examples/input_password/{app.py => app-core.py} | 0 shiny/api-examples/input_radio_buttons/{app.py => app-core.py} | 0 shiny/api-examples/input_select/{app.py => app-core.py} | 0 shiny/api-examples/input_selectize/{app.py => app-core.py} | 0 shiny/api-examples/input_slider/{app.py => app-core.py} | 0 shiny/api-examples/input_switch/{app.py => app-core.py} | 0 shiny/api-examples/input_text/{app.py => app-core.py} | 0 shiny/api-examples/input_text_area/{app.py => app-core.py} | 0 shiny/api-examples/insert_accordion_panel/{app.py => app-core.py} | 0 shiny/api-examples/insert_ui/{app.py => app-core.py} | 0 shiny/api-examples/invalidate_later/{app.py => app-core.py} | 0 shiny/api-examples/isolate/{app.py => app-core.py} | 0 shiny/api-examples/layout_column_wrap/{app.py => app-core.py} | 0 shiny/api-examples/layout_columns/{app.py => app-core.py} | 0 shiny/api-examples/layout_sidebar/{app.py => app-core.py} | 0 shiny/api-examples/markdown/{app.py => app-core.py} | 0 shiny/api-examples/modal/{app.py => app-core.py} | 0 shiny/api-examples/nav_panel/{app.py => app-core.py} | 0 shiny/api-examples/navset_hidden/{app.py => app-core.py} | 0 shiny/api-examples/notification_show/{app.py => app-core.py} | 0 shiny/api-examples/on_ended/{app.py => app-core.py} | 0 shiny/api-examples/on_flush/{app.py => app-core.py} | 0 shiny/api-examples/on_flushed/{app.py => app-core.py} | 0 shiny/api-examples/output_image/{app.py => app-core.py} | 0 shiny/api-examples/output_plot/{app.py => app-core.py} | 0 shiny/api-examples/output_table/{app.py => app-core.py} | 0 shiny/api-examples/output_text/{app.py => app-core.py} | 0 shiny/api-examples/output_transformer/{app.py => app-core.py} | 0 shiny/api-examples/output_ui/{app.py => app-core.py} | 0 shiny/api-examples/page_fixed/{app.py => app-core.py} | 0 shiny/api-examples/page_fluid/{app.py => app-core.py} | 0 shiny/api-examples/page_sidebar/{app.py => app-core.py} | 0 shiny/api-examples/panel_absolute/{app.py => app-core.py} | 0 shiny/api-examples/panel_conditional/{app.py => app-core.py} | 0 shiny/api-examples/panel_title/{app.py => app-core.py} | 0 shiny/api-examples/poll/{app.py => app-core.py} | 0 shiny/api-examples/popover/{app.py => app-core.py} | 0 shiny/api-examples/remove_accordion_panel/{app.py => app-core.py} | 0 shiny/api-examples/remove_ui/{app.py => app-core.py} | 0 shiny/api-examples/render_express/{app.py => app-core.py} | 0 shiny/api-examples/render_image/{app.py => app-core.py} | 0 shiny/api-examples/req/{app.py => app-core.py} | 0 shiny/api-examples/row/{app.py => app-core.py} | 0 shiny/api-examples/send_custom_message/{app.py => app-core.py} | 0 shiny/api-examples/showcase_bottom/{app.py => app-core.py} | 0 shiny/api-examples/showcase_left_center/{app.py => app-core.py} | 0 shiny/api-examples/showcase_top_right/{app.py => app-core.py} | 0 shiny/api-examples/sidebar/{app.py => app-core.py} | 0 shiny/api-examples/template/{app.py => app-core.py} | 0 shiny/api-examples/todo_list/{app.py => app-core.py} | 0 shiny/api-examples/tooltip/{app.py => app-core.py} | 0 shiny/api-examples/update_accordion/{app.py => app-core.py} | 0 shiny/api-examples/update_accordion_panel/{app.py => app-core.py} | 0 shiny/api-examples/update_action_button/{app.py => app-core.py} | 0 shiny/api-examples/update_checkbox/{app.py => app-core.py} | 0 shiny/api-examples/update_checkbox_group/{app.py => app-core.py} | 0 shiny/api-examples/update_date/{app.py => app-core.py} | 0 shiny/api-examples/update_date_range/{app.py => app-core.py} | 0 shiny/api-examples/update_navs/{app.py => app-core.py} | 0 shiny/api-examples/update_numeric/{app.py => app-core.py} | 0 shiny/api-examples/update_popover/{app.py => app-core.py} | 0 shiny/api-examples/update_radio_buttons/{app.py => app-core.py} | 0 shiny/api-examples/update_select/{app.py => app-core.py} | 0 shiny/api-examples/update_selectize/{app.py => app-core.py} | 0 shiny/api-examples/update_sidebar/{app.py => app-core.py} | 0 shiny/api-examples/update_slider/{app.py => app-core.py} | 0 shiny/api-examples/update_text/{app.py => app-core.py} | 0 shiny/api-examples/update_tooltip/{app.py => app-core.py} | 0 shiny/api-examples/value_box/{app.py => app-core.py} | 0 shiny/api-examples/www_dir/{app.py => app-core.py} | 0 106 files changed, 0 insertions(+), 0 deletions(-) rename shiny/api-examples/Module/{app.py => app-core.py} (100%) rename shiny/api-examples/Progress/{app.py => app-core.py} (100%) rename shiny/api-examples/Renderer/{app.py => app-core.py} (100%) rename shiny/api-examples/SafeException/{app.py => app-core.py} (100%) rename shiny/api-examples/SilentCancelOutputException/{app.py => app-core.py} (100%) rename shiny/api-examples/SilentException/{app.py => app-core.py} (100%) rename shiny/api-examples/Value/{app.py => app-core.py} (100%) rename shiny/api-examples/accordion/{app.py => app-core.py} (100%) rename shiny/api-examples/accordion_panel/{app.py => app-core.py} (100%) rename shiny/api-examples/as_fill_item/{app.py => app-core.py} (100%) rename shiny/api-examples/as_fillable_container/{app.py => app-core.py} (100%) rename shiny/api-examples/calc/{app.py => app-core.py} (100%) rename shiny/api-examples/card/{app.py => app-core.py} (100%) rename shiny/api-examples/card_body/{app.py => app-core.py} (100%) rename shiny/api-examples/card_footer/{app.py => app-core.py} (100%) rename shiny/api-examples/card_header/{app.py => app-core.py} (100%) rename shiny/api-examples/close/{app.py => app-core.py} (100%) rename shiny/api-examples/data_frame/{app.py => app-core.py} (100%) rename shiny/api-examples/download/{app.py => app-core.py} (100%) rename shiny/api-examples/download_button/{app.py => app-core.py} (100%) rename shiny/api-examples/download_link/{app.py => app-core.py} (100%) rename shiny/api-examples/dynamic_route/{app.py => app-core.py} (100%) rename shiny/api-examples/effect/{app.py => app-core.py} (100%) rename shiny/api-examples/event/{app.py => app-core.py} (100%) rename shiny/api-examples/extended_task/{app.py => app-core.py} (100%) rename shiny/api-examples/file_reader/{app.py => app-core.py} (100%) rename shiny/api-examples/include_css/{app.py => app-core.py} (100%) rename shiny/api-examples/include_js/{app.py => app-core.py} (100%) rename shiny/api-examples/input_action_button/{app.py => app-core.py} (100%) rename shiny/api-examples/input_action_link/{app.py => app-core.py} (100%) rename shiny/api-examples/input_checkbox/{app.py => app-core.py} (100%) rename shiny/api-examples/input_checkbox_group/{app.py => app-core.py} (100%) rename shiny/api-examples/input_date/{app.py => app-core.py} (100%) rename shiny/api-examples/input_date_range/{app.py => app-core.py} (100%) rename shiny/api-examples/input_file/{app.py => app-core.py} (100%) rename shiny/api-examples/input_numeric/{app.py => app-core.py} (100%) rename shiny/api-examples/input_password/{app.py => app-core.py} (100%) rename shiny/api-examples/input_radio_buttons/{app.py => app-core.py} (100%) rename shiny/api-examples/input_select/{app.py => app-core.py} (100%) rename shiny/api-examples/input_selectize/{app.py => app-core.py} (100%) rename shiny/api-examples/input_slider/{app.py => app-core.py} (100%) rename shiny/api-examples/input_switch/{app.py => app-core.py} (100%) rename shiny/api-examples/input_text/{app.py => app-core.py} (100%) rename shiny/api-examples/input_text_area/{app.py => app-core.py} (100%) rename shiny/api-examples/insert_accordion_panel/{app.py => app-core.py} (100%) rename shiny/api-examples/insert_ui/{app.py => app-core.py} (100%) rename shiny/api-examples/invalidate_later/{app.py => app-core.py} (100%) rename shiny/api-examples/isolate/{app.py => app-core.py} (100%) rename shiny/api-examples/layout_column_wrap/{app.py => app-core.py} (100%) rename shiny/api-examples/layout_columns/{app.py => app-core.py} (100%) rename shiny/api-examples/layout_sidebar/{app.py => app-core.py} (100%) rename shiny/api-examples/markdown/{app.py => app-core.py} (100%) rename shiny/api-examples/modal/{app.py => app-core.py} (100%) rename shiny/api-examples/nav_panel/{app.py => app-core.py} (100%) rename shiny/api-examples/navset_hidden/{app.py => app-core.py} (100%) rename shiny/api-examples/notification_show/{app.py => app-core.py} (100%) rename shiny/api-examples/on_ended/{app.py => app-core.py} (100%) rename shiny/api-examples/on_flush/{app.py => app-core.py} (100%) rename shiny/api-examples/on_flushed/{app.py => app-core.py} (100%) rename shiny/api-examples/output_image/{app.py => app-core.py} (100%) rename shiny/api-examples/output_plot/{app.py => app-core.py} (100%) rename shiny/api-examples/output_table/{app.py => app-core.py} (100%) rename shiny/api-examples/output_text/{app.py => app-core.py} (100%) rename shiny/api-examples/output_transformer/{app.py => app-core.py} (100%) rename shiny/api-examples/output_ui/{app.py => app-core.py} (100%) rename shiny/api-examples/page_fixed/{app.py => app-core.py} (100%) rename shiny/api-examples/page_fluid/{app.py => app-core.py} (100%) rename shiny/api-examples/page_sidebar/{app.py => app-core.py} (100%) rename shiny/api-examples/panel_absolute/{app.py => app-core.py} (100%) rename shiny/api-examples/panel_conditional/{app.py => app-core.py} (100%) rename shiny/api-examples/panel_title/{app.py => app-core.py} (100%) rename shiny/api-examples/poll/{app.py => app-core.py} (100%) rename shiny/api-examples/popover/{app.py => app-core.py} (100%) rename shiny/api-examples/remove_accordion_panel/{app.py => app-core.py} (100%) rename shiny/api-examples/remove_ui/{app.py => app-core.py} (100%) rename shiny/api-examples/render_express/{app.py => app-core.py} (100%) rename shiny/api-examples/render_image/{app.py => app-core.py} (100%) rename shiny/api-examples/req/{app.py => app-core.py} (100%) rename shiny/api-examples/row/{app.py => app-core.py} (100%) rename shiny/api-examples/send_custom_message/{app.py => app-core.py} (100%) rename shiny/api-examples/showcase_bottom/{app.py => app-core.py} (100%) rename shiny/api-examples/showcase_left_center/{app.py => app-core.py} (100%) rename shiny/api-examples/showcase_top_right/{app.py => app-core.py} (100%) rename shiny/api-examples/sidebar/{app.py => app-core.py} (100%) rename shiny/api-examples/template/{app.py => app-core.py} (100%) rename shiny/api-examples/todo_list/{app.py => app-core.py} (100%) rename shiny/api-examples/tooltip/{app.py => app-core.py} (100%) rename shiny/api-examples/update_accordion/{app.py => app-core.py} (100%) rename shiny/api-examples/update_accordion_panel/{app.py => app-core.py} (100%) rename shiny/api-examples/update_action_button/{app.py => app-core.py} (100%) rename shiny/api-examples/update_checkbox/{app.py => app-core.py} (100%) rename shiny/api-examples/update_checkbox_group/{app.py => app-core.py} (100%) rename shiny/api-examples/update_date/{app.py => app-core.py} (100%) rename shiny/api-examples/update_date_range/{app.py => app-core.py} (100%) rename shiny/api-examples/update_navs/{app.py => app-core.py} (100%) rename shiny/api-examples/update_numeric/{app.py => app-core.py} (100%) rename shiny/api-examples/update_popover/{app.py => app-core.py} (100%) rename shiny/api-examples/update_radio_buttons/{app.py => app-core.py} (100%) rename shiny/api-examples/update_select/{app.py => app-core.py} (100%) rename shiny/api-examples/update_selectize/{app.py => app-core.py} (100%) rename shiny/api-examples/update_sidebar/{app.py => app-core.py} (100%) rename shiny/api-examples/update_slider/{app.py => app-core.py} (100%) rename shiny/api-examples/update_text/{app.py => app-core.py} (100%) rename shiny/api-examples/update_tooltip/{app.py => app-core.py} (100%) rename shiny/api-examples/value_box/{app.py => app-core.py} (100%) rename shiny/api-examples/www_dir/{app.py => app-core.py} (100%) 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/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 100% rename from shiny/api-examples/Value/app.py rename to shiny/api-examples/Value/app-core.py 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 100% rename from shiny/api-examples/calc/app.py rename to shiny/api-examples/calc/app-core.py 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/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_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_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_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_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_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_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_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_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_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_selectize/app.py b/shiny/api-examples/input_selectize/app-core.py similarity index 100% rename from shiny/api-examples/input_selectize/app.py rename to shiny/api-examples/input_selectize/app-core.py 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_switch/app.py b/shiny/api-examples/input_switch/app-core.py similarity index 100% rename from shiny/api-examples/input_switch/app.py rename to shiny/api-examples/input_switch/app-core.py 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_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/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 100% rename from shiny/api-examples/invalidate_later/app.py rename to shiny/api-examples/invalidate_later/app-core.py 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/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_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_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/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/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 100% rename from shiny/api-examples/page_sidebar/app.py rename to shiny/api-examples/page_sidebar/app-core.py diff --git a/shiny/api-examples/panel_absolute/app.py b/shiny/api-examples/panel_absolute/app-core.py similarity index 100% rename from shiny/api-examples/panel_absolute/app.py rename to shiny/api-examples/panel_absolute/app-core.py 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_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 100% rename from shiny/api-examples/poll/app.py rename to shiny/api-examples/poll/app-core.py 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/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 From 4abf8f7c6feffbfec4ea6565a8e313856c2c166f Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 26 Jan 2024 13:19:54 -0400 Subject: [PATCH 18/23] Expressify reactive examples (#1078) Co-authored-by: Garrick Aden-Buie --- shiny/api-examples/Progress/app-express.py | 21 ++++++++++++ shiny/api-examples/Value/app-core.py | 14 ++++---- shiny/api-examples/Value/app-express.py | 28 ++++++++++++++++ shiny/api-examples/calc/app-core.py | 18 ++++++----- shiny/api-examples/calc/app-express.py | 32 +++++++++++++++++++ shiny/api-examples/effect/app-express.py | 15 +++++++++ .../api-examples/invalidate_later/app-core.py | 9 ++---- .../invalidate_later/app-express.py | 10 ++++++ shiny/api-examples/isolate/app-express.py | 23 +++++++++++++ shiny/api-examples/poll/app-core.py | 24 +++++++------- 10 files changed, 159 insertions(+), 35 deletions(-) create mode 100644 shiny/api-examples/Progress/app-express.py create mode 100644 shiny/api-examples/Value/app-express.py create mode 100644 shiny/api-examples/calc/app-express.py create mode 100644 shiny/api-examples/effect/app-express.py create mode 100644 shiny/api-examples/invalidate_later/app-express.py create mode 100644 shiny/api-examples/isolate/app-express.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/Value/app-core.py b/shiny/api-examples/Value/app-core.py index fe0112954..04479492a 100644 --- a/shiny/api-examples/Value/app-core.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/calc/app-core.py b/shiny/api-examples/calc/app-core.py index 603313593..fe2620253 100644 --- a/shiny/api-examples/calc/app-core.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/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/invalidate_later/app-core.py b/shiny/api-examples/invalidate_later/app-core.py index 7966d01f0..66de8233d 100644 --- a/shiny/api-examples/invalidate_later/app-core.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-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/poll/app-core.py b/shiny/api-examples/poll/app-core.py index c9411d28a..f6ab621b6 100644 --- a/shiny/api-examples/poll/app-core.py +++ b/shiny/api-examples/poll/app-core.py @@ -89,9 +89,8 @@ def stock_quotes() -> pd.DataFrame: # === Define the Shiny UI and server =============================== app_ui = ui.page_fluid( - ui.row( - ui.column( - 8, + ui.layout_columns( + ui.card( ui.markdown( """ # `shiny.reactive.poll` demo @@ -100,11 +99,14 @@ def stock_quotes() -> pd.DataFrame: case, an in-memory sqlite3) with the help of `shiny.reactive.poll`. """ ), - class_="mb-3", + ui.input_selectize( + "symbols", "Filter by symbol", [""] + SYMBOLS, multiple=True + ), + ui.output_data_frame("table"), + fill=False, ), - ), - ui.input_selectize("symbols", "Filter by symbol", [""] + SYMBOLS, multiple=True), - ui.output_ui("table"), + col_widths=[8, 4], + ) ) @@ -115,13 +117,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) From 524fb6a31b352ea9ad1d3d68985dc2b11831b1e4 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Fri, 26 Jan 2024 12:50:42 -0500 Subject: [PATCH 19/23] temp: `add_examples()` warns about missing docs --- docs/Makefile | 8 +++++++- shiny/_docstring.py | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index a37b25d4e..3efd51267 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -52,11 +52,17 @@ quartodoc_impl: $(PYBIN) ## Build qmd files for API docs $(eval export SHINY_ADD_EXAMPLES=true) $(eval export IN_QUARTODOC=true) . $(PYBIN)/activate \ + && echo "::group::quartodoc interlinks" \ && quartodoc interlinks \ + && echo "::endgroup::" \ + && echo "::group::quartodoc build core docs" \ && SHINY_MODE="core" quartodoc build --config _quartodoc-core.yml --verbose \ && mv objects.json _objects_core.json \ + && echo "::endgroup::" \ + && echo "::group::quartodoc build express docs" \ && SHINY_MODE="express" quartodoc build --config _quartodoc-express.yml --verbose \ - && mv objects.json _objects_express.json + && mv objects.json _objects_express.json \ + && echo "::endgroup::" quartodoc_post: $(PYBIN) ## Post-process qmd files for API docs . $(PYBIN)/activate \ diff --git a/shiny/_docstring.py b/shiny/_docstring.py index fec1df04f..724ebb32a 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -129,9 +129,20 @@ def _(func: F) -> F: ) app_file_name = app_file or "app.py" - example_file = app_choose_core_or_express( - os.path.join(example_dir, app_file_name) - ) + try: + example_file = app_choose_core_or_express( + os.path.join(example_dir, app_file_name) + ) + except ExampleNotFoundException as e: + func_dir = get_decorated_source_directory(func).split("py-shiny/")[1] + if "__code__" in dir(func): + print( + f"::warning file={func_dir},line={func.__code__.co_firstlineno}::{e}" + ) + else: + print(f"::warning file={func_dir}::{e}") + + return func other_files: list[str] = [] for f in os.listdir(example_dir): From 20b0dff2aafb68c628d74ee0d6adcbb778e44afb Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Fri, 26 Jan 2024 12:59:40 -0500 Subject: [PATCH 20/23] don't error in quarto build step if in express docs build --- docs/_renderer_core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/_renderer_core.py b/docs/_renderer_core.py index c0aff1c13..2f30f6124 100644 --- a/docs/_renderer_core.py +++ b/docs/_renderer_core.py @@ -259,6 +259,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") == "express": + # These errors are thrown much earlier by @add_example() + return + if re.search(r"(^|\n)#{2,6} Examples\n", converted): # Manually added examples are fine return From 0d308276b61fc80ad1ab50a35443f08970f6000a Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Fri, 26 Jan 2024 13:01:15 -0500 Subject: [PATCH 21/23] import os --- docs/_renderer_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_renderer_core.py b/docs/_renderer_core.py index 2f30f6124..96239aa9e 100644 --- a/docs/_renderer_core.py +++ b/docs/_renderer_core.py @@ -2,6 +2,7 @@ import base64 import html +import os import re from importlib.resources import files from pathlib import Path From 7bb5f336ba207e5eb30d0b6a8e874f2c36ea789b Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Fri, 26 Jan 2024 13:22:59 -0500 Subject: [PATCH 22/23] fix comment --- shiny/_docstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/_docstring.py b/shiny/_docstring.py index 724ebb32a..8ecc13c08 100644 --- a/shiny/_docstring.py +++ b/shiny/_docstring.py @@ -221,7 +221,7 @@ def __init__( def __str__(self): if self.type in ("core", "express"): - ## Capitalize first letter + # Capitalize first letter type = "a Shiny Express" if self.type == "express" else "a Shiny Core" else: type = "an" From 03d1e69ba000fe0b8858fed293c551cad0b2b95f Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Fri, 26 Jan 2024 13:57:39 -0500 Subject: [PATCH 23/23] don't need gha group around `quartodoc interlinks` --- docs/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 3efd51267..47a6af731 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -52,9 +52,7 @@ quartodoc_impl: $(PYBIN) ## Build qmd files for API docs $(eval export SHINY_ADD_EXAMPLES=true) $(eval export IN_QUARTODOC=true) . $(PYBIN)/activate \ - && echo "::group::quartodoc interlinks" \ && quartodoc interlinks \ - && echo "::endgroup::" \ && echo "::group::quartodoc build core docs" \ && SHINY_MODE="core" quartodoc build --config _quartodoc-core.yml --verbose \ && mv objects.json _objects_core.json \