Skip to content

fix(python-sdk): align sync and async implementations#1403

Open
mishushakov wants to merge 2 commits into
mainfrom
mishushakov/python-sync-async-discrepancies
Open

fix(python-sdk): align sync and async implementations#1403
mishushakov wants to merge 2 commits into
mainfrom
mishushakov/python-sync-async-discrepancies

Conversation

@mishushakov

@mishushakov mishushakov commented Jun 8, 2026

Copy link
Copy Markdown
Member

Reconciles divergences found while auditing the sync and async Python SDK trees, keeping behavior equivalent across both (and with the JS SDK where they differed).

  • pause return value: Sandbox.pause() / AsyncSandbox.pause() (and beta_pause) now return a boolTrue if the sandbox got paused, False if it was already paused — matching the JS SDK. Previously the instance method returned None while the class-method form returned the sandbox ID, and the 409 (already-paused) case was indistinguishable from success. The sync _cls_pause also gains the isinstance(res.parsed, Error) check that async already had.
  • Parameter ordering: aligned _create and Commands._start signatures to the public API and to each other, and reordered the Commands.connect rpc args (headers before timeout) to match the _start convention.
  • Exceptions: the internal "Body of the request is None" guard in sandbox_api now consistently raises a bare Exception (matching the volume client) instead of mixing Exception/SandboxException between sync and async.
  • Misc: async Filesystem.write now passes keyword args; the async constructor reuses the cached envd_api_url property instead of recomputing the sandbox URL; async pty resize gains a -> None annotation.
  • Docstrings: aligned the deprecation marker and get_metrics/pause/write_files/kill wording across sync/async and fixed a **seconds**s typo.
# pause now returns whether it actually paused (matches JS)
paused = sandbox.pause()          # True
already = sandbox.pause()         # False — was already paused

The deeper architectural async-vs-sync splits (streaming-vs-polling watch_dir, pty on_data, command output callbacks) are intentional and left untouched.

🤖 Generated with Claude Code

Reconcile divergences between the sync and async Python SDKs found while
auditing the two trees:

- Match `_create` and `Commands._start` parameter ordering to the public
  signatures and across sync/async; align `Commands.connect` rpc arg order.
- Use a consistent bare `Exception` for the internal "Body of the request
  is None" guard in `sandbox_api` (matching the volume client convention).
- Pass keyword args in async `Filesystem.write`; reuse the cached
  `envd_api_url` property instead of recomputing the sandbox URL.
- Align docstrings: deprecation marker, `get_metrics`/`pause`/`write_files`
  wording, `kill` wording, and a `**seconds**s` typo; add `-> None` to
  async pty `resize`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f7609f4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@e2b/python-sdk Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cursor

cursor Bot commented Jun 8, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
pause() return type changed from None/sandbox ID to bool, which is a breaking API change for consumers; remaining edits are ordering, docs, and exception typing with minimal runtime impact.

Overview
Aligns the Python sync and async SDK trees and changes pause() semantics to match the JS SDK.

Sandbox.pause() / AsyncSandbox.pause() (and beta_pause) now return bool: True when a pause was performed, False when the sandbox was already paused (HTTP 409). Instance methods propagate that value; _cls_pause no longer returns the sandbox ID on success. Callers that relied on the old None or sandbox-id return values need to update.

Other changes are consistency-only: matching _create / Commands._start parameter order (including moving stdin before timeout args), keyword args in async Filesystem.write, reordering connect RPC kwargs, using envd_api_url in the async constructor, unified docstrings, sync _cls_pause error-body handling like async, and raising bare Exception for the internal empty-response guard in sandbox_api (instead of SandboxException).

Reviewed by Cursor Bugbot for commit f7609f4. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from 8cde0d5. Download artifacts from this workflow run.

JS SDK (e2b@2.28.1-mishushakov-python-sync-async-discrepancies.0):

npm install ./e2b-2.28.1-mishushakov-python-sync-async-discrepancies.0.tgz

CLI (@e2b/cli@2.10.5-mishushakov-python-sync-async-discrepancies.0):

npm install ./e2b-cli-2.10.5-mishushakov-python-sync-async-discrepancies.0.tgz

Python SDK (e2b==2.26.0+mishushakov-python-sync-async-discrepancies):

pip install ./e2b-2.26.0+mishushakov.python.sync.async.discrepancies-py3-none-any.whl

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — small alignment cleanup; parameter reorderings are safe (all callers use keyword args), and the other changes are equivalent substitutions, docstring fixes, and type annotations.

Extended reasoning...

Overview

This PR aligns the sync and async Python SDK trees: reorders parameters in _create and Commands._start to match the public API, switches Filesystem.write to keyword args, replaces SandboxException with bare Exception for the internal "Body of the request is None" guard (matching the volume client), reuses the cached envd_api_url property in the async constructor, adds a -> None annotation on async pty resize, and aligns several docstrings (deprecation marker, get_metrics/pause/write_files/kill wording, fixes a **seconds**s typo, removes a misleading :return: on pause()).

Security risks

None. No auth, crypto, permission, or input-validation logic is touched. The reordered _create includes secure and allow_internet_access parameters but the values themselves and their semantics are unchanged — only positional order.

Level of scrutiny

Low. This is a consistency/alignment PR with no behavior changes beyond the internal Exception type swap (for a guard that should never fire). The parameter reorderings are safe because every caller uses keyword arguments — verified by grepping all call sites of _create and _start in both sync and async trees. The envd_api_url property substitution in AsyncSandbox.__init__ computes the same value as the previous inline expression (get_sandbox_url(sandbox_id, sandbox_domain)).

Other factors

A changeset is included (patch bump for @e2b/python-sdk). The PR description is precise and accurately enumerates the changes. The author explicitly notes that deeper architectural splits (watch_dir streaming-vs-polling, pty on_data, command output callbacks) are intentionally left untouched, which suggests careful scoping.

`pause()` / `beta_pause()` now return a `bool` (`True` if the sandbox got
paused, `False` if it was already paused) in both sync and async, matching
the JS SDK. Previously the instance method returned `None` while the
class-method form returned the sandbox ID, and the 409 (already-paused)
case was indistinguishable from success. Also adds the missing
`isinstance(res.parsed, Error)` check to sync `_cls_pause` to match async.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant