configmodule

The configmodule package handles environment variables, repository discovery, settings parsing, and lab loading.

class otto.configmodule.configmodule.ConfigModule(repos, lab, reservation_backend=None, identity=None, skip_reservation_check=False)

Bases: object

repos : --is-rst--:py:class:`list`\ \[:py:class:`~otto.configmodule.repo.Repo`]

Repos under test.

lab : --is-rst--:py:class:`~otto.configmodule.lab.Lab`

All lab information, like hosts, version information, etc.

reservation_backend : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`~otto.reservations.protocol.ReservationBackend`] = None

Configured reservation backend, or None if the repo has no [reservations] settings wired up yet.

identity : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`~otto.reservations.identity.ResolvedIdentity`] = None

Effective reservation identity for this invocation (set by the top-level CLI callback after parsing --as-user).

skip_reservation_check : --is-rst--:py:class:`bool` = False

True when -R/--skip-reservation-check is on the command line.

logRepoCommits()
class otto.configmodule.configmodule.ConfigModuleManager

Bases: object

property configModule : ConfigModule
otto.configmodule.configmodule.getConfigModule()
otto.configmodule.configmodule.tryGetConfigModule()

Return the active ConfigModule, or None if none has been set.

Unlike getConfigModule(), this does not raise when the singleton is uninitialized. Used by code paths (e.g. the reservation gate) that must be callable from unit tests which invoke subcommand apps directly without going through the top-level main callback.

Return type:

Optional[ConfigModule]

otto.configmodule.configmodule.setConfigModule(configModule=None, lab=None, repos=None, reservation_backend=None, identity=None, skip_reservation_check=False)
otto.configmodule.configmodule.getHost(name)
Return type:

RemoteHost

otto.configmodule.configmodule.all_hosts(pattern=None, *, include_containers=False, ssh_options=None, telnet_options=None, sftp_options=None, scp_options=None, ftp_options=None, nc_options=None)

Yield the active lab’s real remote hosts, optionally filtered by regex.

This is the fleet generator: by default it yields only real RemoteHost instances and skips DockerContainerHost entries, since containers aren’t operated on as part of the host fleet (e.g. otto monitor, coverage collection). Containers remain reachable for targeted use via tab completion and get_host — neither of which goes through this generator. Pass include_containers=True to yield container hosts as well.

Parameters:
  • pattern – Compiled regex matched against each host’s id via pattern.search(). When None (the default), all hosts are yielded.

  • include_containers – When True, also yield DockerContainerHost entries. Defaults to False.

  • ssh_options, telnet_options, sftp_options, scp_options,

  • ftp_options, nc_options – Optional per-call option overrides. When supplied, each yielded host is a fresh dataclasses.replace()-style copy whose corresponding *_options field is replaced by the caller’s instance (wholesale replacement, not per-key merge). The new host has a fresh ConnectionManager constructed with the override options, so the override values shape whichever connection opens first. Stored hosts in lab.hosts are untouched. When no overrides are passed, the stored instances are yielded as-is so identity is preserved. Hop resolution is internal and is not affected by overrides.

Yields:

RemoteHost – Each matching host from the lab configuration.

Examples

>>> import re
>>> # assuming hosts: carrot_seed, tomato_seed, pepper_seed
>>> seeds = list(all_hosts(re.compile(r"tomato")))  
Return type:

Generator[RemoteHost, Any, Any]

async otto.configmodule.configmodule.do_for_all_hosts(method, *args, pattern=None, concurrent=True, include_containers=False, ssh_options=None, telnet_options=None, sftp_options=None, scp_options=None, ftp_options=None, nc_options=None, **kwargs)

Call an async host method on every matching host.

