Back to Microsandbox

Execution

docs/sdk/python/execution.mdx

0.5.1022.0 KB
Original Source

Run commands inside a running sandbox: collect output in one shot, stream events as they arrive, or bridge your terminal to an interactive PTY. These methods live on a running Sandbox. See Commands for usage examples.

<div className="msb-glance"> <p className="msb-gl"><span className="msb-dot instance"></span>Run &amp; collect<span className="msb-ct">2</span></p> <a className="msb-row" href="#sandbox-exec"><span className="msb-rn">sandbox.exec()</span><span className="msb-rg">run, buffer output</span></a> <a className="msb-row" href="#sandbox-shell"><span className="msb-rn">sandbox.shell()</span><span className="msb-rg">run through the shell</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Stream<span className="msb-ct">2</span></p> <a className="msb-row" href="#sandbox-exec_stream"><span className="msb-rn">sandbox.exec_stream()</span><span className="msb-rg">stream events live</span></a> <a className="msb-row" href="#sandbox-shell_stream"><span className="msb-rn">sandbox.shell_stream()</span><span className="msb-rg">stream through the shell</span></a> <p className="msb-gl"><span className="msb-dot instance"></span>Attach<span className="msb-ct">2</span></p> <a className="msb-row" href="#sandbox-attach"><span className="msb-rn">sandbox.attach()</span><span className="msb-rg">interactive PTY bridge</span></a> <a className="msb-row" href="#sandbox-attach_shell"><span className="msb-rn">sandbox.attach_shell()</span><span className="msb-rg">attach the default shell</span></a> <p className="msb-gl"><span className="msb-dot type"></span>Types</p> <div className="msb-chiprow"> <a className="msb-typepill" href="#execoutput">ExecOutput</a> <a className="msb-typepill" href="#exechandle">ExecHandle</a> <a className="msb-typepill" href="#execsink">ExecSink</a> <a className="msb-typepill" href="#execevent">ExecEvent</a> <a className="msb-typepill" href="#exitstatus">ExitStatus</a> <a className="msb-typepill" href="#stdin">Stdin</a> <a className="msb-typepill" href="#rlimit">Rlimit</a> <a className="msb-typepill" href="#rlimitresource">RlimitResource</a> </div> </div> <p className="msb-label" id="typical-flow">Typical flow</p>
python
import sys

from microsandbox import Sandbox

sandbox = await Sandbox.create("api", image="python")

# 1. one-shot
out = await sandbox.exec("python3", ["-c", "print(1 + 1)"])
print(out.stdout_text)  # "2\n"

# 2. stream
handle = await sandbox.exec_stream("tail", ["-f", "/var/log/app.log"])
async for event in handle:
    if event.event_type == "stdout":
        sys.stdout.buffer.write(event.data)
    elif event.event_type == "exited":
        break

await sandbox.stop()

Run and collect


<span className="msb-recv">sandbox.</span><span className="msb-hn">exec()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def exec(
    cmd: str,
    args: list[str] | Mapping[str, Any] | None = None,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    timeout: float | None = None,
    stdin: Stdin | bytes | str | None = None,
    tty: bool = False,
    rlimits: list[Rlimit] | None = None,
) -> ExecOutput

Run a command inside the sandbox and wait for it to complete, buffering all stdout and stderr into memory. The keyword-only options apply to this call alone and don't change the sandbox's defaults. For long-running processes or large output, use exec_stream() instead. Raises ExecTimeoutError if timeout elapses and ExecFailedError if the process can't be spawned.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Command to execute (e.g. <code>"python3"</code>, <code>"/usr/bin/node"</code>).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">list[str] | Mapping[str, Any] | None</span></div> <div className="msb-param-desc">Command arguments. A mapping is accepted as a shorthand for the keyword-only options below.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Working directory for this command.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>user</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Guest user to run as.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>env</code><span className="msb-type">Mapping[str, str] | None</span></div> <div className="msb-param-desc">Environment variables, merged on top of the sandbox defaults.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>timeout</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Seconds before the process is killed.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>stdin</code><a className="msb-type" href="#stdin">Stdin | bytes | str | None</a></div> <div className="msb-param-desc">Stdin mode. Raw <code>bytes</code> / <code>str</code> are sent inline; default is <code>/dev/null</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>tty</code><span className="msb-type">bool</span></div> <div className="msb-param-desc">Allocate a pseudo-terminal, merging stdout and stderr.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>rlimits</code><a className="msb-type" href="#rlimit">list[Rlimit] | None</a></div> <div className="msb-param-desc">POSIX resource limits applied to the process.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div> <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div> </div> </div> <Accordion title="Example">
python
out = await sandbox.exec(
    "python3",
    ["script.py"],
    cwd="/app",
    env={"PYTHONPATH": "/app/lib"},
    timeout=30.0,
)
print(out.stdout_text)
print(out.exit_code)  # 0
</Accordion>

<span className="msb-recv">sandbox.</span><span className="msb-hn">shell()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def shell(
    script: str,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    timeout: float | None = None,
    stdin: Stdin | bytes | str | None = None,
    tty: bool = False,
    rlimits: list[Rlimit] | None = None,
) -> ExecOutput

