API Reference
Sandbox Class​
The main class for creating and managing sandboxes.
Class Methods​
Sandbox.create()​
Creates a new sandbox instance.
@classmethod
def create(
variant: str = "datalayer",
timeout: float | None = None,
environment: str | None = None,
gpu: str | None = None,
cpu: float | None = None,
memory: int | None = None,
env: dict[str, str] | None = None,
tags: dict[str, str] | None = None,
name: str | None = None,
network_policy: str | None = None,
allowed_hosts: list[str] | None = None,
examples: list[tuple[str, str]] | None = None,
config: SandboxConfig | None = None,
**kwargs,
) -> Sandbox
Parameters:
| Parameter | Type | Description |
|---|---|---|
variant | str | Sandbox type: "cloudflare", "coreweave", "datalayer", "daytona", "docker", "e2b", "eval", "google-colab", "jupyter-server", "kaggle", "modal", or "monty". Defaults to "datalayer". |
timeout | float | Execution timeout in seconds |
environment | str | Runtime environment name |
gpu | str | GPU type (e.g., "T4", "A100", "H100") |
cpu | float | Number of CPU cores |
memory | int | Memory in MB |
env | dict | Environment variables |
tags | dict | Metadata tags |
name | str | Sandbox name |
network_policy | str | Network access policy: "inherit", "none", "allowlist", "all" |
allowed_hosts | list[str] | Hosts reachable when the policy is "allowlist" |
examples | list[tuple[str, str]] | (title, code) pairs the REPL lists under :examples |
config | SandboxConfig | Full configuration object |
**kwargs | dict | Variant-specific arguments handed to the sandbox constructor — template= (e2b), container_image= (coreweave), api_url= (cloudflare), snapshot_name= (datalayer), and the rest documented on each variant's page |
Sandbox.from_id()​
Reconnects to an existing sandbox.
@classmethod
def from_id(sandbox_id: str, **kwargs) -> Sandbox
Datalayer only — it reconnects to a Datalayer runtime whatever else is
running. **kwargs are that variant's: token=, run_url=.
Sandbox.list()​
Lists running sandboxes, as sandbox objects.
@classmethod
def list(tags: dict[str, str] | None = None, **kwargs) -> Iterator[Sandbox]
Datalayer only. For any other variant, ask its manager:
get_manager(variant).list(), which answers with list[SandboxInfo] — see
the CLI page.
Sandbox.list_environments()​
Lists available environments for a sandbox variant.
@classmethod
def list_environments(
variant: str = "datalayer",
**kwargs,
) -> list[SandboxEnvironment]
Parameters:
| Parameter | Type | Description |
|---|---|---|
variant | str | Sandbox type: "cloudflare", "coreweave", "datalayer", "daytona", "docker", "e2b", "eval", "google-colab", "jupyter-server", "kaggle", "modal", or "monty" |
**kwargs | dict | Variant-specific arguments (e.g., credentials, run URL) |
Legacy local-eval, local-docker, and local-jupyter variant names are not supported.
Instance Methods​
run_code()​
Executes Python code in the sandbox.
def run_code(
code: str,
language: str = "python",
context: Context | None = None,
on_stdout: Callable[[OutputMessage], None] | None = None,
on_stderr: Callable[[OutputMessage], None] | None = None,
on_result: Callable[[Result], None] | None = None,
on_error: Callable[[CodeError], None] | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> ExecutionResult
run_code_streaming()​
Executes Python code and yields output/result/error events as they arrive.
def run_code_streaming(
code: str,
language: str = "python",
context: Context | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> Iterator[OutputMessage | Result | CodeError]
run_code_streaming_async()​
Async version of run_code_streaming().
async def run_code_streaming_async(
code: str,
language: str = "python",
context: Context | None = None,
envs: dict[str, str] | None = None,
timeout: float | None = None,
) -> AsyncIterator[OutputMessage | Result | CodeError]
start()​
Starts the sandbox.
def start() -> None
stop()​
Stops the sandbox gracefully.
def stop() -> None
terminate() / kill()​
Datalayer only; both are aliases for stop(). Every other variant is stopped
with stop() — or by leaving its with block.
def terminate() -> None
def kill() -> None
set_timeout()​
Extends the life of a running sandbox. Only two variants offer it, and the argument is not spelled the same in both.
def set_timeout(timeout_seconds: float) -> None # datalayer
def set_timeout(seconds: float) -> None # e2b
Properties​
| Property | Type | Description |
|---|---|---|
sandbox_id | str | None | Unique sandbox identifier — None until start() has run |
files | SandboxFilesystem | Filesystem interface |
commands | SandboxCommands | Command execution interface |
tags | dict[str, str] | Metadata tags |
CodeSandboxClient​
CodeSandboxClient is a high-level, variant-agnostic facade over a sandbox.
It normalizes one-shot outcomes and streaming events across all variants.
Methods​
execute_code()​
def execute_code(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> CodeExecutionOutcome
execute_code_async()​
async def execute_code_async(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> CodeExecutionOutcome
execute_code_streaming()​
def execute_code_streaming(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> Iterator[OutputMessage | Result | CodeError]
execute_code_streaming_async()​
async def execute_code_streaming_async(
code: str,
language: str = "python",
timeout: float | None = None,
envs: dict[str, str] | None = None,
) -> AsyncIterator[OutputMessage | Result | CodeError]
Compatibility-free execution facade​
Consumers that need a Jupyter-shaped reply can stay on the sandbox client without accessing a variant's underlying kernel implementation:
reply = client.execute(code, timeout=60)
reply = client.execute_interactive(code, output_hook=handle_output)
The client also exposes variant-neutral lifecycle and variable operations:
client.start()
client.interrupt()
client.restart()
client.set_variable("name", value)
client.set_variables({"one": 1, "two": 2})
value = client.get_variable("name")
client.stop()
Properties​
| Property | Description |
|---|---|
id | Active backend identifier, including a remote kernel ID when applicable |
variant | Configured sandbox variant |
config | Variant-neutral sandbox configuration |
info | Runtime sandbox information after startup |
kernel_info | Language metadata without exposing the underlying kernel client |
is_started | Whether the sandbox has been started |
Outcome model​
CodeExecutionOutcome includes normalized fields such as:
successexecution_okstdoutstderrresultsexecution_errorcode_errorexit_codeinterrupted
Contents attachments​
The Datalayer Contents service writes down what a sandbox is to be given as a
ContentManifest; the sandbox honours it and answers with one
PreparedAttachment per attachment, whichever provider is underneath. The
models are importable from code_sandboxes.
Models​
| Model | What it is |
|---|---|
ContentManifest | contract_version, sandbox_uid, sandbox_provider, generated_at, attachments, and the sandbox's own short-lived credentials to Contents: contents_url, token |
ContentAttachmentSpec | One attachment: uid, source_uid, revision_uid, mode (ro/rw), mount_path, delivery (mount, local-bridge, materialize, client, environment), required, access_mode, provider_resource_id, filesystem_primitives, materialize |
MaterializeEntry | One file to fetch: source_url (signed), path (absolute or under mount_path), sha256, size |
PreparedAttachment | uid, status (ready, degraded, failed), mount_path, provider_resource_id, capabilities, detail, error_code |
ContentCapabilities | provider, mount, bucket_mount, materialize, client, local_bridge_mount (a LocalBridgeCapability), filesystem_primitives |
ContentAttachmentError | Raised by attach() for a required attachment that is not ready: uid, code, attachments |
A degraded attachment is an optional one that could not be honoured; a
failed one is required. The error_code is one of DELIVERY_UNSUPPORTED,
MOUNT_NEEDS_RESTART, MOUNT_MISSING, MOUNT_PATH_MISSING,
CREDENTIAL_DELIVERY_UNSUPPORTED, CHECKSUM_MISMATCH, FETCH_FAILED.
CodeSandboxClient methods​
def content_capabilities() -> ContentCapabilities
def attach(manifest: ContentManifest) -> list[PreparedAttachment]
def prepare_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def reconcile_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def attachment_status(uid: str) -> PreparedAttachment | None
def detach(uid: str) -> None
contents_location -> ManifestLocation | None
prepare_contents configures a sandbox that has not started (the environment,
and the mounts a provider only makes at creation), starts it, writes the
manifest inside, then prepares every attachment. attach is the same and
raises ContentAttachmentError when a required attachment is not ready —
leaving the sandbox running. reconcile_contents re-checks and repairs after
a restart: a mount that is there is ready, a materialized file with the right
digest is not fetched again, and no mount is ever made twice. Every method is
safe to call again.
Inside the sandbox the manifest is written to /etc/datalayer/contents.json,
or ~/.datalayer/contents.json when /etc cannot be written, and the
environment is set — at creation where the provider takes it (Daytona, E2B,
Modal) and in the kernel everywhere: DATALAYER_CONTENTS_MANIFEST,
DATALAYER_CONTENTS_URL, DATALAYER_CONTENTS_TOKEN,
DATALAYER_CONTENTS_TOKEN_FILE. The token never goes into the JSON; it is
written to contents.token with mode 0600.
Sandbox hooks​
def content_capabilities() -> ContentCapabilities
def configure_contents(manifest: ContentManifest) -> None # before creation
def prepare_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def reconcile_contents(manifest: ContentManifest) -> list[PreparedAttachment]
def attachment_status(uid: str) -> PreparedAttachment | None
def remove_attachment(uid: str) -> None
None of them is abstract. The base answers client only; an adapter overrides
content_capabilities, configure_contents and the small _prepare_mount /
_prepare_local_bridge / _forget_mount hooks rather than the walk over the
manifest.
What each provider honours​
| Provider | Volume mount (provider_resource_id) | Shared filesystem | Bucket mount | Local bridge | Materialize |
|---|---|---|---|---|---|
datalayer | made by the Operator before the pod starts; checked for | same | same | Clouder CSI | fetched inside the sandbox |
daytona | VolumeMount at creation; MOUNT_NEEDS_RESTART on a running sandbox | DELIVERY_UNSUPPORTED | DELIVERY_UNSUPPORTED | no | fetched inside the sandbox |
e2b | volume_mounts at creation; MOUNT_NEEDS_RESTART on a running sandbox | DELIVERY_UNSUPPORTED | DELIVERY_UNSUPPORTED | no | fetched inside the sandbox |
modal | volumes={path: Volume.from_name(id)} at creation; MOUNT_NEEDS_RESTART on a running sandbox | DELIVERY_UNSUPPORTED | CREDENTIAL_DELIVERY_UNSUPPORTED — a CloudBucketMount needs the bucket credential handed to Modal | no | fetched inside the sandbox |
| others | DELIVERY_UNSUPPORTED | same | same | no | DELIVERY_UNSUPPORTED |
Materialized files are fetched by the sandbox itself from the signed URLs,
so the bytes never pass through the host; each is written beside its
destination and moved into place once its digest checks out. detach removes
materialized files and forgets a mount request; it never deletes a volume.
Kaggle and Colab Clients​
Code Sandboxes owns the provider-specific clients used by its managed notebook
variants. They are exported from code_sandboxes:
from code_sandboxes import (
GoogleColabKernelClient,
KaggleExecutionResult,
KaggleKernelClient,
KaggleKernelExecutor,
parse_google_colab_channels_url,
parse_kaggle_channels_url,
)
KaggleKernelClientconnects to an interactive Kaggle notebook kernel.KaggleKernelExecutorsubmits and monitors Kaggle batch notebook jobs.KaggleExecutionResultnormalizes batch output into a Jupyter-like reply.GoogleColabKernelClientconnects to an already-running Google Colab kernel.- The parser helpers extract connection details from browser channels URLs.
See Kaggle and Google Colab for authentication and complete examples.
SandboxFilesystem​
File operations interface.
Methods​
read()​
def read(path: str) -> str
Reads file contents as a string.
read_bytes()​
def read_bytes(path: str) -> bytes
Reads file contents as bytes.
write()​
def write(path: str, content: str, make_dirs: bool = True) -> None
Writes text to a file, creating parent directories unless told not to.
write_bytes()​
def write_bytes(path: str, content: bytes, make_dirs: bool = True) -> None
Writes bytes to a file. write() handles text only.
list()​
def list(path: str = "/") -> list[FileInfo]
Lists directory contents.
mkdir()​
def mkdir(path: str, parents: bool = True) -> None
Creates a directory.
rm()​
def rm(path: str, recursive: bool = False) -> None
Removes a file or directory.
exists() / is_file() / is_dir()​
def exists(path: str) -> bool
def is_file(path: str) -> bool
def is_dir(path: str) -> bool
Asks about a path without reading it.
copy() / move()​
def copy(src: str, dst: str) -> None
def move(src: str, dst: str) -> None
get_info()​
def get_info(path: str) -> FileInfo
One entry's metadata, without listing its directory.
upload()​
def upload(local_path: str, remote_path: str) -> None
Uploads a local file to the sandbox.
download()​
def download(remote_path: str, local_path: str) -> None
Downloads a file from the sandbox.
FileInfo​
What list() and get_info() answer with.
@dataclass
class FileInfo:
name: str
path: str
type: FileType = FileType.FILE # FILE, DIRECTORY, SYMLINK
size: int = 0 # bytes, for files
modified: float = 0.0 # Unix timestamp
permissions: str = ""
# Convenience properties
@property
def is_file(self) -> bool: ...
@property
def is_directory(self) -> bool: ...
SandboxCommands​
Command execution interface.
Methods​
run()​
def run(
command: str,
cwd: str | None = None,
env: dict[str, str] | None = None,
timeout: float | None = None,
shell: bool = True,
) -> CommandResult
Runs a command and waits for completion.
exec()​
def exec(*args: str, **kwargs) -> ProcessHandle
Executes a command with streaming output.
spawn()​
def spawn(command: str, **kwargs) -> ProcessHandle
Starts a background process.
CommandResult​
@dataclass
class CommandResult:
exit_code: int = 0
stdout: str = ""
stderr: str = ""
duration: float = 0.0
@property
def success(self) -> bool: ... # exit_code == 0
ProcessHandle​
A process still running. stdout and stderr are iterators that yield as
output arrives.
@dataclass
class ProcessHandle:
command: str
pid: int | None = None
@property
def stdout(self) -> Iterator[str]: ...
@property
def stderr(self) -> Iterator[str]: ...
@property
def returncode(self) -> int | None: ...
def read_stdout(self) -> str: ...
def read_stderr(self) -> str: ...
def wait(self, timeout: float | None = None) -> int: ...
def poll(self) -> int | None: ...
def terminate(self) -> None: ...
def kill(self) -> None: ...
Data Models (Pydantic)​
ExecutionResult​
Complete result of a code execution with detailed status information.
class ExecutionResult(BaseModel):
# Results and logs
results: list[Result]
logs: Logs
# Execution-level (infrastructure) status
execution_ok: bool = True # Did sandbox infrastructure work?
execution_error: str | None = None # Infrastructure failure details
# Code-level (user code) status
code_error: CodeError | None = None # Python exception info
# Process exit status (sys.exit)
exit_code: int | None = None # Exit code if code calls sys.exit()
# Metadata
execution_count: int = 0
context_id: str | None = None
started_at: float | None = None
completed_at: float | None = None
interrupted: bool = False
# Properties
@property
def success(self) -> bool:
"""True if execution_ok and no code_error and not interrupted and exit_code is 0 or None"""
@property
def duration(self) -> float | None:
"""Execution duration in seconds"""
@property
def text(self) -> str | None:
"""Main text result"""
@property
def stdout(self) -> str:
"""All stdout as single string"""
@property
def stderr(self) -> str:
"""All stderr as single string"""
Usage:
result = sandbox.run_code("x = 1 + 1; print(x)")
# Check infrastructure success
if not result.execution_ok:
print(f"Sandbox failed: {result.execution_error}")
# Check explicit process exit
elif result.exit_code not in (None, 0):
print(f"Process exited with code: {result.exit_code}")
# Check code-level error
elif result.code_error:
print(f"Python error: {result.code_error.name}: {result.code_error.value}")
# Success!
else:
print(f"Result: {result.text}")
print(f"Duration: {result.duration:.2f}s")
# Or use convenience property
if result.success:
print("Everything worked!")
CodeError​
Error information when Python code raises an exception.
class CodeError(BaseModel):
name: str # Error class name (e.g., "ValueError")
value: str # Error message
traceback: str = "" # Full traceback
SandboxEnvironment​
class SandboxEnvironment(BaseModel):
name: str
title: str
language: str = "python"
owner: str = "local"
visibility: str = "local"
burning_rate: float = 0.0
metadata: dict[str, Any] | None = None
cpu: str | None = None
memory: str | None = None
gpu: str | None = None
gpu_count: int | None = None
gpu_memory: int | None = None
Result​
A single execution result with multiple representations.
class Result(BaseModel):
data: dict[str, Any] # MIME type to content mapping
is_main_result: bool = False
extra: dict[str, Any] # Additional metadata
# Convenience properties
@property
def text(self) -> str | None: ... # text/plain
@property
def html(self) -> str | None: ... # text/html
@property
def json(self) -> Any: ... # application/json
@property
def png(self) -> str | None: ... # image/png (base64)
@property
def jpeg(self) -> str | None: ... # image/jpeg (base64)
@property
def svg(self) -> str | None: ... # image/svg+xml
@property
def markdown(self) -> str | None: ... # text/markdown
OutputMessage​
class OutputMessage(BaseModel):
line: str
timestamp: float = 0.0
error: bool = False # True for stderr
Logs​
class Logs(BaseModel):
stdout: list[OutputMessage]
stderr: list[OutputMessage]
@property
def stdout_text(self) -> str: ... # All stdout as string
@property
def stderr_text(self) -> str: ... # All stderr as string
Context​
class Context(BaseModel):
id: str
language: str = "python"
cwd: str | None = None
env: dict[str, str]
SandboxConfig​
class SandboxConfig(BaseModel):
timeout: float = 30.0
memory_limit: int | None = None
cpu_limit: float | None = None
environment: str = "ai-agents-env"
working_dir: str | None = None
env_vars: dict[str, str]
gpu: str | None = None
name: str | None = None
network_policy: str = "inherit" # "inherit", "none", "allowlist", "all"
allowed_hosts: list[str]
idle_timeout: float | None = None
max_lifetime: float = 86400.0 # 24 hours
examples: list[tuple[str, str]] # (title, code) pairs for the REPL
SandboxInfo​
class SandboxInfo(BaseModel):
id: str
variant: str
status: SandboxStatus = SandboxStatus.RUNNING
created_at: float = 0.0
end_at: float | None = None
config: SandboxConfig | None = None
name: str | None = None
metadata: dict[str, Any]
resources: ResourceConfig | None = None
@property
def remaining_time(self) -> float | None: ... # Seconds until termination
SnapshotInfo​
What create_snapshot() and list_snapshots() answer with — datalayer only.
class SnapshotInfo(BaseModel):
id: str
name: str
sandbox_id: str
created_at: float = 0.0
size: int = 0
description: str = ""
JupyterServerOptions​
What prepare_jupyter_server() is asked for — see
Jupyter over Provider Ingress.
class JupyterServerOptions(BaseModel):
port: int = 8888
token: str | None = None # generated when omitted
install_if_missing: bool = True
install_timeout: float = 180.0
JupyterServerEndpoint​
What it answers with. headers authenticate the provider ingress and query
authenticates Jupyter itself; both are secrets, and both are kept out of the
repr.
class JupyterServerEndpoint(BaseModel):
port: int
http_url: str
websocket_url: str
headers: dict[str, str]
query: dict[str, str]
ResourceConfig​
class ResourceConfig(BaseModel):
cpu: float | None = None # CPU cores
memory: int | None = None # Memory in MB
gpu: str | None = None # GPU type ("T4", "A100", etc.)
gpu_count: int = 1 # Number of GPUs
disk: int | None = None # Disk in GB
Management API​
The verbs the CLI runs on, from Python. A manager exists per variant, and states what it can do rather than failing late.
from code_sandboxes import get_manager, manageable_variants
manageable_variants() # the variants a manager exists for
manager = get_manager("modal")
manager.capabilities # frozenset of the verbs this variant answers
manager.list() # list[SandboxInfo]
manager.get(sandbox_id) # SandboxInfo | None
manager.create(**kwargs) # SandboxInfo
manager.update(sandbox_id, **changes)
manager.delete(sandbox_id) # bool
A verb outside capabilities raises SandboxManagementError. See
the CLI page for what each variant
answers.
Provider Registry​
What a variant needs before it can run anything — credentials, and the extra that installs it. It reports what is present; it starts nothing and reads no secret's value.
from code_sandboxes import PROVIDERS, available_providers, get_provider
PROVIDERS # every provider declared
available_providers() # those whose credentials are on this machine
get_provider("daytona") # one, or None
Exceptions​
| Exception | Description |
|---|---|
SandboxError | Base exception for all sandbox errors |
SandboxTimeoutError | Execution timed out |
SandboxExecutionError | Code execution failed |
SandboxNotStartedError | The sandbox was used before start() |
SandboxConnectionError | Connection to sandbox failed |
SandboxConfigurationError | The variant cannot be used as configured — a missing extra, or a setting it refuses |
ContextNotFoundError | No such execution context |
VariableNotFoundError | No such variable in the context |
SandboxSnapshotError | Snapshot operation failed |
SandboxResourceError | Resource limit exceeded |
SandboxAuthenticationError | Authentication failed |
SandboxQuotaExceededError | Usage quota exceeded |