diff --git a/.changeset/sync-command-handle-accessors.md b/.changeset/sync-command-handle-accessors.md new file mode 100644 index 0000000000..9ad4d4bc7c --- /dev/null +++ b/.changeset/sync-command-handle-accessors.md @@ -0,0 +1,5 @@ +--- +"@e2b/python-sdk": minor +--- + +Add `stdout`, `stderr`, `error` and `exit_code` accessors to the sync `CommandHandle`, mirroring the async handle and the JS SDK for parity. diff --git a/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py b/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py index ea2b3fdad7..b827671c54 100644 --- a/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py +++ b/packages/python-sdk/e2b/sandbox_sync/commands/command_handle.py @@ -30,6 +30,42 @@ def pid(self): """ return self._pid + @property + def stdout(self): + """ + Command stdout output. + """ + return "".join(self._stdout_chunks) + + @property + def stderr(self): + """ + Command stderr output. + """ + return "".join(self._stderr_chunks) + + @property + def error(self): + """ + Command execution error message. + """ + if self._result is None: + return None + return self._result.error + + @property + def exit_code(self): + """ + Command execution exit code. + + `0` if the command finished successfully. + + It is `None` if the command is still running. + """ + if self._result is None: + return None + return self._result.exit_code + def __init__( self, pid: int, diff --git a/packages/python-sdk/tests/test_command_handle.py b/packages/python-sdk/tests/test_command_handle.py index 6a2f81c1ad..99521f885d 100644 --- a/packages/python-sdk/tests/test_command_handle.py +++ b/packages/python-sdk/tests/test_command_handle.py @@ -288,3 +288,26 @@ async def events(): # be flushed to the stdout callback as a replacement character. assert "".join(chunks) == "a�" assert isinstance(handle._iteration_exception, RuntimeError) + + +def test_sync_command_handle_exposes_result_accessors(): + def events(): + yield _stdout_event(b"out") + yield _stderr_event(b"err") + yield _end_event(0) + + handle = CommandHandle(pid=1, handle_kill=lambda: True, events=events()) + + # None while the command is still running, matching the async twin. + assert handle.exit_code is None + assert handle.error is None + assert handle.stdout == "" + assert handle.stderr == "" + + handle.wait() + + assert handle.stdout == "out" + assert handle.stderr == "err" + assert handle.exit_code == 0 + assert handle.error is None +