> ## Documentation Index
> Fetch the complete documentation index at: https://docs.microsandbox.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Execution

> Python SDK - Command execution API reference

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`](/sdk/python/sandbox). See [Commands](/sandboxes/commands) for usage examples.

<p className="msb-label" id="typical-flow">Typical flow</p>

```python theme={null}
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>

```python theme={null}
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
```

<Accordion title="Example">
  ```python theme={null}
  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>

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()`](#sandbox-exec_stream) instead. Raises [`ExecTimeoutError`](/sdk/python/sandbox) if `timeout` elapses and [`ExecFailedError`](/sdk/python/sandbox) 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>

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

```python theme={null}
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
```

<Accordion title="Example">
  ```python theme={null}
  out = await sandbox.shell("ls -la /app && echo done")
  print(out.stdout_text)
  ```
</Accordion>

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()`](#sandbox-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 && 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>

## Stream

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

```python theme={null}
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
```

<Accordion title="Example">
  ```python theme={null}
  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>

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

<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>

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

```python theme={null}
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
```

<Accordion title="Example">
  ```python theme={null}
  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>

Streaming variant of [`shell()`](#sandbox-shell): runs `script` through the configured shell but returns an [`ExecHandle`](#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>

## Attach

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

```python theme={null}
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
```

<Accordion title="Example">
  ```python theme={null}
  code = await sandbox.attach("bash", cwd="/app", env={"EDITOR": "vim"})
  ```
</Accordion>

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>

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

```python theme={null}
async def attach_shell() -> int
```

<Accordion title="Example">
  ```python theme={null}
  code = await sandbox.attach_shell()
  ```
</Accordion>

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>

## Types

### ExecOutput

<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.

| Property      | Type    | Description                                                   |
| ------------- | ------- | ------------------------------------------------------------- |
| exit\_code    | `int`   | Process exit code                                             |
| success       | `bool`  | `True` when `exit_code == 0`                                  |
| stdout\_text  | `str`   | Collected stdout decoded as UTF-8. Raises on invalid encoding |
| stderr\_text  | `str`   | Collected stderr decoded as UTF-8. Raises on invalid encoding |
| stdout\_bytes | `bytes` | Raw stdout bytes                                              |
| stderr\_bytes | `bytes` | Raw stderr bytes                                              |

### ExecHandle

<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`](#execevent) until the stream ends.

| Property / Method | Type                                 | Description                                                                            |
| ----------------- | ------------------------------------ | -------------------------------------------------------------------------------------- |
| id                | `str`                                | Correlation ID for this execution                                                      |
| take\_stdin()     | [`ExecSink`](#execsink) ` \| None`   | Take the stdin writer. Returns `None` after the first call, or when stdin wasn't piped |
| recv()            | [`ExecEvent`](#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`](#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

<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()`](#exechandle) when the execution was configured with `stdin=Stdin.pipe()`.

| Method      | Parameters    | Description                                  |
| ----------- | ------------- | -------------------------------------------- |
| write(data) | `data: bytes` | *(async)* Write bytes to the process's stdin |
| close()     | -             | *(async)* Close stdin. The process sees EOF  |

### ExecEvent

<p className="msb-backref">Emitted by <a href="#exechandle">ExecHandle</a></p>

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

| Property    | Type            | Description                                                                                         |
| ----------- | --------------- | --------------------------------------------------------------------------------------------------- |
| event\_type | `str`           | `"started"`, `"stdout"`, `"stderr"`, `"exited"`, `"failed"`, or `"stdin_error"`                     |
| pid         | `int \| None`   | Guest PID, set on `"started"`                                                                       |
| data        | `bytes \| None` | Output bytes on `"stdout"` / `"stderr"`, or a UTF-8 failure message on `"failed"` / `"stdin_error"` |
| code        | `int \| None`   | Exit code on `"exited"`, or errno when available on `"failed"` / `"stdin_error"`                    |

### ExitStatus

<p className="msb-backref">Used by <a href="#exechandle">ExecHandle.wait()</a></p>

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

| Field   | Type   | Description             |
| ------- | ------ | ----------------------- |
| code    | `int`  | Process exit code       |
| success | `bool` | `True` when `code == 0` |

### Stdin

<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.

| Factory           | Parameters    | Description                                                                 |
| ----------------- | ------------- | --------------------------------------------------------------------------- |
| Stdin.null()      | -             | Connect stdin to `/dev/null` (default)                                      |
| Stdin.pipe()      | -             | Open a writable pipe. Write via [`take_stdin()`](#exechandle) on the handle |
| Stdin.bytes(data) | `data: bytes` | Inline data sent before the process starts, then EOF                        |

### Rlimit

<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.

| Factory                     | Parameters             | Description               |
| --------------------------- | ---------------------- | ------------------------- |
| Rlimit.nofile(limit)        | `limit: int`           | Max open file descriptors |
| Rlimit.cpu(secs)            | `secs: int`            | CPU time limit in seconds |
| Rlimit.as\_(\*, soft, hard) | `soft: int, hard: int` | Virtual memory size       |
| Rlimit.nproc(limit)         | `limit: int`           | Max number of processes   |
| Rlimit.fsize(limit)         | `limit: int`           | Max file size             |
| Rlimit.memlock(limit)       | `limit: int`           | Max locked memory         |
| Rlimit.stack(limit)         | `limit: int`           | Max stack size            |

**Fields**

| Field    | Type                                | Description               |
| -------- | ----------------------------------- | ------------------------- |
| resource | [`RlimitResource`](#rlimitresource) | Which resource is limited |
| soft     | `int`                               | Soft limit                |
| hard     | `int`                               | Hard limit                |

### RlimitResource

<p className="msb-backref">Used by <a href="#rlimit">Rlimit.resource</a></p>

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

| Value        | Description                |
| ------------ | -------------------------- |
| `CPU`        | CPU time                   |
| `FSIZE`      | File size                  |
| `DATA`       | Data segment size          |
| `STACK`      | Stack size                 |
| `CORE`       | Core file size             |
| `RSS`        | Resident set size          |
| `NPROC`      | Number of processes        |
| `NOFILE`     | Open file descriptors      |
| `MEMLOCK`    | Locked memory              |
| `AS`         | Virtual memory             |
| `LOCKS`      | File locks                 |
| `SIGPENDING` | Pending signals            |
| `MSGQUEUE`   | Message queue size         |
| `NICE`       | Nice priority ceiling      |
| `RTPRIO`     | Real-time priority ceiling |
| `RTTIME`     | Real-time CPU time         |
