Null backend for .protocol.ContainerRuntime.
Runs no subprocesses — useful for tests and dry-run modes. Every handle
operation returns a safe default (None, False, [], empty
strings). NullRuntime.exec returns an empty success.
State fixtures can be injected via NullRuntime.set_container_state
and NullRuntime.add_image when tests need a specific shape.
ExecStdioStep = tuple[str, bytes]
module-attribute
NullLogStream
Empty log stream — yields no lines and closes cleanly.
__iter__()
Return self — iteration yields nothing.
Source code in src/terok_sandbox/runtime/null.py
| def __iter__(self) -> NullLogStream:
"""Return self — iteration yields nothing."""
return self
|
__next__()
Terminate iteration immediately.
Source code in src/terok_sandbox/runtime/null.py
| def __next__(self) -> str:
"""Terminate iteration immediately."""
raise StopIteration
|
__enter__()
Enter no-op context.
Source code in src/terok_sandbox/runtime/null.py
| def __enter__(self) -> NullLogStream:
"""Enter no-op context."""
return self
|
__exit__(*exc)
Exit no-op context.
Source code in src/terok_sandbox/runtime/null.py
| def __exit__(self, *exc: object) -> None:
"""Exit no-op context."""
|
close()
Nothing to close.
Source code in src/terok_sandbox/runtime/null.py
| def close(self) -> None:
"""Nothing to close."""
return None
|
NullPortReservation(host='127.0.0.1')
Real port reservation (binds a socket) — useful even in null mode
because callers usually need an actually-free port to hand off.
Source code in src/terok_sandbox/runtime/null.py
| def __init__(self, host: str = "127.0.0.1") -> None:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind((host, 0))
except BaseException:
s.close()
raise
self._socket: socket.socket | None = s
self.port = s.getsockname()[1]
|
port = s.getsockname()[1]
instance-attribute
__enter__()
Enter the context.
Source code in src/terok_sandbox/runtime/null.py
| def __enter__(self) -> NullPortReservation:
"""Enter the context."""
return self
|
__exit__(*exc)
Release the port.
Source code in src/terok_sandbox/runtime/null.py
| def __exit__(self, *exc: object) -> None:
"""Release the port."""
self.close()
|
close()
Release the port (idempotent).
Source code in src/terok_sandbox/runtime/null.py
| def close(self) -> None:
"""Release the port (idempotent)."""
if self._socket is not None:
self._socket.close()
self._socket = None
|
NullContainer(name, *, runtime)
Container handle that reads from a NullRuntime's fixtures.
Source code in src/terok_sandbox/runtime/null.py
| def __init__(self, name: str, *, runtime: NullRuntime) -> None:
self.name = name
self._runtime = runtime
|
name = name
instance-attribute
state
property
Return the fixture state, or None when unset.
running
property
Return True when the fixture state is "running".
image
property
Return the fixture image, or None when unset.
rw_size
property
Return the fixture rw_size, or None when unset.
__repr__()
Render as NullContainer(name='...').
Source code in src/terok_sandbox/runtime/null.py
| def __repr__(self) -> str:
"""Render as ``NullContainer(name='...')``."""
return f"NullContainer(name={self.name!r})"
|
__eq__(other)
Equality by name.
Source code in src/terok_sandbox/runtime/null.py
| def __eq__(self, other: object) -> bool:
"""Equality by name."""
return isinstance(other, NullContainer) and self.name == other.name
|
__hash__()
Hash by name.
Source code in src/terok_sandbox/runtime/null.py
| def __hash__(self) -> int:
"""Hash by name."""
return hash(("NullContainer", self.name))
|
start()
Flip the fixture state to "running".
Source code in src/terok_sandbox/runtime/null.py
| def start(self) -> None:
"""Flip the fixture state to ``"running"``."""
self._runtime._container_states[self.name] = "running"
|
stop(*, timeout=10)
Flip the fixture state to "exited".
Source code in src/terok_sandbox/runtime/null.py
| def stop(self, *, timeout: int = 10) -> None:
"""Flip the fixture state to ``"exited"``."""
self._runtime._container_states[self.name] = "exited"
|
wait(timeout=None)
Return the fixture exit code (default 0).
Source code in src/terok_sandbox/runtime/null.py
| def wait(self, timeout: float | None = None) -> int:
"""Return the fixture exit code (default 0)."""
return self._runtime._container_exit_codes.get(self.name, 0)
|
copy_in(src, dest)
Record the copy_in call for test inspection.
Source code in src/terok_sandbox/runtime/null.py
| def copy_in(self, src: Path, dest: str) -> None:
"""Record the copy_in call for test inspection."""
self._runtime._copy_in_calls.append((self.name, src, dest))
|
login_command(*, command=())
Return a placeholder login argv.
Source code in src/terok_sandbox/runtime/null.py
| def login_command(self, *, command: tuple[str, ...] = ()) -> list[str]:
"""Return a placeholder login argv."""
return ["null-exec", self.name, *command]
|
logs(*, follow=False, tail=None)
Return an empty NullLogStream.
Source code in src/terok_sandbox/runtime/null.py
| def logs(self, *, follow: bool = False, tail: int | None = None) -> LogStream:
"""Return an empty [`NullLogStream`][terok_sandbox.runtime.null.NullLogStream]."""
return NullLogStream()
|
stream_initial_logs(ready_check, timeout_sec)
Return the fixture readiness result (default True).
Source code in src/terok_sandbox/runtime/null.py
| def stream_initial_logs(
self,
ready_check: Callable[[str], bool],
timeout_sec: float | None,
) -> bool:
"""Return the fixture readiness result (default ``True``)."""
return self._runtime._ready_results.get(self.name, True)
|
NullImage(ref, *, runtime)
Image handle backed by NullRuntime fixtures.
Source code in src/terok_sandbox/runtime/null.py
| def __init__(self, ref: str, *, runtime: NullRuntime) -> None:
self.ref = ref
self._runtime = runtime
|
ref = ref
instance-attribute
id
property
Return the ref itself when the image exists, else None.
repository
property
Return the fixture repository, or "".
tag
property
Return the fixture tag, or "".
size
property
Return the fixture size, or "".
created
property
Return the fixture created timestamp, or "".
__repr__()
Render as NullImage(ref='...').
Source code in src/terok_sandbox/runtime/null.py
| def __repr__(self) -> str:
"""Render as ``NullImage(ref='...')``."""
return f"NullImage(ref={self.ref!r})"
|
__eq__(other)
Equality by ref.
Source code in src/terok_sandbox/runtime/null.py
| def __eq__(self, other: object) -> bool:
"""Equality by ref."""
return isinstance(other, NullImage) and self.ref == other.ref
|
__hash__()
Hash by ref.
Source code in src/terok_sandbox/runtime/null.py
| def __hash__(self) -> int:
"""Hash by ref."""
return hash(("NullImage", self.ref))
|
exists()
Return True when the fixture lists this ref.
Source code in src/terok_sandbox/runtime/null.py
| def exists(self) -> bool:
"""Return ``True`` when the fixture lists this ref."""
return self.ref in self._runtime._image_records
|
labels()
Return the fixture labels, or {}.
Source code in src/terok_sandbox/runtime/null.py
| def labels(self) -> dict[str, str]:
"""Return the fixture labels, or ``{}``."""
return dict(self._runtime._image_labels.get(self.ref, {}))
|
history()
Return the fixture history, or [].
Source code in src/terok_sandbox/runtime/null.py
| def history(self) -> list[str]:
"""Return the fixture history, or ``[]``."""
return list(self._runtime._image_history.get(self.ref, ()))
|
remove()
Remove from the fixture; return True when actually removed.
Source code in src/terok_sandbox/runtime/null.py
| def remove(self) -> bool:
"""Remove from the fixture; return ``True`` when actually removed."""
had = self.ref in self._runtime._image_records
self._runtime._image_records.pop(self.ref, None)
self._runtime._image_labels.pop(self.ref, None)
self._runtime._image_history.pop(self.ref, None)
return had
|
NullRuntime()
Stub ContainerRuntime for tests and dry-run modes.
All state lives in dictionaries on the runtime instance. Tests
pre-populate fixtures via the set_container_state,
add_image, etc. helpers.
Source code in src/terok_sandbox/runtime/null.py
| def __init__(self) -> None:
self._container_states: dict[str, str] = {}
self._container_images: dict[str, str] = {}
self._container_rw_sizes: dict[str, int] = {}
self._container_exit_codes: dict[str, int] = {}
self._ready_results: dict[str, bool] = {}
self._image_records: dict[str, dict[str, str]] = {}
self._image_labels: dict[str, dict[str, str]] = {}
self._image_history: dict[str, tuple[str, ...]] = {}
self._exec_results: dict[tuple[str, tuple[str, ...]], ExecResult] = {}
self._exec_stdio_scripts: dict[
tuple[str, tuple[str, ...]], tuple[tuple[ExecStdioStep, ...], int]
] = {}
self._exec_stdio_calls: list[tuple[str, tuple[str, ...], dict[str, str]]] = []
self._copy_in_calls: list[tuple[str, Path, str]] = []
self._force_remove_calls: list[list[str]] = []
|
set_container_state(name, state)
Record state ("running", "exited", ...) for container name.
Source code in src/terok_sandbox/runtime/null.py
| def set_container_state(self, name: str, state: str) -> None:
"""Record *state* (``"running"``, ``"exited"``, ...) for container *name*."""
self._container_states[name] = state
|
set_container_image(name, image_ref)
Record the image ref behind container name.
Source code in src/terok_sandbox/runtime/null.py
| def set_container_image(self, name: str, image_ref: str) -> None:
"""Record the image ref behind container *name*."""
self._container_images[name] = image_ref
|
set_container_rw_size(name, bytes_)
Record the writable-layer size of container name.
Source code in src/terok_sandbox/runtime/null.py
| def set_container_rw_size(self, name: str, bytes_: int) -> None:
"""Record the writable-layer size of container *name*."""
self._container_rw_sizes[name] = bytes_
|
set_exit_code(name, code)
Record the exit code Container.wait will return for name.
Source code in src/terok_sandbox/runtime/null.py
| def set_exit_code(self, name: str, code: int) -> None:
"""Record the exit code [`Container.wait`][terok_sandbox.runtime.Container.wait] will return for *name*."""
self._container_exit_codes[name] = code
|
set_ready_result(name, ready)
Record the outcome Container.stream_initial_logs returns.
Source code in src/terok_sandbox/runtime/null.py
| def set_ready_result(self, name: str, ready: bool) -> None:
"""Record the outcome [`Container.stream_initial_logs`][terok_sandbox.runtime.Container.stream_initial_logs] returns."""
self._ready_results[name] = ready
|
add_image(ref, *, repository='', tag='', size='', created='', labels=None, history=())
Register an image fixture.
Source code in src/terok_sandbox/runtime/null.py
| def add_image(
self,
ref: str,
*,
repository: str = "",
tag: str = "",
size: str = "",
created: str = "",
labels: dict[str, str] | None = None,
history: tuple[str, ...] = (),
) -> None:
"""Register an image fixture."""
self._image_records[ref] = {
"repository": repository,
"tag": tag,
"size": size,
"created": created,
}
if labels:
self._image_labels[ref] = dict(labels)
if history:
self._image_history[ref] = tuple(history)
|
set_exec_result(container_name, cmd, result)
Pre-register the result exec returns for exact cmd.
Source code in src/terok_sandbox/runtime/null.py
| def set_exec_result(
self,
container_name: str,
cmd: tuple[str, ...],
result: ExecResult,
) -> None:
"""Pre-register the result [`exec`][exec] returns for exact *cmd*."""
self._exec_results[(container_name, cmd)] = result
|
set_exec_stdio_script(container_name, cmd, script, *, exit_code=0)
Pre-register a stdio interaction for exec_stdio.
script is a sequence of ("read", bytes) / ("write", bytes)
steps replayed in order: read consumes the matching prefix from
the caller-supplied stdin; write emits the bytes to stdout.
Use this to drive deterministic ACP-handshake tests without spinning
up a real container.
Source code in src/terok_sandbox/runtime/null.py
| def set_exec_stdio_script(
self,
container_name: str,
cmd: tuple[str, ...],
script: tuple[ExecStdioStep, ...],
*,
exit_code: int = 0,
) -> None:
"""Pre-register a stdio interaction for [`exec_stdio`][terok_sandbox.runtime.null.NullRuntime.exec_stdio].
*script* is a sequence of ``("read", bytes)`` / ``("write", bytes)``
steps replayed in order: ``read`` consumes the matching prefix from
the caller-supplied *stdin*; ``write`` emits the bytes to *stdout*.
Use this to drive deterministic ACP-handshake tests without spinning
up a real container.
"""
self._exec_stdio_scripts[(container_name, cmd)] = (tuple(script), exit_code)
|
container(name)
Return a NullContainer handle.
Source code in src/terok_sandbox/runtime/null.py
| def container(self, name: str) -> Container:
"""Return a [`NullContainer`][terok_sandbox.runtime.null.NullContainer] handle."""
return NullContainer(name, runtime=self)
|
containers_with_prefix(prefix)
Return fixtures whose name starts with prefix-.
Source code in src/terok_sandbox/runtime/null.py
| def containers_with_prefix(self, prefix: str) -> list[Container]:
"""Return fixtures whose name starts with ``prefix-``."""
return [
NullContainer(name, runtime=self)
for name in self._container_states
if name.startswith(f"{prefix}-")
]
|
image(ref)
Return a NullImage handle.
Source code in src/terok_sandbox/runtime/null.py
| def image(self, ref: str) -> Image:
"""Return a [`NullImage`][terok_sandbox.runtime.null.NullImage] handle."""
return NullImage(ref, runtime=self)
|
images(*, dangling_only=False)
Return fixture images; dangling_only filters by tag == "<none>".
Source code in src/terok_sandbox/runtime/null.py
| def images(self, *, dangling_only: bool = False) -> list[Image]:
"""Return fixture images; *dangling_only* filters by ``tag == "<none>"``."""
images: list[Image] = []
for ref, rec in self._image_records.items():
if dangling_only and rec.get("tag") != "<none>":
continue
images.append(NullImage(ref, runtime=self))
return images
|
exec(container, cmd, *, timeout=None)
Return a pre-registered result, or a default empty success.
Source code in src/terok_sandbox/runtime/null.py
| def exec(
self,
container: Container,
cmd: list[str],
*,
timeout: float | None = None,
) -> ExecResult:
"""Return a pre-registered result, or a default empty success."""
key = (container.name, tuple(cmd))
return self._exec_results.get(key, ExecResult(exit_code=0, stdout="", stderr=""))
|
exec_stdio(container, cmd, *, stdin, stdout, stderr=None, env=None, timeout=None)
Replay a pre-registered stdio script, or no-op with exit code 0.
Records every call (with env) for test inspection. When a script is
registered for (container, cmd), replays it in order: read
consumes from stdin and asserts a match; write pushes bytes to
stdout. Without a script, returns immediately with exit code 0
— matches the empty-success default of exec.
Source code in src/terok_sandbox/runtime/null.py
| def exec_stdio(
self,
container: Container,
cmd: list[str],
*,
stdin: BinaryIO,
stdout: BinaryIO,
stderr: BinaryIO | None = None,
env: Mapping[str, str] | None = None,
timeout: float | None = None,
) -> int:
"""Replay a pre-registered stdio script, or no-op with exit code 0.
Records every call (with env) for test inspection. When a script is
registered for ``(container, cmd)``, replays it in order: ``read``
consumes from *stdin* and asserts a match; ``write`` pushes bytes to
*stdout*. Without a script, returns immediately with exit code 0
— matches the empty-success default of [`exec`][terok_sandbox.runtime.null.NullRuntime.exec].
"""
key = (container.name, tuple(cmd))
self._exec_stdio_calls.append((container.name, tuple(cmd), dict(env or {})))
script_entry = self._exec_stdio_scripts.get(key)
if script_entry is None:
return 0
script, exit_code = script_entry
for direction, payload in script:
if direction == "read":
got = stdin.read(len(payload))
if got != payload:
raise AssertionError(
f"NullRuntime.exec_stdio script mismatch for {cmd!r}: "
f"expected {payload!r}, got {got!r}"
)
elif direction == "write":
stdout.write(payload)
stdout.flush()
else:
raise ValueError(f"unknown exec_stdio script direction: {direction!r}")
return exit_code
|
force_remove(containers)
Record the call and clear every fixture for each container.
Source code in src/terok_sandbox/runtime/null.py
| def force_remove(self, containers: list[Container]) -> list[ContainerRemoveResult]:
"""Record the call and clear every fixture for each container."""
names = [c.name for c in containers]
self._force_remove_calls.append(names)
for name in names:
self._container_states.pop(name, None)
self._container_images.pop(name, None)
self._container_rw_sizes.pop(name, None)
self._container_exit_codes.pop(name, None)
self._ready_results.pop(name, None)
# Drop any pre-registered exec results keyed by this container name
self._exec_results = {
key: result for key, result in self._exec_results.items() if key[0] != name
}
return [ContainerRemoveResult(name=n, removed=True) for n in names]
|
reserve_port(host='127.0.0.1')
Reserve a real host port (even null backend callers want a live port).
Source code in src/terok_sandbox/runtime/null.py
| def reserve_port(self, host: str = "127.0.0.1") -> PortReservation:
"""Reserve a real host port (even null backend callers want a live port)."""
return NullPortReservation(host)
|