Run a command through the sandbox's configured shell (defaults to /bin/sh). Shell syntax like pipes, redirects, and && chains works. Accepts the same keyword-only options as exec().

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>script</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Shell command string (e.g. <code>"ls -la /app &amp;&amp; echo done"</code>).</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd, user, env, timeout, stdin, tty, rlimits</code></div> <div className="msb-param-desc">Same per-call options as <a className="msb-type" href="#sandbox-exec">exec()</a>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#execoutput">ExecOutput</a></div> <div className="msb-param-desc">Collected stdout, stderr, and exit status.</div> </div> </div> <Accordion title="Example">
python
out = await sandbox.shell("ls -la /app && echo done")
print(out.stdout_text)
</Accordion>

Stream


<span className="msb-recv">sandbox.</span><span className="msb-hn">exec_stream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def exec_stream(
    cmd: str,
    args: list[str] | Mapping[str, Any] | None = None,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    timeout: float | None = None,
    stdin: Stdin | bytes | str | None = None,
    tty: bool = False,
    rlimits: list[Rlimit] | None = None,
) -> ExecHandle

Run a command with streaming output. Returns an ExecHandle that emits stdout, stderr, and exit events as they happen rather than buffering everything. Takes the same per-call options as exec(). Pass stdin=Stdin.pipe() to write to the process while it runs via take_stdin().

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Command to execute.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">list[str] | Mapping[str, Any] | None</span></div> <div className="msb-param-desc">Command arguments.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd, user, env, timeout, stdin, tty, rlimits</code></div> <div className="msb-param-desc">Same per-call options as <a className="msb-type" href="#sandbox-exec">exec()</a>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div> <div className="msb-param-desc">Streaming handle for receiving events and controlling the process.</div> </div> </div> <Accordion title="Example">
python
import sys

handle = await sandbox.exec_stream("tail", ["-f", "/var/log/app.log"])
async for event in handle:
    if event.event_type == "stdout":
        sys.stdout.buffer.write(event.data)
    elif event.event_type == "exited":
        break
</Accordion>

<span className="msb-recv">sandbox.</span><span className="msb-hn">shell_stream()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def shell_stream(
    script: str,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    timeout: float | None = None,
    stdin: Stdin | bytes | str | None = None,
    tty: bool = False,
    rlimits: list[Rlimit] | None = None,
) -> ExecHandle

Streaming variant of shell(): runs script through the configured shell but returns an ExecHandle instead of buffering output.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>script</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Shell command string.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd, user, env, timeout, stdin, tty, rlimits</code></div> <div className="msb-param-desc">Same per-call options as <a className="msb-type" href="#sandbox-exec">exec()</a>.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#exechandle">ExecHandle</a></div> <div className="msb-param-desc">Streaming handle.</div> </div> </div> <Accordion title="Example">
python
import sys

handle = await sandbox.shell_stream("for i in 1 2 3; do echo $i; sleep 1; done")
async for event in handle:
    if event.event_type == "stdout":
        sys.stdout.buffer.write(event.data)
</Accordion>

Attach


<span className="msb-recv">sandbox.</span><span className="msb-hn">attach()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def attach(
    cmd: str,
    args: list[str] | None = None,
    *,
    cwd: str | None = None,
    user: str | None = None,
    env: Mapping[str, str] | None = None,
    detach_keys: str | None = None,
) -> int

Bridge your terminal directly to a process inside the sandbox for a fully interactive PTY session. Press the configured detach key sequence (default Ctrl+]) to disconnect without stopping the process. Returns the process exit code.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>cmd</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Command to run.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>args</code><span className="msb-type">list[str] | None</span></div> <div className="msb-param-desc">Command arguments.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>cwd</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Working directory.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>user</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Guest user to run as.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>env</code><span className="msb-type">Mapping[str, str] | None</span></div> <div className="msb-param-desc">Environment variables for the session.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>detach_keys</code><span className="msb-type">str | None</span></div> <div className="msb-param-desc">Detach key sequence (e.g. <code>"ctrl-]"</code> or <code>"ctrl-p,ctrl-q"</code>).</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">int</span></div> <div className="msb-param-desc">Exit code of the process.</div> </div> </div> <Accordion title="Example">
python
code = await sandbox.attach("bash", cwd="/app", env={"EDITOR": "vim"})
</Accordion>

<span className="msb-recv">sandbox.</span><span className="msb-hn">attach_shell()</span>

<div className="msb-tags"><span className="msb-tag is-instance">instance</span><span className="msb-tag is-async">async</span></div>
python
async def attach_shell() -> int

Bridge your terminal to the sandbox's default shell in a fully interactive PTY session. Returns the shell's exit code.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">int</span></div> <div className="msb-param-desc">Exit code of the shell process.</div> </div> </div> <Accordion title="Example">
python
code = await sandbox.attach_shell()
</Accordion>

Types

ExecOutput

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sandbox-exec">exec()</a> · <a href="#sandbox-shell">shell()</a> · <a href="#exechandle">ExecHandle.collect()</a></p>

