Skip to content

fix(python-sdk): keep streamed request bodies unbuffered across retries - #1718

Merged
mishushakov merged 4 commits into
mainfrom
devin/1787165767-unbuffered-retry-streams
Aug 21, 2026
Merged

fix(python-sdk): keep streamed request bodies unbuffered across retries#1718
mishushakov merged 4 commits into
mainfrom
devin/1787165767-unbuffered-retry-streams

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

pyqwest's retry middleware keeps a request replayable by mirroring a non-bytes body into memory as it is sent, so a streamed upload through the shared retrying transports (files.write of a file-like object, volume.write_file) reached the wire in chunks yet accumulated its whole body in RAM. curioswitch/pyqwest#219 (released in pyqwest 0.10.0) adds RetryMode.UNBUFFERED, which drops that copy: a streamed body is handed to the next attempt only while nothing has been read from it.

That is exactly what the SDK's connect-only retry policy needs — pyqwest raises the builtin ConnectionError (the only thing should_retry_response retries) only before the request body was written, so the stream is still untouched on every failure we retry. bytes bodies (unary RPCs, in-memory writes) stay replayable in either mode.

Both ConnectionRetryTransports (sync + async) now declare it:

def should_retry_request(self, request: Request) -> RetryMode:
    return RetryMode.UNBUFFERED

The pyqwest pin moves to >=0.10.0,<0.11, the release shipping RetryMode.

Tests (tests/test_retry_stream_buffering.py):

  • both transports override should_retry_request with RetryMode.UNBUFFERED (the inherited hook returns True — buffered)
  • untouched streams are retried after a simulated connect failure (sync + async)
  • peak allocation stays far below body size while streaming (tracemalloc, 16 MiB body), and a stream that failed after its first chunk is not replayed
  • the httpx→pyqwest adapters hand the body to the retry middleware as a stream, not flattened bytes

Link to Devin session: https://app.devin.ai/sessions/895a6967064e425abfc84b8cfbc32910
Requested by: @mishushakov

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@cla-bot cla-bot Bot added the cla-signed label Aug 19, 2026
@changeset-bot

changeset-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8bd0e82

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

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from c2b95c9. Download artifacts from this workflow run.

JS SDK (e2b@2.44.2-devin-1787165767-unbuffered-retry-streams.0):

npm install ./e2b-2.44.2-devin-1787165767-unbuffered-retry-streams.0.tgz

CLI (@e2b/cli@2.16.4-devin-1787165767-unbuffered-retry-streams.0):

npm install ./e2b-cli-2.16.4-devin-1787165767-unbuffered-retry-streams.0.tgz

Python SDK (e2b==2.44.0+devin.1787165767.unbuffered.retry.streams):

pip install ./e2b-2.44.0+devin.1787165767.unbuffered.retry.streams-py3-none-any.whl

@mishushakov

Copy link
Copy Markdown
Member

@devin-ai-integration instead of any assume we're on the version of pyqwest that ships the retrymode

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2d63c7613

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/python-sdk/e2b/api/__init__.py Outdated
Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Done in fe00f13 — dropped the Any-typed fallback: both transports now from pyqwest.middleware.retry import RetryMode and return RetryMode.UNBUFFERED (typed -> RetryMode), and the buffered-fallback tests are gone.

One dependency note: no PyPI release ships RetryMode yet (latest is 0.9.0), so with the current pyqwest>=0.9.0,<0.10 pin the import fails at runtime/CI. I verified everything against a source build of pyqwest main (lint/ty/tests green). Once the release with curioswitch/pyqwest#219 is out, the pin needs to move — happy to bump it here when you tell me the version.

cursor[bot]

This comment was marked as outdated.

@linear-code

linear-code Bot commented Aug 21, 2026

Copy link
Copy Markdown

SDK-332

@mishushakov

Copy link
Copy Markdown
Member

pyqwest v0.10.0 was released

Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

Bumped the pin to pyqwest>=0.10.0,<0.11 in 6c380c4 — lint/ty/tests green locally against the released 0.10.0.

@mishushakov
mishushakov enabled auto-merge (squash) August 21, 2026 12:34
Co-Authored-By: mish@e2b.dev <mish@e2b.dev>
@mishushakov
mishushakov merged commit b17b726 into main Aug 21, 2026
22 checks passed
@mishushakov
mishushakov deleted the devin/1787165767-unbuffered-retry-streams branch August 21, 2026 12:46
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