[WIP] Add tethys run — zero-config single-file app runner (express mode POC)#1287
Draft
gagelarsen wants to merge 2 commits into
Draft
[WIP] Add tethys run — zero-config single-file app runner (express mode POC)#1287gagelarsen wants to merge 2 commits into
tethys run — zero-config single-file app runner (express mode POC)#1287gagelarsen wants to merge 2 commits into
Conversation
Proof-of-concept, Shiny-inspired runner: "tethys run app.py" serves a single-file component app with no portal configuration, no database setup, no pip install, and no login. It generates an isolated TETHYS_HOME (~/.tethys/express/<pkg>_<hash>/) containing a portal config (single-app mode + open portal) and a throwaway SQLite database, grafts the app file into the tethysapp namespace via sys.modules, and launches the standard development server. - tethys_cli/run_commands.py: new "run" subcommand (-p/--port, --host, --no-browser, --no-reload, --clean) - tethys_apps/base/express.py: express loader + metadata synthesis (package/name/root_url/index derived from the file when omitted) - tethys_apps/harvester.py: include the express app during harvest - tethys_apps/base/component_base.py: __init_subclass__ hook for express metadata; fix auto nav links in single-app mode (/apps/<url>/ 404s when MULTIPLE_APP_MODE=False) - tethys_apps/utilities.py: catch sqlite OperationalError in get_configured_standalone_app (fresh single-app portals crashed during migrate when reactpy_django imports the URLconf) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- docs/tethys_cli/run.rst: full reference page (quick start, how it works, auto-generated arguments via sphinx-argparse, examples) - docs/tethys_cli.rst: add run to the CLI toctree - docs/whats_new.rst: release note entry for express mode - docs/tethys_sdk/components.rst: tip cross-referencing tethys run from the component app.py docs Docs build verified locally with sphinx (no warnings from these files). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Added documentation in 35617c4:
Sphinx build verified locally — no warnings from the changed files. 🤖 Generated with Claude Code |
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an AI-generated proof of concept. The design and implementation were produced with an AI assistant (Claude) working interactively with @gagelarsen, then verified end-to-end locally. It is a draft for discussion with the Tethys team — not a request to merge as-is. All suggestions, redesigns, and pushback are welcome.
Implements #1286.
What this does
Runs a bare, single-file component app — never pip-installed, no
pyproject.toml, no portal config, no database setup, no login — with the ergonomics ofshiny run/streamlit run:First run takes ~4s (silent SQLite migration); subsequent runs ~3s. Hot reload is on by default (Django's stock autoreloader — the app file is an imported module).
tethys runwith no argument looks forapp.pyin the cwd.How it works (design)
"Portal-in-a-box": the existing portal runtime runs unmodified, configured down via an ephemeral generated environment — chosen over (a) a purpose-built minimal runtime (dual-runtime drift risk) and (b) materializing the file into a generated package (codegen indirection). Because everything downstream is stock machinery, an express app is guaranteed to behave identically when later installed in a real portal — the same file drops verbatim into a scaffolded component app's
app.py(a futuretethys scaffold --from app.pycould automate graduation).tethys_cli/run_commands.py(new): validates the file (AST check for aComponentBasesubclass), creates~/.tethys/express/<pkg>_<hash-of-abs-path>/as an isolatedTETHYS_HOME, writes aportal_config.ymlinto it (MULTIPLE_APP_MODE: False,STANDALONE_APP: <pkg>,ENABLE_OPEN_PORTAL: True, persistedSECRET_KEY), runsmanage.py migrate --no-input(idempotent, every start), then execsmanage.py runserver. Configuration flows through env vars (TETHYS_HOME,TETHYS_EXPRESS_APP) so it survives autoreloader restarts. No changes tosettings.pywere needed.tethys_apps/base/express.py(new): whenTETHYS_EXPRESS_APPis set, loads the file astethysapp.<pkg>.appand pre-registers it (plus a synthetic parent package) insys.modules, so the untouched harvester/register_controllersmachinery — including itsimportlib.reload()— resolves it like an installed app. Missing metadata is synthesized:package/root_urlfrom the filename (parent dir for genericapp.py),nametitle-cased,index= first@App.pagefunction.tethys_apps/harvester.py(~10 lines): include the express app in the harvest dict.tethys_apps/base/component_base.py:__init_subclass__hook calls the metadata synthesis at class-definition time — required because@App.pagereadsapp.packageat decoration time (ComponentLibrary keying).Drive-by bug fixes (pre-existing, single-app mode)
Both were hit during testing and affect non-express portals too — happy to split them into a separate PR:
tethys_apps/utilities.py:get_configured_standalone_app()caught Postgres'sProgrammingErrorbut not SQLite'sOperationalError, so a fresh single-app portal withreactpy_djangoinstalled crashed duringmigrate(reactpy's app-ready hook imports the URLconf, which queriesTethysAppbefore tables exist).tethys_apps/base/component_base.py: auto nav links hard-coded/apps/<root_url>/, which 404s whenMULTIPLE_APP_MODE=False(apps are served at root). Now settings-aware.Verification performed
test_express.py,test_run_commands.py) — all passing; regression run of touched-module test files shows failure sets identical tomain(3 pre-existing, unrelated).flake8andblack --checkclean.Known limitations / open questions
ENABLE_OPEN_PORTAL); no auth flag yet.controllers.pyetc. are importable via the synthetic package path) but are untested/undocumented.~/.tethys/express/(per-app, keyed by file path;--cleanwipes one app's state). No global GC.requests/network access to esm.sh (React CDN) is required at page load, as with all component apps.tethys runvstethys express), the state-dir lifecycle, and whether portal-in-a-box is the right long-term architecture are all up for debate in Feature proposal:tethys run app.py— zero-config single-file app runner ("Tethys Express") #1286.🤖 Generated with Claude Code