Skip to main content
The Python SDK exposes PTY support through sandbox.pty. The module has three callables you’ll use directly: Pty.create() to start a new session, Pty.connect() to reattach to an existing one, and the low-level Pty.send_stdin / resize / kill trio when you already hold a pid. For conceptual background see the PTY feature overview.

sandbox.pty.create(...)PtyHandle

Create a new PTY session. The sandbox spawns an interactive bash -l login shell with TERM=xterm-256color pre-set and returns a PtyHandle that can send input, resize, or kill the session and receive its output.

Parameters

Returns

A PtyHandle.

sandbox.pty.connect(pid, on_data=None)PtyHandle

Reattach to a PTY session that’s already running. Useful when another process created the session (e.g. a background worker) or when you want multiple UI clients to share the same shell.
Multiple subscribers see output from the moment they connect — there’s no scrollback replay. If the remote process has already exited, the stream immediately emits the cached exit frame.

PtyHandle

Returned by both create() and connect(). Exposes the full session lifecycle.

Properties

  • handle.pid: int — remote process id (matches what you’d see in ps inside the sandbox).
  • handle.exit_code: int | NoneNone while the session is live; becomes the remote exit code once the stream terminates.

Methods

handle.send_stdin(data: bytes | str, request_timeout=None) -> None

Forward keystrokes / text to the shell. Accepts bytes or str; str is UTF-8 encoded.

handle.resize(size: PtySize, request_timeout=None) -> None

Change the remote terminal dimensions. Equivalent to TIOCSWINSZ — fires SIGWINCH inside, so ncurses apps like vim and htop redraw.

handle.disconnect() -> None

Stop consuming the output stream without killing the remote process. The PTY keeps running server-side; a subsequent sandbox.pty.connect(pid) reattaches a new callback. Use this to pause/resume a UI or hand the session off to another client.

handle.kill(request_timeout=None) -> bool

Terminate the remote shell (SIGKILL to the process group). Returns True if the session existed at the time of the call. Idempotent.

handle.wait(timeout: float | None = None) -> PtyResult

Block until the remote shell exits and return a PtyResult. If the handle was created with an on_data callback, this joins the background reader thread. Otherwise it drains the stream inline, discarding bytes — iterate the handle directly if you care about the output.

Iterator

PtyHandle is iterable — each iteration yields the next chunk of output as bytes:
Use the iterator or on_data, not both — they consume the same stream.

PtyResult

The value returned from handle.wait().
Int-coercible — int(result) and result == 0 both work so existing code that treated it as a plain integer keeps compiling.

Low-level API (by pid)

When you don’t hold a PtyHandle — for example you’re wiring an HTTP route where the client passes pid as a query param — use the module-level operations:
All three hit the same REST endpoints as the PtyHandle methods; they just don’t require you to keep the handle around.

PtySize

Both fields default to 80 × 24 when the dataclass is constructed with no args.

Threading notes

  • The on_data callback runs on a background daemon thread. Don’t do long blocking work inside it — write bytes to a queue and process them elsewhere if needed.
  • send_stdin, resize, kill, and disconnect are safe to call from any thread.
  • wait() is synchronous. If you need an async loop, use the AsyncSandbox PTY module instead.

Example: hand off a PTY to your local terminal

A full “ssh-style” forwarder where keystrokes go to the sandbox and output comes back to your terminal, with SIGWINCH + raw-mode + clean exit is in the interactive terminal cookbook.