Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

py/facett_demo/cli.py

"""`facett-demo` — start the demo, or say what it is (build-version-githash LAW).

    facett-demo                          # the app on http://127.0.0.1:8501
    facett-demo run [streamlit options]  # e.g. run --server.port 8502
    facett-demo --version | about

Streamlit is started IN-PROCESS through its own Python entry point
(``streamlit.web.cli``) — the same call the ``streamlit`` console script makes — rather
than by spawning a ``streamlit`` executable, so the demo needs nothing on ``PATH`` and
runs under exactly the interpreter it was installed into.

The default bind address is LOOPBACK (``127.0.0.1``), on purpose: nothing about a demo
is authenticated, and a 0.0.0.0 bind on a workstation once published it to the
internet. Pass ``--server.address`` to choose otherwise.
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

from .buildinfo import build_info, version_string

#: The app script Streamlit runs — inside the INSTALLED package.
APP = Path(__file__).resolve().parent / "app.py"

#: Options the demo sets unless the caller passes its own.
DEFAULTS: dict[str, str] = {
    "--server.address": "127.0.0.1",
    "--server.port": "8501",
    "--server.headless": "true",
    "--browser.gatherUsageStats": "false",
}


def _about() -> str:
    bi = build_info()
    from .client import DEFAULT_TARGET, KORP_EXTRA_HINT, korp_available

    korp = "installed" if korp_available() else f"NOT installed — offline mode ({KORP_EXTRA_HINT})"
    lines = [
        version_string(),
        f"  version    : {bi['version']}",
        f"  git-hash   : {bi['git_hash']}",
        f"  build-time : {bi['build_time']}",
        f"  hash-source: {bi['source']}",
        f"  korp wire  : {korp}",
        f"  korp-server: {DEFAULT_TARGET} (override with $KORP_SERVER)",
        "  panes      : Komponenter (every wasm component, no server) · Map(Assets+MapQuery) · "
        "Graph(Graph) · Population(IcebergTables+TablePreview+Population) · Errors",
    ]
    return "\n".join(lines)


def streamlit_argv(extra: list[str]) -> list[str]:
    """The ``streamlit run`` arguments: the app, the caller's options, then any of
    :data:`DEFAULTS` the caller did not set (``--opt value`` or ``--opt=value``)."""
    given = {a.split("=", 1)[0] for a in extra if a.startswith("--")}
    argv = ["run", str(APP), *extra]
    for opt, value in DEFAULTS.items():
        if opt not in given:
            argv += [opt, value]
    return argv


def run(extra: list[str]) -> int:
    """Run the app in THIS process, through Streamlit's own CLI entry point."""
    try:
        from streamlit.web import cli as stcli
    except ImportError as e:  # facett[streamlit] is a hard dependency; say so if it is gone
        print(f"facett-demo needs Streamlit (pip install 'facett[streamlit]'): {e}", file=sys.stderr)
        return 2
    try:
        stcli.main(args=streamlit_argv(extra), prog_name="facett-demo", standalone_mode=True)
    except SystemExit as e:  # click exits; turn it into our return code
        return int(e.code or 0) if not isinstance(e.code, str) else 1
    return 0


def main(argv: list[str] | None = None) -> int:
    argv = sys.argv[1:] if argv is None else argv
    # `run` passes everything after it to Streamlit untouched (its own --server.* flags).
    if argv[:1] == ["run"]:
        return run(argv[1:])

    p = argparse.ArgumentParser(prog="facett-demo", description="facett's components in a Streamlit app")
    p.add_argument("--version", action="store_true", help="print version + git-hash and exit")
    sub = p.add_subparsers(dest="cmd")
    sub.add_parser("about", help="print the full build/about banner")
    sub.add_parser("run", help="start the app (default); Streamlit options follow")
    args = p.parse_args(argv)

    if args.version:
        print(version_string())
        return 0
    if args.cmd == "about":
        print(_about())
        return 0
    return run([])


if __name__ == "__main__":
    sys.exit(main())