posit-dev / posit-dev/py-shiny
feat: Expose missing App attributes in Express via app_opts() and/or an app accessor
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.8k
- Forks
- 135
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 21
Description
Problem
Shiny Express apps configure app-level options via app_opts(), but it only covers a subset of what can be set on a shiny.App object in Core mode. Several important App attributes are completely inaccessible from Express:
| Attribute | Core mode | Express app_opts() |
|---|---|---|
static_assets |
App(...) constructor |
✅ supported |
bookmark_store |
App(...) constructor |
✅ supported |
debug |
App(...) constructor |
✅ supported |
sanitize_errors |
app.sanitize_errors = True |
❌ missing |
sanitize_error_msg |
app.sanitize_error_msg = "..." |
❌ missing |
sanitize_otel_errors |
app.sanitize_otel_errors = False |
❌ missing |
lib_prefix |
app.lib_prefix = "assets/" |
❌ missing |
In Core mode, an app sets these like:
app = App(app_ui, server)
app.sanitize_errors = True
app.sanitize_otel_errors = False
app.sanitize_error_msg = "Something went wrong."
In Express, there is no equivalent — app_opts() doesn't support any of them and the underlying App object is never exposed to the user.
Proposed Solutions
Option A: Extend app_opts() with all missing attributes
Add the missing parameters to the existing app_opts() function:
app_opts(
sanitize_errors=True,
sanitize_otel_errors=False,
sanitize_error_msg="Something went wrong.",
lib_prefix="assets/",
)
Pros: Consistent with the existing pattern; all options in one place.
Cons: app_opts() grows with every new App attribute; may lag behind future additions.
Option B: Provide an accessor to the underlying App object
Expose a function (e.g., express.get_app()) that returns the live App instance, allowing direct attribute mutation:
from shiny.express import get_app
get_app().sanitize_errors = True
get_app().sanitize_otel_errors = False
Pros: No need to update app_opts() every time App gains a new attribute; users have full control; mirrors the Core mode experience.
Cons: Requires the App to exist at call time (it's created during create_express_app()); needs careful handling of the execution lifecycle; slightly less discoverable.
Option C: Both — extend app_opts() and add an accessor
Keep app_opts() for the most common options, and add get_app() as an escape hatch for advanced use.
Recommendation
Implement Option C:
- Add
sanitize_errors,sanitize_otel_errors, andsanitize_error_msgtoapp_opts()since they're immediately needed and commonly configured. - Add a
get_app()(or similar name —current_app(),express_app()?) accessor as an escape hatch so futureAppattributes don't requireapp_opts()updates.
The naming of the accessor is worth discussion — options include:
express.get_app()express.current_app()express.app()(but could conflict with theappmodule)
Related
shiny/express/_run.py—app_opts()andAppOptsTypedDictshiny/_app.py—Appclass with all mutable attributesexamples/req/app.py— Core mode example usingapp.sanitize_errors = True
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with shiny/express/_run.py, especially app_opts() and the AppOpts TypedDict, then inspect shiny/_app.py for the mutable attributes listed in the issue. Review examples/req/app.py for the Core-mode behavior. Done means selecting and implementing the agreed accessor and/or option design so the missing Express configuration is available, with the lifecycle and API naming resolved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100