The result of a completed command execution: collected output plus exit status. All members are properties.

PropertyTypeDescription
exit_codeintProcess exit code
successboolTrue when exit_code == 0
stdout_textstrCollected stdout decoded as UTF-8. Raises on invalid encoding
stderr_textstrCollected stderr decoded as UTF-8. Raises on invalid encoding
stdout_bytesbytesRaw stdout bytes
stderr_bytesbytesRaw stderr bytes

ExecHandle

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#sandbox-exec_stream">exec_stream()</a> · <a href="#sandbox-shell_stream">shell_stream()</a></p>

A handle to a running streaming execution. It is an async iterator, so async for event in handle: yields each ExecEvent until the stream ends.

Property / MethodTypeDescription
idstrCorrelation ID for this execution
take_stdin()ExecSink | NoneTake the stdin writer. Returns None after the first call, or when stdin wasn't piped
recv()ExecEvent | None(async) Receive the next event. Returns None when the stream ends
wait()tuple[int, bool](async) Wait for the process to exit. Returns (code, success)
collect()ExecOutput(async) Drain remaining output and wait for exit
signal(sig)None(async) Send a POSIX signal (numeric) to the process
kill()None(async) Send SIGKILL to the process

ExecSink

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Returned by <a href="#exechandle">ExecHandle.take_stdin()</a></p>

Writer for sending data to a running process's stdin. Obtained from ExecHandle.take_stdin() when the execution was configured with stdin=Stdin.pipe().

MethodParametersDescription
write(data)data: bytes(async) Write bytes to the process's stdin
close()-(async) Close stdin. The process sees EOF

ExecEvent

<div className="msb-tags"><span className="msb-tag is-type">class</span></div> <p className="msb-backref">Emitted by <a href="#exechandle">ExecHandle</a></p>

Native event object emitted by recv() and by iterating an ExecHandle. Fields that don't apply to a given event are None.

PropertyTypeDescription
event_typestr"started", "stdout", "stderr", "exited", "failed", or "stdin_error"
pidint | NoneGuest PID, set on "started"
databytes | NoneOutput bytes on "stdout" / "stderr", or a UTF-8 failure message on "failed" / "stdin_error"
codeint | NoneExit code on "exited", or errno when available on "failed" / "stdin_error"

ExitStatus

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Used by <a href="#exechandle">ExecHandle.wait()</a></p>

Frozen dataclass describing a process exit result. ExecHandle.wait() returns the same information as a (code, success) tuple.

FieldTypeDescription
codeintProcess exit code
successboolTrue when code == 0

Stdin

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Used by <a href="#sandbox-exec">exec()</a> · <a href="#sandbox-shell">shell()</a> · <a href="#sandbox-exec_stream">exec_stream()</a> · <a href="#sandbox-shell_stream">shell_stream()</a></p>

Frozen dataclass with factory methods for configuring process stdin. Pass the result as the stdin argument to an exec or shell method. Raw bytes and str are also accepted directly and are sent inline.

FactoryParametersDescription
Stdin.null()-Connect stdin to /dev/null (default)
Stdin.pipe()-Open a writable pipe. Write via take_stdin() on the handle
Stdin.bytes(data)data: bytesInline data sent before the process starts, then EOF

Rlimit

<div className="msb-tags"><span className="msb-tag is-type">dataclass</span></div> <p className="msb-backref">Used by <a href="#sandbox-exec">exec()</a> · <a href="#sandbox-shell">shell()</a> · <a href="#sandbox-exec_stream">exec_stream()</a> · <a href="#sandbox-shell_stream">shell_stream()</a></p>

Frozen dataclass describing a POSIX resource limit. Construct one directly or via a factory, then pass a list as the rlimits argument.

FactoryParametersDescription
Rlimit.nofile(limit)limit: intMax open file descriptors
Rlimit.cpu(secs)secs: intCPU time limit in seconds
Rlimit.as_(*, soft, hard)soft: int, hard: intVirtual memory size
Rlimit.nproc(limit)limit: intMax number of processes
Rlimit.fsize(limit)limit: intMax file size
Rlimit.memlock(limit)limit: intMax locked memory
Rlimit.stack(limit)limit: intMax stack size

Fields

FieldTypeDescription
resourceRlimitResourceWhich resource is limited
softintSoft limit
hardintHard limit

RlimitResource

<div className="msb-tags"><span className="msb-tag is-type">enum</span></div> <p className="msb-backref">Used by <a href="#rlimit">Rlimit.resource</a></p>

String enum (enum.StrEnum) naming a limitable POSIX resource.

ValueDescription
CPUCPU time
FSIZEFile size
DATAData segment size
STACKStack size
CORECore file size
RSSResident set size
NPROCNumber of processes
NOFILEOpen file descriptors
MEMLOCKLocked memory
ASVirtual memory
LOCKSFile locks
SIGPENDINGPending signals
MSGQUEUEMessage queue size
NICENice priority ceiling
RTPRIOReal-time priority ceiling
RTTIMEReal-time CPU time