Skip to content

sdk/python: Add PTY-related APIs#579

Open
chenhengqi wants to merge 1 commit into
masterfrom
pty-api
Open

sdk/python: Add PTY-related APIs#579
chenhengqi wants to merge 1 commit into
masterfrom
pty-api

Conversation

@chenhengqi

Copy link
Copy Markdown
Collaborator

No description provided.

Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
Comment on lines +102 to +103
def __iter__(self) -> Generator[PtyOutput, None, None]:
return self._handle_events()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double iteration silently produces no output

__iter__ returns self._handle_events(), which creates a fresh generator each call. But self._events (the underlying Connect-JSON event generator) is single-use. Calling __iter__ a second time — e.g., calling wait() twice, or manually iterating then calling wait() — yields zero chunks because the inner generator is exhausted. The second wait() would raise a confusing "PTY stream ended without an end event" error.

Consider guarding with a consumed flag that raises RuntimeError on reuse, or clearly documenting/explicitly enforcing single-iteration.

Comment on lines +202 to +205
def _client(self):
if self._sandbox._client is None:
self._sandbox._client = self._sandbox._build_data_client()
return self._sandbox._client

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return type annotation

_client() has no return type annotation. It always returns a non-None httpx.Client (it lazily builds one). The companion Sandbox._build_data_client in sandbox.py is annotated; this one should be for consistency and to help type checkers. Should be -> httpx.Client.

Comment on lines +446 to +451
except BaseException:
try:
resp_ctx.__exit__(None, None, None)
except Exception:
pass
raise

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except BaseException: catches KeyboardInterrupt and SystemExit

The broadest catch clause silently swallows Ctrl-C and interpreter shutdown signals. If a user hits Ctrl-C during PTY creation, the handler blocks clean process exit. Change to except Exception: — or handle GeneratorExit explicitly if the generator cleanup path requires it.

_raise_connect_end_stream(raw)
return

message = json.loads(raw.decode("utf-8"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary raw.decode("utf-8") before json.loads

json.loads has accepted bytes directly since Python 3.6, and the Connect-JSON wire format is always UTF-8. Writing json.loads(raw) instead eliminates one intermediate string allocation and one decode pass per event. On busy PTY output streams with thousands of events per second this adds measurable GC pressure.

Comment on lines +102 to +103
def __iter__(self) -> Generator[PtyOutput, None, None]:
return self._handle_events()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double iteration silently produces no output

__iter__ returns self._handle_events(), which creates a fresh generator each call. But self._events (the underlying Connect-JSON event generator) is single-use. Calling __iter__ a second time — e.g., calling wait() twice, or manually iterating then calling wait() — yields zero chunks because the inner generator is exhausted. The second wait() would raise a confusing "PTY stream ended without an end event" error.

Consider guarding with a consumed flag that raises RuntimeError on reuse, or clearly documenting/explicitly enforcing single-iteration.

Comment on lines +202 to +205
def _client(self):
if self._sandbox._client is None:
self._sandbox._client = self._sandbox._build_data_client()
return self._sandbox._client

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing return type annotation

_client() has no return type annotation. It always returns a non-None httpx.Client (it lazily builds one). The companion Sandbox._build_data_client in sandbox.py is annotated; this one should be for consistency and to help type checkers. Should be -> httpx.Client.

Comment on lines +446 to +451
except BaseException:
try:
resp_ctx.__exit__(None, None, None)
except Exception:
pass
raise

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except BaseException: catches KeyboardInterrupt and SystemExit

The broadest catch clause silently swallows Ctrl-C and interpreter shutdown signals. If a user hits Ctrl-C during PTY creation, the handler prevents clean process exit. Change to except Exception: — or handle GeneratorExit explicitly if cleanup requires it.

_raise_connect_end_stream(raw)
return

message = json.loads(raw.decode("utf-8"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary raw.decode("utf-8") before json.loads

json.loads has accepted bytes directly since Python 3.6, and the Connect-JSON wire format is always UTF-8. Writing json.loads(raw) instead eliminates one intermediate string allocation and one decode pass per event. On busy PTY output streams with thousands of events per second this adds measurable GC pressure.

@chenhengqi chenhengqi marked this pull request as draft June 16, 2026 09:22
@tinklone tinklone marked this pull request as ready for review June 23, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant