Skip to content

Commit

Permalink
Add remaining examples for express API reference (#1093)
Browse files Browse the repository at this point in the history
Co-authored-by: Garrick Aden-Buie <garrick@adenbuie.com>
  • Loading branch information
cpsievert and gadenbuie committed Feb 2, 2024
1 parent ce970d8 commit a3389df
Show file tree
Hide file tree
Showing 90 changed files with 1,990 additions and 411 deletions.
52 changes: 0 additions & 52 deletions docs/_quartodoc-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,58 +328,6 @@ quartodoc:
- types.SilentException
- types.SilentCancelOutputException
- types.SafeException
- title: Shiny Express
desc: Functions for Shiny Express applications
contents:
- kind: page
path: ContextManagerComponents
summary:
name: "Context manager components"
desc: ""
flatten: true
contents:
- express.ui.sidebar
- express.ui.layout_sidebar
- express.ui.layout_column_wrap
- express.ui.layout_columns
- express.ui.card
- express.ui.accordion
- express.ui.accordion_panel
- express.ui.nav_panel
- express.ui.nav_control
- express.ui.nav_menu
- express.ui.navset_bar
- express.ui.navset_card_pill
- express.ui.navset_card_tab
- express.ui.navset_card_underline
- express.ui.navset_hidden
- express.ui.navset_pill
- express.ui.navset_pill_list
- express.ui.navset_tab
- express.ui.navset_underline
- express.ui.value_box
- express.ui.panel_title
- express.ui.panel_well
- express.ui.panel_conditional
- express.ui.panel_fixed
- express.ui.panel_absolute
- kind: page
path: PageFunctions
summary:
name: "Page functions"
desc: ""
flatten: true
contents:
- express.ui.page_opts
- kind: page
path: DisplayFunctions
summary:
name: "Display functions"
desc: ""
flatten: true
contents:
- express.ui.hold
- express.expressify
- title: Deprecated
desc: ""
contents:
Expand Down
14 changes: 7 additions & 7 deletions docs/_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,6 @@ 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
Expand All @@ -287,7 +283,12 @@ def is_no_ex_decorator(x):
if x == "no_example()":
return True

return x == f'no_example("{os.environ.get("SHINY_MODE", "core")}")'
no_ex_decorators = [
f'no_example("{os.environ.get("SHINY_MODE", "core")}")',
f"no_example('{os.environ.get('SHINY_MODE', 'core')}')",
]

return x in no_ex_decorators

if hasattr(el, "decorators") and any(
[is_no_ex_decorator(d.value.canonical_name) for d in el.decorators]
Expand All @@ -304,8 +305,7 @@ def is_no_ex_decorator(x):
# In practice, this covers methods of exported classes (class still needs ex)
return

# TODO: Remove shiny.express from no_req_examples when we have examples ready
no_req_examples = ["shiny.express", "shiny.experimental"]
no_req_examples = ["shiny.experimental"]
if any([el.target_path.startswith(mod) for mod in no_req_examples]):
return

Expand Down
14 changes: 11 additions & 3 deletions shiny/_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def _(func: F) -> F:
app_file_name = app_file or "app.py"
try:
example_file = app_choose_core_or_express(
os.path.join(example_dir, app_file_name)
os.path.join(example_dir, app_file_name),
mode="express" if "shiny/express/" in func_dir else None,
)
except ExampleNotFoundException as e:
file = "shiny/" + func_dir.split("shiny/")[1]
Expand Down Expand Up @@ -248,10 +249,17 @@ def __init__(
super().__init__(file_names, dir, "express")


def app_choose_core_or_express(app_path: Optional[str] = None) -> str:
def app_choose_core_or_express(
app_path: Optional[str] = None,
mode: Optional[Literal["express", "core"]] = None,
) -> str:
app_path = app_path or "app.py"

if os.environ.get("SHINY_MODE") == "express":
if mode is None:
mode_env = os.environ.get("SHINY_MODE", "core")
mode = "express" if mode_env == "express" else "core"

if mode == "express":
if is_express_app(app_path):
return app_path

Expand Down
156 changes: 10 additions & 146 deletions shiny/api-examples/Renderer/app-core.py
Original file line number Diff line number Diff line change
@@ -1,157 +1,21 @@
from __future__ import annotations

from typing import Literal, Optional
# Import the custom renderer implementations
from renderers import render_capitalize, render_upper

from shiny import App, Inputs, Outputs, Session, ui
from shiny.render.renderer import Renderer, ValueFn

#######
# Start of package author code
#######


class render_capitalize(Renderer[str]):
# The documentation for the class will be displayed when the user hovers over the
# decorator when **no** parenthesis are used. Ex: `@render_capitalize`
# If no documentation is supplied to the `__init__()` method, then this
# documentation will be displayed when parenthesis are used on the decorator.
"""
Render capitalize class documentation goes here.
"""

to_case: Literal["upper", "lower", "ignore"]
"""
The case to render the value in.
"""
placeholder: bool
"""
Whether to render a placeholder value. (Defaults to `True`)
"""

def auto_output_ui(self):
"""
Express UI for the renderer
"""
return ui.output_text_verbatim(self.output_name, placeholder=self.placeholder)

def __init__(
self,
_fn: Optional[ValueFn[str]] = None,
*,
to_case: Literal["upper", "lower", "ignore"] = "upper",
placeholder: bool = True,
) -> None:
# If a different set of documentation is supplied to the `__init__` method,
# then this documentation will be displayed when parenthesis are used on the decorator.
# Ex: `@render_capitalize()`
"""
Render capitalize documentation goes here.
It is a good idea to talk about parameters here!
Parameters
----------
to_case
The case to render the value. (`"upper"`)
Options:
- `"upper"`: Render the value in upper case.
- `"lower"`: Render the value in lower case.
- `"ignore"`: Do not alter the case of the value.
placeholder
Whether to render a placeholder value. (`True`)
"""
# Do not pass params
super().__init__(_fn)
self.to_case = to_case

async def render(self) -> str | None:
value = await self.fn()
if value is None:
# If `None` is returned, then do not render anything.
return None

ret = str(value)
if self.to_case == "upper":
return ret.upper()
if self.to_case == "lower":
return ret.lower()
if self.to_case == "ignore":
return ret
raise ValueError(f"Invalid value for `to_case`: {self.to_case}")


class render_upper(Renderer[str]):
"""
Minimal capitalize string transformation renderer.
No parameters are supplied to this renderer. This allows us to skip the `__init__()`
method and `__init__()` documentation. If you hover over this decorator with and
without parenthesis, you will see this documentation in both situations.
Note: This renderer is equivalent to `render_capitalize(to="upper")`.
"""

def auto_output_ui(self):
"""
Express UI for the renderer
"""
return ui.output_text_verbatim(self.output_name, placeholder=True)

async def transform(self, value: str) -> str:
"""
Transform the value to upper case.
This method is shorthand for the default `render()` method. It is useful to
transform non-`None` values. (Any `None` value returned by the app author will
be forwarded to the browser.)
Parameters
----------
value
The a non-`None` value to transform.
Returns
-------
str
The transformed value. (Must be a subset of `Jsonifiable`.)
"""

return str(value).upper()


#######
# End of package author code
#######


#######
# Start of app author code
#######


def text_row(id: str, label: str):
return ui.tags.tr(
ui.tags.td(f"{label}:"),
ui.tags.td(ui.output_text_verbatim(id, placeholder=True)),
)
return ui.row(
ui.column(6, f"{id}:"),
ui.column(6, ui.output_text_verbatim(id, placeholder=True)),
)


app_ui = ui.page_fluid(
ui.h1("Capitalization renderer"),
ui.input_text("caption", "Caption:", "Data summary"),
ui.tags.table(
text_row("upper", "@render_upper"),
text_row("upper_with_paren", "@render_upper()"),
#
text_row("cap_upper", "@render_capitalize"),
text_row("cap_lower", "@render_capitalize(to='lower')"),
),
"@render_upper: ",
ui.output_text_verbatim("upper", placeholder=True),
"@render_upper(): ",
ui.output_text_verbatim("upper_with_paren", placeholder=True),
"@render_capitalize: ",
ui.output_text_verbatim("cap_upper", placeholder=True),
"@render_capitalize(to='lower'): ",
ui.output_text_verbatim("cap_lower", placeholder=True),
)


Expand Down
43 changes: 43 additions & 0 deletions shiny/api-examples/Renderer/app-express.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Import the custom renderer implementations
from renderers import render_capitalize, render_upper

from shiny.express import input, ui

ui.h1("Capitalization renderer")
ui.input_text("caption", "Caption:", "Data summary")

"@render_upper:"


# Hovering over `@render_upper` will display the class documentation
@render_upper
def upper():
return input.caption()


"@render_upper():"


# Hovering over `@render_upper` will display the class documentation as there is no
# `__init__()` documentation
@render_upper()
def upper_with_paren():
return input.caption()


"@render_capitalize:"


# Hovering over `@render_capitalize` will display the class documentation
@render_capitalize
def cap_upper():
return input.caption()


"@render_capitalize(to='lower'): "


# Hovering over `@render_capitalize` will display the `__init__()` documentation
@render_capitalize(to_case="lower")
def cap_lower():
return input.caption()
Loading

0 comments on commit a3389df

Please sign in to comment.