Python Host API¶
The isola package exposes an async host-side API for compiling sandbox
templates and running code inside isolated runtimes.
This page documents the embedding SDK that runs in your host process. For the Python modules available inside sandboxed guest code, see Python Guest API. For the JavaScript globals available inside JS guests, see JavaScript Guest API.
Install¶
Lifecycle¶
The normal flow is:
await build_template(...)async with template.create(...) as sandbox:await sandbox.load_script(...)await sandbox.run(...)or iteratesandbox.run_stream(...)
import asyncio
from isola import build_template
async def main() -> None:
template = await build_template("python")
async with template.create() as sandbox:
await sandbox.load_script("def hello(name):\n return f'hello {name}'")
result = await sandbox.run("hello", "world")
print(result)
asyncio.run(main())
Runtime Resolution¶
Most users can skip this section and let build_template(...) handle runtime
downloads automatically.
Use resolve_runtime(runtime, *, version=None) to fetch the runtime bundle
config directly:
When runtime_path is omitted from build_template(...), the SDK resolves the
runtime automatically, downloads the matching release asset on first use,
verifies its digest, and caches it under ~/.cache/isola/runtimes/.
Supported runtime names:
"python""js"
Core Types¶
build_template(...)¶
Builds and returns a reusable sandbox template using an internal SandboxContext.
from isola import build_template
template = await build_template(runtime, *, version=None, **template_config)
build_template(...) accepts:
runtime:"python"or"js"version: optional release tag to resolve when auto-downloading a runtimeruntime_path: directory or path used to initialize the runtime bundleruntime_lib_dir: runtime library directory, required for Python runtimes that are provided manuallycache_dir: template cache directorymax_memory: template memory limit in bytesprelude: code injected before user scriptsmounts:list[MountConfig]env:dict[str, str]
SandboxContext¶
Advanced API for explicitly owning a template compilation context. It exposes
the same template-building behavior as the top-level helper, plus explicit
close() and async context-manager lifecycle control.
SandboxContext supports async with ... cleanup and explicit close().
SandboxTemplate¶
Instantiates sandboxes from a compiled template.
Use create(...) for the normal case where the sandbox lifetime is scoped to an
async context manager. If you need the raw Sandbox object before entering it,
use await template.instantiate(**sandbox_config) instead.
create(...) and instantiate(...) accept:
max_memory: per-sandbox memory limit in bytesmounts:list[MountConfig]env:dict[str, str]hostcalls:dict[str, async callable]used for guestsandbox.asyncio.hostcall(...)http:Noneto disable guest HTTP,Trueto use the built-inhttpxbridge, or an async callable for a custom outbound HTTP policyhttp_handler: legacy alias forhttp
Sandbox¶
Runs guest code inside an instantiated sandbox.
async with sandbox:
await sandbox.load_script(code)
result = await sandbox.run(name, *args, **kwargs)
Public methods:
await load_script(code)await run(name, *args, **kwargs) -> JsonValue | Nonerun_stream(name, *args, **kwargs) -> AsyncIterator[Event]close()await aclose()
Use async with sandbox: before executing code. Entering the async context starts the sandbox.
Arguments and Streaming¶
run(...) and run_stream(...) accept positional JSON values directly:
Keyword arguments become named guest arguments:
Use Arg(value, name="...") to pass a named argument:
Use StreamArg for JSON streams passed into guest code:
from isola import StreamArg
stream = StreamArg.from_iterable([1, 2, 3])
result = await sandbox.run("consume", stream)
JSON lists are passed as normal values:
Available constructors:
StreamArg.from_iterable(values, *, name=None, capacity=1024)StreamArg.from_async_iterable(values, *, name=None, capacity=1024)
Hostcalls¶
Register host callbacks when the sandbox is created. Each handler receives the decoded payload for its call name and must return a serializable value.
Guest sandboxes invoke these handlers with their runtime-specific guest APIs:
- Python guests use
sandbox.asyncio.hostcall(...) - JS guests use top-level
await hostcall(...)
Those guest-side call sites are documented separately in Python Guest API and JavaScript Guest API.
from sandbox.asyncio import hostcall
async def lookup_user(payload: dict[str, object]) -> object:
user_id = int(payload["user_id"])
return {"user_id": user_id, "name": f"user-{user_id}"}
async with template.create(hostcalls={"lookup_user": lookup_user}) as sandbox:
await sandbox.load_script(
"from sandbox.asyncio import hostcall\n"
"\n"
"async def lookup_user(user_id):\n"
" return await hostcall('lookup_user', {'user_id': user_id})\n"
)
result = await sandbox.run("lookup_user", 7)
Configure hostcalls and HTTP behavior when the sandbox is created.
Events and Results¶
run(...) returns the final value directly:
run_stream(...) yields typed Event objects for fine-grained control:
async for event in sandbox.run_stream("compute"):
match event:
case ResultEvent(data=value):
print("intermediate:", value)
case EndEvent(data=value):
print("final:", value)
case StdoutEvent(data=line):
print("stdout:", line)
case StderrEvent(data=line):
print("stderr:", line)
case ErrorEvent(data=msg):
print("error:", msg)
case LogEvent(data=msg):
print("log:", msg)
Event is a union of: ResultEvent, EndEvent, StdoutEvent, StderrEvent, ErrorEvent, LogEvent. Each carries a typed data field (JsonValue for result/end, str for the rest).
Filesystem and Environment¶
Use MountConfig to mount host paths into the guest:
from isola import MountConfig
mount = MountConfig(
host="./data",
guest="/workspace",
dir_perms="read",
file_perms="read",
)
dir_perms and file_perms accept:
"read""write""read-write"
Environment variables can be supplied in both template and sandbox config via env={"KEY": "value"}.
HTTP Bridge¶
Outbound guest HTTP is disabled unless you pass http= when creating
the sandbox. Use http=True to enable the built-in httpx pass-through
bridge, or provide your own async handler to enforce a custom HTTP policy.
The guest-side request APIs used inside the sandbox are documented in Python Guest API and JavaScript Guest API.
from collections.abc import AsyncIterator
from isola import HttpRequest, HttpResponse
async def body() -> AsyncIterator[bytes]:
yield b"hello "
yield b"world"
async def http(request: HttpRequest) -> HttpResponse:
return HttpResponse(
status=200,
headers={"content-type": "text/plain"},
body=body(),
)
async with template.create(http=http) as sandbox:
...
Request and response models:
HttpRequest(method, url, headers, body)HttpResponse(status, headers=None, body=None)
HttpResponse.body may be:
bytesAsyncIterable[bytes]None
Errors¶
The package exports these exception types:
IsolaErrorInvalidArgumentErrorInternalErrorStreamFullErrorStreamClosedError