Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remaining examples for express API reference #1093

Merged
merged 18 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading