cli.main¶
Top-level otto CLI: callback, subcommand dispatch, and eager option handlers.
- otto.cli.main.version_callback(version: bool) None¶
Print the otto version string and exit when
--versionis passed.
- otto.cli.main.clear_autocomplete_cache_callback(value: bool) None¶
Delete the shell-completion cache file and exit when the flag is set.
- otto.cli.main.list_labs_callback(value: bool) None¶
Print all available lab names (one panel per repo) and exit when the flag is set.
- otto.cli.main.log_level_callback(value: str) str¶
Normalise the
--log-levelvalue to upper-case before Typer stores it.
- otto.cli.main.parse_lab_selection(value: list[str] | None) list[str] | None¶
Typer callback for
--lab: split each value on+.Typer hands this
Nonewhen neither--labnorOTTO_LABis set — and also whenOTTO_LABis set but empty (verified empirically). Both pass straight through asNone, so the preamble’s no-lab path is unchanged. Repeats accumulate and each value is itself split, so--lab a+b --lab cselectsa,b, andc. Malformed input is a usage error (exit 2).The grammar itself lives in
otto.config.lab.split_lab_names()— this is only the CLI adapter, translating itsValueErrorinto a Typer usage error. The import is function-local to match this module’s existing convention for narrowly-used helpers (see_lab_completerabove andreservation.py).
- otto.cli.main.main(ctx: ~typer.models.Context, labs: ~types.Annotated[list[str] | None, <typer.models.OptionInfo object at 0x736d6cdaaf20>] | None = None, xdir: ~pathlib.Annotated[~pathlib.Path, <typer.models.OptionInfo object at 0x736d6cdab160>] = PosixPath('.'), debug: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cda9e10>] = False, log_days: ~typing.Annotated[int, <typer.models.OptionInfo object at 0x736d6cdabc70>] = 30, log_level: ~typing.Annotated[str, <typer.models.OptionInfo object at 0x736d6cdabc10>] = 'INFO', rich_log_file: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cdaba30>] = False, show_time: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cdabaf0>] = False, lab_depth: ~typing.Annotated[int, <typer.models.OptionInfo object at 0x736d6cda9750>] = 3, list_labs: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cda8f70>] = False, show_lab: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cda94e0>] = False, list_hosts: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cdab7f0>] = False, dry_run: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cda9ab0>] = False, version: ~types.Annotated[bool | None, <typer.models.OptionInfo object at 0x736d6cdaa9b0>] | None = None, clear_autocomplete_cache: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cdab0d0>] = False, as_user: ~types.Annotated[str | None, <typer.models.OptionInfo object at 0x736d6cdab790>] | None = None, skip_reservation_check: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x736d6cdaa320>] = False) None¶
Record root options for lazy lab loading; handle inline root-flag actions.
This is the Typer root callback executed before every
ottosubcommand. It no longer loads the lab or initialises logging: it stashes the root options onctx.metaand returns. The real work (lab load, session setup, output dir, reservation gate) runs lazily in the leaf-invokecommand_preamble(), so--help/ discovery paths are structurally incapable of touching host state. The only exceptions are--show-lab/--list-hosts, which inspect live lab state and so load it inline here before printing and exiting.
- otto.cli.main.entry() None¶
Console-script entry: composition root, then the Typer app.
Completion invocations take the cache fast path (zero user code); everything else runs
otto.bootstrap.bootstrap()before argv parsing so registered third-party commands exist when the root group is consulted. Contained user-code failures print one framed warning line each; real command dispatch fails loud in the invoke preamble.