Parameters:
  • method – Unbound async method (e.g. RemoteHost.oneshot).

  • *args – Positional arguments forwarded to method after the host.

  • pattern – Compiled regex filter passed to all_hosts().

  • concurrent – When True (default), run all calls via asyncio.gather with return_exceptions=True. When False, execute serially.

  • include_containers – Forwarded to all_hosts(). When False (default), container hosts are excluded.

  • ssh_options, telnet_options, sftp_options, scp_options,

  • ftp_options, nc_options – Optional per-call option overrides forwarded to all_hosts(). See its docstring for semantics.

  • **kwargs – Keyword arguments forwarded to method.

Return type:

dict[str, Union[TypeVar(T), BaseException]]

Returns:

A dict keyed by host ID. Values are the return of method, or a BaseException if that host’s call failed.

Examples

>>> import re
>>> from otto.host import RemoteHost
>>> results = await do_for_all_hosts(  
...     RemoteHost.oneshot, "uname -a",
...     pattern=re.compile(r"router"),
... )
async otto.configmodule.configmodule.run_on_all_hosts(cmds, pattern=None, concurrent=True, timeout=None, *, include_containers=False, ssh_options=None, telnet_options=None, sftp_options=None, scp_options=None, ftp_options=None, nc_options=None)

Run commands on every matching host via RemoteHost.run().

Convenience wrapper around do_for_all_hosts() for the most common use case.

Parameters:
  • cmds – Command string or list of command strings.

  • pattern – Compiled regex filter passed to all_hosts().

  • concurrent – When True (default), run all calls via asyncio.gather. When False, execute serially.

  • timeout – Per-host timeout forwarded to run.

  • include_containers – Forwarded to do_for_all_hosts(). When False (default), container hosts are excluded.

  • ssh_options, telnet_options, sftp_options, scp_options,

  • ftp_options, nc_options – Optional per-call option overrides forwarded to do_for_all_hosts().

Return type:

dict[str, RunResult | BaseException]

Returns:

A dict keyed by host ID. Values are RunResult instances, or a BaseException if that host’s call failed.

Examples

>>> results = await run_on_all_hosts("uname -a")  
otto.configmodule.configmodule.get_host(host_id, *, ssh_options=None, telnet_options=None, sftp_options=None, scp_options=None, ftp_options=None, nc_options=None)

Return the host registered under host_id in the active lab.

Parameters:
  • host_id – Unique host id (as produced by RemoteHost.id).

  • ssh_options, telnet_options, sftp_options, scp_options,

  • ftp_options, nc_options – Optional per-call option overrides. Each non-None argument replaces the corresponding *_options field on a returned copy wholesale; the copy is built via dataclasses.replace() so the new host’s ConnectionManager is constructed with the override options from the start. The stored host (and any connection it owns) is untouched. With no overrides, the stored instance is returned unchanged so get_host('x') is get_host('x') still holds. Hop resolution is internal and is not affected by overrides.

Return type:

RemoteHost

Environment variables that are needed before parsing CLI arguments

class otto.configmodule.env.OttoEnv

Bases: object

Otto environment variables

sutDirs : --is-rst--:py:class:`list`\ \[:py:class:`~pathlib.Path`]
classmethod getEnvVar(varName, default=None)
Return type:

Optional[str]

classmethod getEnvInt(varName, default=None)
Return type:

Optional[int]

classmethod getEnvPath(envVar, default=None, mustExist=True)
Return type:

Optional[Path]

classmethod getEnvPaths(envVar, mustExist=True)
Return type:

list[Path]

classmethod validatePath(path, mustExist=True)
Return type:

None

class otto.configmodule.lab.Lab(name, resources=<factory>, hosts=<factory>)

Bases: object

name : --is-rst--:py:class:`str`

Name of this lab.

resources : --is-rst--:py:class:`set`\ \[:py:class:`str`]

Resources required to reserve this lab.

hosts : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:class:`~otto.host.host.Host`]

Host objects, keyed by unique host id.

addHost(host)

Add a Host object to the Lab’s dictionary of hosts.

Return type:

None

Parameters

host : Host to add to the dictionary of hosts

otto.configmodule.lab.getLab(labnames, search_paths=None, defaults=None)

Perform all actions necessary to build a Lab object based on a list of lab names.

Return type:

