Skip to navigation

nemo_gym.sandbox.providers.docker

View as Markdown

Docker provider package.

Submodules

Package Contents

Classes

NameDescription
DockerCreateConfig-
DockerCreateErrorRaised when Docker cannot create a sandbox.
DockerCreateVerificationErrorRaised when a new container fails its readiness probe.
DockerExecConfig-
DockerProbeConfig-
DockerProviderSandbox provider backed by the local Docker CLI / daemon.

API

class nemo_gym.sandbox.providers.docker.DockerCreateConfig(
keepalive_shell: str = DEFAULT_KEEPALIVE_SHELL,
keepalive_cmd: str = DEFAULT_KEEPALIVE_CMD,
start_timeout_s: float | None = 600,
use_init: bool = True,
network: str | None = None,
read_only: bool = False,
cap_drop: list[str] = list(),
security_opt: list[str] = list(),
pids_limit: int | None = None,
extra_run_args: list[str] = list(),
apply_resource_limits: bool = True,
publish_host: str = '127.0.0.1',
transport_retries: int = 2,
transport_retry_delay_s: float = 1.0
)
Dataclass
apply_resource_limits
bool = True
cap_drop
list[str] = field(default_factory=list)
extra_run_args
list[str] = field(default_factory=list)
keepalive_cmd
str = DEFAULT_KEEPALIVE_CMD
keepalive_shell
str = DEFAULT_KEEPALIVE_SHELL
network
str | None = None
pids_limit
int | None = None
publish_host
str = '127.0.0.1'
read_only
bool = False
security_opt
list[str] = field(default_factory=list)
start_timeout_s
float | None = 600
transport_retries
int = 2
transport_retry_delay_s
float = 1.0
use_init
bool = True
nemo_gym.sandbox.providers.docker.DockerCreateConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.docker.DockerCreateError()

Bases: SandboxCreateError

Raised when Docker cannot create a sandbox.

class nemo_gym.sandbox.providers.docker.DockerCreateVerificationError()

Bases: SandboxCreateVerificationError

Raised when a new container fails its readiness probe.

class nemo_gym.sandbox.providers.docker.DockerExecConfig(
default_timeout_s: float | None = 180,
extra_exec_args: list[str] = list(),
concurrency: int = 32,
exec_shell: str | None = None
)
Dataclass
concurrency
int = 32
default_timeout_s
float | None = 180
exec_shell
str | None = None
extra_exec_args
list[str] = field(default_factory=list)
nemo_gym.sandbox.providers.docker.DockerExecConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.docker.DockerProbeConfig(
command: str | None = READY_PROBE_COMMAND,
expected_stdout: str | None = READY_PROBE_EXPECTED,
timeout_s: int = 30,
deadline_s: float | None = None,
stable_count: int = 1,
stable_delay_s: float = 0.0
)
Dataclass
command
str | None = READY_PROBE_COMMAND
deadline_s
float | None = None
expected_stdout
str | None = READY_PROBE_EXPECTED
stable_count
int = 1
stable_delay_s
float = 0.0
timeout_s
int = 30
nemo_gym.sandbox.providers.docker.DockerProbeConfig.__post_init__() -> None
class nemo_gym.sandbox.providers.docker.DockerProvider(
exec: nemo_gym.sandbox.providers.docker.provider.DockerExecConfig | collections.abc.Mapping[str, typing.Any] | None = None,
create: nemo_gym.sandbox.providers.docker.provider.DockerCreateConfig | collections.abc.Mapping[str, typing.Any] | None = None,
probe: nemo_gym.sandbox.providers.docker.provider.DockerProbeConfig | collections.abc.Mapping[str, typing.Any] | None = None
)

Sandbox provider backed by the local Docker CLI / daemon.

SERVICE_OPTION_FIELDS
_binary
= _require_docker()
_create_config
= _coerce_config(create, DockerCreateConfig)
_exec_config
= _coerce_config(exec, DockerExecConfig)
_probe
= _coerce_config(probe, DockerProbeConfig)
_pty_sessions
dict[str, set[DockerPtySession]] = {}
_semaphore
= asyncio.Semaphore(self._exec_config.concurrency)
_spawn_pty
= staticmethod(docker_pty.spawn)
name
= 'docker'
nemo_gym.sandbox.providers.docker.DockerProvider._cleanup_failed_create_handle(
) -> None
async
nemo_gym.sandbox.providers.docker.DockerProvider._force_remove(
name: str
) -> None
async
nemo_gym.sandbox.providers.docker.DockerProvider._inspect_owned_container(
name: str,
image: str
) -> str | None
async

Return state for an exact owned container, None when it cannot be resolved.

nemo_gym.sandbox.providers.docker.DockerProvider._pty_argv(
session_id: str
) -> list[str]

The docker exec command line of one PTY session: interactive, with a terminal in PTY mode.

The session’s marker (NEMO_GYM_PTY_SESSION=<session id>) goes last among the --env flags so no spec env can override it: it is how the recorded pid is known to be this session’s process.

nemo_gym.sandbox.providers.docker.DockerProvider._reconcile_created_container(
name: str,
image: str
) -> bool
async

Recover the exact Sandbox-owned container after an ambiguous docker run.

nemo_gym.sandbox.providers.docker.DockerProvider._release_pty_session(
name: str,
abandon: bool
) -> None
async

One session of _release_pty_sessions; a failure is logged, never raised into the caller.

nemo_gym.sandbox.providers.docker.DockerProvider._release_pty_sessions(
name: str,
abandon: bool
) -> None
async

