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
| Name | Type | Default | Description |
|---|---|---|---|
size | PtySize | PtySize(cols=80, rows=24) | Initial terminal size. |
user | str | "user" | User the shell runs as. |
cwd | str | None | None | Starting working directory. |
envs | dict[str, str] | None | None | Environment variables merged into the shell env. TERM defaults to xterm-256color unless overridden here. |
timeout | float | None | 3600 | PTY session TTL in seconds. 0 keeps the session alive until the sandbox itself expires. |
on_data | Callable[[bytes], None] | None | None | If provided, a background reader thread calls this for every output chunk. Without it you consume output by iterating the returned handle. |
request_timeout | float | None | None | httpx timeout applied to the create POST only. |
Returns
APtyHandle.
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.
PtyHandle
Returned by both create() and connect(). Exposes the full session
lifecycle.
Properties
handle.pid: int— remote process id (matches what you’d see inpsinside the sandbox).handle.exit_code: int | None—Nonewhile 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:
on_data, not both — they consume the same
stream.
PtyResult
The value returned from handle.wait().
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 aPtyHandle — for example you’re wiring an HTTP
route where the client passes pid as a query param — use the
module-level operations:
PtyHandle methods; they
just don’t require you to keep the handle around.
PtySize
80 × 24 when the dataclass is constructed
with no args.
Threading notes
- The
on_datacallback 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, anddisconnectare safe to call from any thread.wait()is synchronous. If you need an async loop, use theAsyncSandboxPTY 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, withSIGWINCH + raw-mode + clean
exit is in the
interactive terminal cookbook.