Lab

Parameters

labnamesstr | list[str]

Name(s) of lab data to retrieve.

search_pathslist[Path] | None

Directories to search for lab data.

defaultsdict[str, dict[str, Any]] | None

Optional repo-level option defaults applied to every host in the resulting lab. Keys are *_options table names; values are per-field dicts merged beneath each host’s own options.

Returns

Lab

Fully defined lab instance.

class otto.configmodule.repo.DockerImage(name, dockerfile, context, target=None, build_args=())

Bases: object

A Dockerfile-built image declared by a project.

name : --is-rst--:py:class:`str`

Short logical name used in tags and CLI selection.

dockerfile : --is-rst--:py:class:`~pathlib.Path`

Absolute path to the Dockerfile.

context : --is-rst--:py:class:`~pathlib.Path`

Absolute path to the build context directory.

target : --is-rst--:py:class:`str` | :py:obj:`None` = None

Optional multi-stage build target.

build_args : --is-rst--:py:class:`tuple`\ \[:py:class:`tuple`\ \[:py:class:`str`, :py:class:`str`], :py:data:`...<Ellipsis>`] = ()

Frozen list of (name, value) build args. Tuples (not dicts) so the container is hashable and order is preserved for context-hash inputs.

class otto.configmodule.repo.DockerCompose(path, default_host=None, services=())

Bases: object

A docker-compose file contributed by a project.

path : --is-rst--:py:class:`~pathlib.Path`

Absolute path to the compose YAML file.

default_host : --is-rst--:py:class:`str` | :py:obj:`None` = None

Lab host id where this stack should run by default. Overridden by otto docker up --on <host>.

services : --is-rst--:py:class:`tuple`\ \[:py:class:`str`, :py:data:`...<Ellipsis>`] = ()

Service names declared in the compose file. Used to synthesize container host ids for tab-completion without parsing YAML at completion-fast-path time. The runtime is the source of truth and will warn on mismatch with docker compose config --services.

class otto.configmodule.repo.DockerSettings(registry_url='docker.io', images=(), composes=())

Bases: object

Per-repo docker configuration parsed from [docker] in settings.toml.

registry_url : --is-rst--:py:class:`str` = 'docker.io'

Default registry. Overridable per-image via the image’s tag prefix.

images : --is-rst--:py:class:`tuple`\ \[:py:class:`~otto.configmodule.repo.DockerImage`, :py:data:`...<Ellipsis>`] = ()

Images this project knows how to build.

composes : --is-rst--:py:class:`tuple`\ \[:py:class:`~otto.configmodule.repo.DockerCompose`, :py:data:`...<Ellipsis>`] = ()

Compose files this project contributes.

class otto.configmodule.repo.CollectedTest(nodeid, name, path, cls_name)

Bases: object

A single test item collected from a SUT repo’s test directories.

Attributes

nodeid :

Full pytest node ID, e.g. dir/test_x.py::ClassName::test_fn. Suitable for use directly as the SUITE argument to otto test.

name :

Test function name only, e.g. test_fn.

path :

Absolute path to the test file.

cls_name :

Class name if the test belongs to a class, else None.

nodeid : --is-rst--:py:class:`str`
name : --is-rst--:py:class:`str`
path : --is-rst--:py:class:`~pathlib.Path`
cls_name : --is-rst--:py:class:`str` | :py:obj:`None`
class otto.configmodule.repo.Repo(sutDir, settings=<factory>)

Bases: object

sutDir : --is-rst--:py:class:`~pathlib.Path`

SUT directory from which the settings came.

name : --is-rst--:py:class:`str`

Product/repo name

version : --is-rst--:py:class:`~otto.configmodule.version.Version`

Product version

labs : --is-rst--:py:class:`list`\ \[:py:class:`~pathlib.Path`]

Paths to lab data

libs : --is-rst--:py:class:`list`\ \[:py:class:`~pathlib.Path`]

Extra paths to add to the PYTHONPATH

