models.settings

Pydantic boundary specs for .otto/settings.toml and the OTTO_* env.

These validate the settings dict (extra='forbid') and build the unchanged runtime objects (DockerSettings/DockerImage/DockerCompose frozen dataclasses, OsProfile, the reservation backend) via to_runtime() — the same two-type split the option/host specs use.

Leaf isolation: this module must NOT import from otto.configmodule at module top — doing so triggers configmodule/__init__’s app bootstrap. Runtime types from configmodule.repo are imported lazily inside to_runtime() and under TYPE_CHECKING for annotations only.

class otto.models.settings.DockerImageSpec(*, name: str, dockerfile: ~pathlib.Path, context: ~pathlib.Path, target: str | None = None, build_args: dict[str, ~typing.Any] = <factory>)

Bases: OttoModel

Boundary spec for a [[docker.images]] entry in settings.toml.

Validates the image name, Dockerfile path, build context, optional build stage target, and build_args dict (scalar TOML values are accepted and stringified). Builds a DockerImage runtime dataclass via to_runtime(), with build_args normalised to a sorted, frozen tuple-of-pairs for hashability.

name : str
dockerfile : Path
context : Path
target : str | None
build_args : dict[str, Any]
to_runtime() DockerImage

Build the DockerImage runtime dataclass from the validated spec fields.

class otto.models.settings.DockerComposeSpec(*, path: Path, default_host: str | None = None, services: tuple[str, ...] = ())

Bases: OttoModel

Boundary spec for a [[docker.composes]] entry in settings.toml.

Validates the Compose file path, an optional default service host name, and the list of services within the Compose project. Builds a DockerCompose runtime dataclass via to_runtime().

path : Path
default_host : str | None
services : tuple[str, ...]
to_runtime() DockerCompose

Build the DockerCompose runtime dataclass from the validated spec fields.

class otto.models.settings.DockerSettingsSpec(*, registry_url: str = 'docker.io', images: list[~otto.models.settings.DockerImageSpec] = <factory>, composes: list[~otto.models.settings.DockerComposeSpec] = <factory>)

Bases: OttoModel

Boundary spec for the [docker] section of settings.toml.

Validates the Docker registry URL and the lists of image and Compose specs. Builds a DockerSettings runtime dataclass (with images and composes as frozen tuples) via to_runtime().

registry_url : str
images : list[DockerImageSpec]
composes : list[DockerComposeSpec]
to_runtime() DockerSettings

Build the DockerSettings runtime dataclass from the validated spec fields.

class otto.models.settings.OsProfileSpec(*, base: str, **extra_data: Any)

Bases: OttoModel

A named [os_profiles.<name>] bundle: a base host-class plus raw default field values.

extra='allow' collects the non-base keys; the per-field typo guard runs later, in register_os_profile (against the base class’s slots), so the bundle stays raw here exactly as a hosts.json entry would be.

base : str
property defaults : dict[str, Any]

Return the non-base extra fields as a plain dict of host field defaults.

class otto.models.settings.ReservationConfigSpec(*, backend: str = 'none', url: str | None = None, **extra_data: Any)

Bases: OttoModel

The otto-owned [reservations] envelope: backend + optional url.

extra='allow' keeps the backend-specific [reservations.<backend>] sub-table open — otto-core cannot type a third-party backend’s kwargs.

backend : str
url : str | None
class otto.models.settings.LoggingConfigSpec(*, capture: list[str] = <factory>)

Bases: OttoModel

Boundary spec for the [logging] section of settings.toml.

capture lists top-level logger prefixes whose logging.getLogger(__name__) records otto should route into its sinks (in addition to the package prefixes auto-derived from a repo’s init/libs). Defaults to an empty list.

capture : list[str]
class otto.models.settings.LabConfigSpec(*, backend: str = 'json', **extra_data: Any)

Bases: OttoModel

The otto-owned [lab] envelope: which host-source backend to use.

extra='allow' keeps the backend-specific [lab.<backend>] sub-table open — otto-core cannot type a third-party backend’s kwargs. Defaults to the built-in "json" backend so repos with no [lab] block behave exactly as before.

backend : str
class otto.models.settings.ReservationEntry(*, user: str, resources: list[str], expires: datetime | None = None)

Bases: OttoModel

A single reservation record: the holder, the reserved resource names, and an optional expiry.

The expires field accepts an ISO-8601 string from JSON (including trailing Z) and normalises it to a timezone-aware datetime via _normalize_expires.

user : str
resources : list[str]
expires : datetime | None
class otto.models.settings.ReservationFile(*, version: ~typing.Literal[1], reservations: list[~otto.models.settings.ReservationEntry] = <factory>)

Bases: OttoModel

The version: 1 JSON reservation file the built-in JSON backend reads.

version : Literal[1]
reservations : list[ReservationEntry]
class otto.models.settings.SettingsModel(*, name: str, version: str, lab_data_type: str = 'json', coverage: dict[str, ~typing.Any] = <factory>, labs: list[~pathlib.Path] = <factory>, valid_labs: list[str] = <factory>, libs: list[~pathlib.Path] = <factory>, tests: list[~pathlib.Path] = <factory>, init: list[str] = <factory>, host_preferences: dict[str, dict[str, ~typing.Any]] = <factory>, os_profiles: dict[str, ~otto.models.settings.OsProfileSpec] = <factory>, docker: ~otto.models.settings.DockerSettingsSpec = DockerSettingsSpec(registry_url='docker.io', images=[], composes=[]), lab: ~otto.models.settings.LabConfigSpec = LabConfigSpec(backend='json'), logging: ~otto.models.settings.LoggingConfigSpec = LoggingConfigSpec(capture=[]), reservations: ~otto.models.settings.ReservationConfigSpec = ReservationConfigSpec(backend='none', url=None))

Bases: OttoModel

Boundary model for a repo’s .otto/settings.toml (post ${sut_dir} expansion).

extra='forbid' turns a typo’d top-level key into an error.

name : str
version : str
lab_data_type : str
coverage : dict[str, Any]
labs : list[Path]
valid_labs : list[str]
libs : list[Path]
tests : list[Path]
init : list[str]
host_preferences : dict[str, dict[str, Any]]
os_profiles : dict[str, OsProfileSpec]
docker : DockerSettingsSpec
lab : LabConfigSpec
logging : LoggingConfigSpec
reservations : ReservationConfigSpec
class otto.models.settings.OttoEnvSettings

Bases: BaseSettings

Typed view of the OTTO_* environment surface; single source of truth for otto’s env vars.

The six CLI-option vars are read by Typer’s envvar= at parse time; this model documents the whole surface and is the reader for the non-CLI reads: sut_dirs, field_default, compose_suffix, and the completion-cache xdir.

sut_dirs existence-checking is done by configmodule.env.load_otto_env so a missing dir raises FileNotFoundError (not a wrapped ValidationError).

sut_dirs : Annotated[list[Path], NoDecode]
lab : str | None
xdir : Path | None
log_days : int
log_level : str
log_rich : bool
field_default : str | None
field_products : str | None
compose_suffix : str | None