> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.nvidia.com/nemo/gym/v0.2.1/nemo-gym/nemo_gym/sandbox/providers/enroot/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server. # nemo_gym.sandbox.providers.enroot Enroot provider package. ## Submodules * **[`nemo_gym.sandbox.providers.enroot.provider`](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/enroot/provider)** ## Package Contents ### Classes | Name | Description | | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | [`EnrootCreateConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateConfig) | Settings for creating an Enroot sandbox container. | | [`EnrootCreateError`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateError) | Raised when Enroot cannot create a sandbox. | | [`EnrootCreateVerificationError`](#nemo_gym-sandbox-providers-enroot-provider-EnrootCreateVerificationError) | Raised when a newly-created sandbox cannot execute a probe command. | | [`EnrootExecConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootExecConfig) | Settings for running commands inside an Enroot sandbox. | | [`EnrootProbeConfig`](#nemo_gym-sandbox-providers-enroot-provider-EnrootProbeConfig) | Post-create probe settings: a test command confirming the sandbox is usable. | | [`EnrootProvider`](#nemo_gym-sandbox-providers-enroot-provider-EnrootProvider) | Sandbox provider backed by the local Enroot CLI. | ### API ```python class nemo_gym.sandbox.providers.enroot.EnrootCreateConfig( mount_point: str = DEFAULT_MOUNT_POINT, base_dir: str | None = None, data_path: str | None = None, cache_path: str | None = None, runtime_path: str | None = None, sqsh_cache_dir: str | None = None, rw: bool = True, remap_root: bool = False, bypass_entrypoint: bool = True, init_command: str = DEFAULT_INIT_COMMAND, import_timeout_s: float | None = 1800, create_timeout_s: float | None = 600, start_timeout_s: float | None = 600, start_poll_s: float = 0.5, extra_import_args: list[str] = list(), extra_create_args: list[str] = list(), extra_start_args: list[str] = list() ) ``` Dataclass Settings for creating an Enroot sandbox container. **`base_dir`** `str | None = None` --- **`bypass_entrypoint`** `bool = True` --- **`cache_path`** `str | None = None` --- **`create_timeout_s`** `float | None = 600` --- **`data_path`** `str | None = None` --- **`extra_create_args`** `list[str] = field(default_factory=list)` --- **`extra_import_args`** `list[str] = field(default_factory=list)` --- **`extra_start_args`** `list[str] = field(default_factory=list)` --- **`import_timeout_s`** `float | None = 1800` --- **`init_command`** `str = DEFAULT_INIT_COMMAND` --- **`mount_point`** `str = DEFAULT_MOUNT_POINT` --- **`remap_root`** `bool = False` --- **`runtime_path`** `str | None = None` --- **`rw`** `bool = True` --- **`sqsh_cache_dir`** `str | None = None` --- **`start_poll_s`** `float = 0.5` --- **`start_timeout_s`** `float | None = 600` --- ```python nemo_gym.sandbox.providers.enroot.EnrootCreateConfig.__post_init__() -> None ``` ```python class nemo_gym.sandbox.providers.enroot.EnrootCreateError() ``` **Bases:** [SandboxCreateError](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/base#nemo_gym-sandbox-providers-base-SandboxCreateError) Raised when Enroot cannot create a sandbox. ```python class nemo_gym.sandbox.providers.enroot.EnrootCreateVerificationError() ``` **Bases:** [SandboxCreateVerificationError](/nemo/gym/nemo-gym/nemo_gym/sandbox/providers/base#nemo_gym-sandbox-providers-base-SandboxCreateVerificationError) Raised when a newly-created sandbox cannot execute a probe command. ```python class nemo_gym.sandbox.providers.enroot.EnrootExecConfig( default_timeout_s: float | None = 180, default_mounts: list[str] = list(), extra_exec_args: list[str] = list(), concurrency: int = 32, exec_shell: str = 'sh' ) ``` Dataclass Settings for running commands inside an Enroot sandbox. **`concurrency`** `int = 32` --- **`default_mounts`** `list[str] = field(default_factory=list)` --- **`default_timeout_s`** `float | None = 180` --- **`exec_shell`** `str = 'sh'` --- **`extra_exec_args`** `list[str] = field(default_factory=list)` --- ```python nemo_gym.sandbox.providers.enroot.EnrootExecConfig.__post_init__() -> None ``` ```python class nemo_gym.sandbox.providers.enroot.EnrootProbeConfig( 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 Post-create probe settings: a test command confirming the sandbox is usable. **`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` --- ```python nemo_gym.sandbox.providers.enroot.EnrootProbeConfig.__post_init__() -> None ``` ```python class nemo_gym.sandbox.providers.enroot.EnrootProvider( exec: nemo_gym.sandbox.providers.enroot.provider.EnrootExecConfig | collections.abc.Mapping[str, typing.Any] | None = None, create: nemo_gym.sandbox.providers.enroot.provider.EnrootCreateConfig | collections.abc.Mapping[str, typing.Any] | None = None, probe: nemo_gym.sandbox.providers.enroot.provider.EnrootProbeConfig | collections.abc.Mapping[str, typing.Any] | None = None ) ``` Sandbox provider backed by the local Enroot CLI. **`_binary`** `= _require_enroot()` --- **`_create_config`** `= _coerce_config(create, EnrootCreateConfig)` --- **`_enroot_env`** --- **`_exec_config`** `= _coerce_config(exec, EnrootExecConfig)` --- **`_import_locks`** `dict[str, Lock] = {}` --- **`_probe`** `= _coerce_config(probe, EnrootProbeConfig)` --- **`_semaphore`** `= asyncio.Semaphore(self._exec_config.concurrency)` --- **`_sqsh_cache_dir`** `= Path(cfg.sqsh_cache_dir or base / 'sqsh')` --- **`name`** `= 'enroot'` --- ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._await_container_pid( instance: nemo_gym.sandbox.providers.enroot.provider._EnrootInstance, err_f: typing.IO[bytes] ) -> int ``` async Poll `enroot list` until the container's init PID appears, or raise. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._cleanup_failed_create_handle( handle: nemo_gym.sandbox.providers.base.SandboxHandle ) -> None ``` async Best-effort teardown of a sandbox that failed to start or verify. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._ensure_image( image: str ) -> pathlib.Path ``` async Return a local squashfs path for `image`, importing (and caching) if needed. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._kill_start_group( instance: nemo_gym.sandbox.providers.enroot.provider._EnrootInstance ) -> None ``` Best-effort SIGTERM then SIGKILL of the detached start's process group. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._lookup_container( name: str ) -> tuple[int | None, bool] ``` async Return (pid, present). pid is the running init PID or None; present is whether the rootfs exists at all (running or not). ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._pid_has_container_marker( pid: int, name: str ) -> bool ``` staticmethod Return True if /proc/\/cmdline still carries the container's unique marker. This guards against PID reuse: if the init exits and Linux reuses the PID for an unrelated process, the container marker won't appear in the new process's cmdline, so exec() returns a sandbox error instead of joining the wrong namespace. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._read_temp( handle: typing.IO[bytes] ) -> str ``` staticmethod ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._resolve_image( image: str ) -> tuple[str | None, pathlib.Path | None] ``` Return (import\_uri, sqsh\_path). Exactly one is non-None. A local `.sqsh` file is used directly (no import). Anything else is translated to an enroot import URI. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._run( argv: list[str], timeout_s: float | None, stdin: bytes | None = None ) -> tuple[int, str, str] ``` async Run an enroot CLI command. Returns (return\_code, stdout, stderr). Enforces timeout via asyncio.wait\_for and kills the whole process group on timeout so child processes do not linger. Bounds concurrency with a shared semaphore. Decodes output with errors="replace". Every call runs with the pinned ENROOT\_\* environment. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._start_detached( argv: list[str] ) -> tuple[typing.Any, typing.IO[bytes], typing.IO[bytes]] ``` async Launch the long-lived `enroot start` init without awaiting its exit. `enroot start` does not daemonize — it stays in the foreground for the whole container lifetime. We launch it detached in its own session (start\_new\_session=True), capture output to temp files (so an early exit leaves diagnosable stderr), and return the process handle plus the temp files. The caller confirms readiness by polling `enroot list`. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider._verify_created_handle( handle: nemo_gym.sandbox.providers.base.SandboxHandle ) -> None ``` async Run the readiness probe until the sandbox responds, or raise. * probe.command is None -> skip (no verification). * probe.deadline\_s is None -> single attempt; a failure raises immediately. * probe.deadline\_s is set -> poll until the sandbox passes the probe `stable_count` consecutive times, or the deadline elapses. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.aclose() -> None ``` async No provider-wide resources to close. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.close( handle: nemo_gym.sandbox.providers.base.SandboxHandle ) -> None ``` async Kill the container init, remove the rootfs, and clean up the staging dir. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.create( spec: nemo_gym.sandbox.providers.base.SandboxSpec ) -> nemo_gym.sandbox.providers.base.SandboxHandle ``` async Import/create the rootfs, launch a detached init, and return a ready handle. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.download_file( handle: nemo_gym.sandbox.providers.base.SandboxHandle, source_path: str, target_path: pathlib.Path ) -> None ``` async Download one sandbox file to the host. Fast path (source under the bind mount): read directly from the host side of the shared folder. Fallback (arbitrary path): cp inside the container into the shared folder, then read the host side. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.exec( handle: nemo_gym.sandbox.providers.base.SandboxHandle, 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 ) -> nemo_gym.sandbox.providers.base.SandboxExecResult ``` async Run a command inside the container via `enroot exec <pid>`. Maps the neutral `user` parameter onto enroot: * None -> run as the default (launching) user, or root if the container was started with remap\_root. * "root" / 0 -> run directly (requires create.remap\_root=true to be root inside the container). * other user/uid -> wrap in `su` to switch to that user (requires root inside the container). `stdin`, when given, is piped to the command's standard input. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.status( handle: nemo_gym.sandbox.providers.base.SandboxHandle ) -> nemo_gym.sandbox.providers.base.SandboxStatus ``` async Return the container's lifecycle status via `enroot list -f`. Liveness is keyed off the PID column, not name presence: enroot lists created-but-not-running rootfs with an empty PID. Running -> RUNNING; present without a PID, or absent -> STOPPED; timeout/parse error -> UNKNOWN. ```python nemo_gym.sandbox.providers.enroot.EnrootProvider.upload_file( handle: nemo_gym.sandbox.providers.base.SandboxHandle, source_path: pathlib.Path, target_path: str ) -> None ``` async Upload one host file into the sandbox. Fast path (target under the bind mount): write directly to the host side of the shared folder. Fallback (arbitrary path): stage into the shared folder, then cp inside the container. ## Docs - [nemo_gym.sandbox.providers.enroot.provider](https://docs.nvidia.com/nemo/gym/v0.2.1/nemo-gym/nemo_gym/sandbox/providers/enroot/provider.md)