init : --is-rst--:py:class:`list`\ \[:py:class:`str`]

Module paths that need to be imported during otto init.

Modules containing instructions are an example of modules that need to be imported eagerly.

tests : --is-rst--:py:class:`list`\ \[:py:class:`~pathlib.Path`]

Directories that contain test suites.

host_defaults : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]]

Per-protocol option defaults applied to every host loaded under this repo’s labs. Keys are *_options table names (ssh_options, telnet_options, etc.); values are dicts whose keys correspond to fields on the matching options dataclass.

settings : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]

Repo settings dict as parsed from the settings.toml file

docker_settings : --is-rst--:py:class:`~otto.configmodule.repo.DockerSettings`

Parsed [docker] table — image build definitions, compose files, and registry URL. Defaults to an empty DockerSettings when the section is absent.

getLabPanel()
Return type:

Panel

getInstructionsPanel()

Build a Rich panel listing all instructions contributed by this repo.

Instructions are attributed to this repo by matching each registered instruction’s module against the module prefixes in init.

Return type:

Panel

collectTests()

Collect all tests from this repo’s configured test directories.

Performs a single pytest collection pass (no tests are executed). The returned list can be passed to any of the get*Panel methods so that multiple listing options share one collection run.

Return type:

list[CollectedTest]

Returns

list[CollectedTest]

One entry per discovered test item, in collection order.

getTestsPanel(items)

Rich panel listing every individual test with its full run syntax.

Each line shows otto test <absolute-path>::[Class::]test_fn which can be copy-pasted directly to run that specific test regardless of the current working directory.

Return type:

Panel

Parameters

items :

Pre-collected tests from collectTests().

getTestFilesPanel(items)

Rich panel listing unique test files with their run syntax.

Each line shows otto test <absolute-path> which runs all tests in that file.

Return type:

Panel

Parameters

items :

Pre-collected tests from collectTests().

getTestSuitesPanel(items)

Rich panel listing unique test suites with their run syntax.

Only class-based tests are listed, using just ClassName — the subcommand name passed directly to otto test ClassName. Bare functions (not part of a class) are omitted since they have no corresponding otto test subcommand. Entries are de-duplicated and preserve collection order.

Return type:

Panel

Parameters

items :

Pre-collected tests from collectTests().

getOttoSettingsPath()

Create the path to the otto settings TOML file.

Return type:

Path

Returns

Path to the otto settings TOML file.

Raises

FileNotFoundError

If the TOML file is not found.

readSettings()
Return type:

str

parseSettings()

Parse the settings TOML file in the repo’s .otto directory.

Return type:

None

property reservationSettings : dict[str, Any]

Return the [reservations] settings sub-dict with ${sutDir} expanded.

Returns an empty dict when the section is absent. Every string value (including nested tables) has ${sutDir} substituted so the reservation backend can use the same path-expansion convention as the other repo settings.

addLibsToPythonpath()

Add configured library directories to the PYTHONPATH

Return type:

None

importInitModules()
Return type:

None

importTestFiles()

Import test_*.py files from each configured tests directory.

This triggers @register_suite() decorators, which populate otto.suite.register._SUITE_REGISTRY at import time. The registry is later consumed by cli/test.py to add sub-Typers to testing_app.

Return type:

None

applySettings()
async setGitDescription()
async setCommitHash()
property commit
property description
property commitName : str
async runGitCommand(cmd)
Return type:

CommandStatus

otto.configmodule.repo.applyRepoSettings(repos)
Return type:

None

otto.configmodule.repo.getRepos(repos)

Create Repo objects from the list of provided repo paths.

Return type:

list[Repo]

Parameters

repos : List of paths to repos under test.

Returns

List of Repo objects

Raises

FileNotFoundError

If a repo’s settings TOML file is not found.

class otto.configmodule.version.Version(version)

Bases: object

major : --is-rst--:py:class:`int`

Product major version.

minor : --is-rst--:py:class:`int`

Product minor version.

patch : --is-rst--:py:class:`int`

Product patch version.