Close (or, when the container goes away, abandon) the live sessions of container name.

nemo_gym.sandbox.providers.docker.DockerProvider._resolve_shell(
name: str
) -> str
async

Configured exec shell, else bash when the image has it (for conda source), else sh.

nemo_gym.sandbox.providers.docker.DockerProvider._run(
argv: list[str],
timeout_s: float | None,
stdin: bytes | None = None
) -> tuple[int, str, str]
async

Run a docker CLI command as (return_code, stdout, stderr); SIGKILL the group on timeout.

nemo_gym.sandbox.providers.docker.DockerProvider._run_create_with_reconciliation(
argv: list[str],
name: str,
image: str
) -> None
async

Run docker run without leaking a container when its response is lost.

A fixed, random name makes retry idempotent. After a timeout, transport error, or same-name conflict, inspect that exact name before retrying. Reuse it only when both the Sandbox ownership label and requested image match; never remove an unrelated collision.

nemo_gym.sandbox.providers.docker.DockerProvider._start_reconciled_container(
name: str,
image: str
) -> bool
async

Finish a docker run that created its container but lost the start response.

nemo_gym.sandbox.providers.docker.DockerProvider._verify_created_handle(
) -> None
async

Poll the readiness probe until it passes stable_count times or the deadline elapses.

nemo_gym.sandbox.providers.docker.DockerProvider.aclose() -> None
async

Close the PTY sessions still open; the containers themselves belong to their handles.

nemo_gym.sandbox.providers.docker.DockerProvider.close(
) -> None
async

Force-remove the container (already-gone counts as success); its PTY clients are released first.

nemo_gym.sandbox.providers.docker.DockerProvider.connect(
descriptor: collections.abc.Mapping[str, typing.Any]
async

Create a handle for an existing running container, rediscovering its image and shell via the Docker CLI.

sandbox_id is required. AsyncSandbox.serialize() adds a top-level ports key from the original spec; read it back here so endpoint() still works after reconnecting.

nemo_gym.sandbox.providers.docker.DockerProvider.create(
async

Start a detached keep-alive container (image ENTRYPOINT overridden) and probe readiness.

spec.ttl_s bounds the lifetime (the keep-alive sleeps for it and --rm self-removes on exit). spec.provider_options may carry volumes (-> -v) and run_args (extra run flags). A half-created container is force-removed on any failure.

async

Open an interactive terminal in the container: one docker exec -it client under a host pty.

cwd, env and user map to the exec flags, rows/cols size the terminal before the client starts, pty=False runs the client on pipes. The client process is not counted against exec.concurrency: it lives as long as the session. The session’s pid is recorded under /tmp in the container (under /dev/shm when /tmp refuses the write, as under create.read_only: true; a session that can record it nowhere ends at once with a message instead of running unsignallable), read as the session’s own user and signalled as root (no CAP_SYS_PTRACE needed). This returns as soon as the client is spawned: send_signal()/close() wait up to two seconds for the record when they come first, refuse at once a record that is not a regular file, and read it under a timeout of their own (PID_READ_TIMEOUT_S) rather than exec.default_timeout_s. A client that exits without having started the program (the daemon refused the exec: the container paused or gone, the daemon unreachable, told by the client’s stderr as exec() tells a runtime failure) ends the session with SandboxPtyError from wait_exit() and read() rather than as the program’s exit; a container closed while the session was being opened has it abandoned and SandboxPtyError raised here. See nemo_gym.sandbox.providers.docker.pty.

nemo_gym.sandbox.providers.docker.DockerProvider.download_file(
source_path: str,
target_path: pathlib.Path
) -> None
async

Download one container file to the host.

nemo_gym.sandbox.providers.docker.DockerProvider.endpoint(
port: int
async

Resolve a dynamically-published TCP port through the Docker daemon.

nemo_gym.sandbox.providers.docker.DockerProvider.exec(
command: str,
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout_s: int | float | None = None,
user: str | int | None = None,
stdin: bytes | None = None
async

Run <shell> -c <command> via docker exec; never raises for command failure.

user maps to --user (root/0 -> 0). A timeout kills the local docker client only; the in-container process is reaped when the sandbox is closed.

nemo_gym.sandbox.providers.docker.DockerProvider.pull_policy_options(
policy: str
) -> dict[str, typing.Any]

Compose’s pull_policy as docker run --pull; only always, missing and never exist.

nemo_gym.sandbox.providers.docker.DockerProvider.serialize_handle(
scope: str | None = None
) -> dict[str, typing.Any]
async

Return a descriptor for reattaching to this container by id.

Docker recovers image and shell from the running container on connect, and docker exec runs inside the container’s own environment, so the id alone is enough; AsyncSandbox.serialize() layers the declared ports on top. scope is unused by the Docker provider.

nemo_gym.sandbox.providers.docker.DockerProvider.service_options(
tmpfs: tuple[str, ...] = (),
ulimits: collections.abc.Mapping[str, typing.Any] | None = None,
sysctls: collections.abc.Mapping[str, str] | None = None,
security_opt: tuple[str, ...] = ()
) -> dict[str, typing.Any]

Compose service options as run_args: --tmpfs, --ulimit, --sysctl, --security-opt.

nemo_gym.sandbox.providers.docker.DockerProvider.status(
async

Container status via docker inspect (missing -> STOPPED; error/timeout -> UNKNOWN).

nemo_gym.sandbox.providers.docker.DockerProvider.upload_file(
source_path: pathlib.Path,
target_path: str
) -> None
async

Upload one host file (creates the parent dir; the file lands owned by root).