Skip to content

fix(python-sdk): enforce request timeouts as whole-request deadlines - #1977

Draft
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1791887064-python-request-deadline
Draft

devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1791887064-python-request-deadline

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Fixes #1881 (run_code(timeout=N) enforced at ~2N) at the layer that owns the deadline, superseding the per-call-site write=0 approach in #1886.

pyqwest's httpx adapter has no deadline input: convert_timeout approximates an operation timeout as read + write (curioswitch/pyqwest#220) and ignores connect/pool. Every httpx.Timeout(x) the SDK builds (ApiClient's Timeout(config.request_timeout), envd file ops, the volume client, template uploads, code-interpreter's Timeout(timeout, connect=request_timeout)) was therefore enforced at 2x, while RetryableTransport._request_deadline budgeted min(phases) = x.

New leaf module e2b/deadline.py (import-cycle-free so e2b.retry can use it) defines the SDK rule once — the read phase of httpx.Timeout is the whole-request deadline:

request_deadline(extensions)        -> extensions["timeout"]["read"]  (None = unbounded)
with_request_deadline(extensions)   -> timeout={..., "read": d, "write": 0 if d is not None else None}

class PyqwestDeadlineTransport(PyqwestTransport):          # + Async twin
    def handle_request(self, request):
        request.extensions = with_request_deadline(request.extensions)
        return super().handle_request(request)
  • get_httpx_transport (sync/async) and the inline template-upload transports now build PyqwestDeadlineTransport / AsyncPyqwestDeadlineTransport, so all shared httpx consumers get Timeout(x) → x with no call-site changes.
  • _request_deadline in e2b/retry.py uses request_deadline, so the retry budget equals the enforced deadline (Timeout(300, connect=60) → 300, not 60).
  • code-interpreter: replaced the stale "takes the longest phase" comment, and request_timeout=0 on run_code/create_code_context now disables the timeout via ConnectionConfig.get_request_timeout instead of request_timeout or default (pre-existing nit from the fix(code-interpreter): abort Jupyter requests at the requested deadline #1886 review).

The rewrite deliberately encodes pyqwest's current sum rule in one place; once pyqwest exposes an explicit operation-timeout extension, with_request_deadline becomes a one-line change.

Tests: tests/test_request_deadline.py — pure mapping/retry-budget cases plus a stalling local HTTP server proving sync and async ApiClient (request_timeout) and envd (Timeout(d, connect=60)) requests fail at ~d rather than 2d, and Timeout(None) still waits.

Link to Devin session: https://app.devin.ai/sessions/8c713b7050c949b892adfbbfa9fd4525
Open in Devin Desktop: https://app.devin.ai/desktop/session/8c713b7050c949b892adfbbfa9fd4525?variant=devin
Requested by: @mishushakov

devin-ai-integration Bot and others added 2 commits October 13, 2026 10:24
pyqwest's httpx adapter approximates an operation timeout as the sum of the
httpx read and write phases, so every httpx.Timeout(N) the SDK built was
enforced at ~2N (#1881). Map the deadline once in the shared transports
(e2b.deadline) and budget retries against the same number; drop the stale
code-interpreter comment and let request_timeout=0 disable the timeout there.

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

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@cla-bot cla-bot Bot added the cla-signed label Oct 13, 2026
@changeset-bot

changeset-bot Bot commented Oct 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 89a7d30

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

This PR includes changesets to release 2 packages
Name Type
@e2b/python-sdk Patch
@e2b/code-interpreter-python 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

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from 5715099. Download artifacts from this workflow run.

JS SDK (e2b@2.55.1-devin-1791887064-python-request-deadline.0, with @e2b/dockerfile-utils@0.1.1-devin-1791887064-python-request-deadline.0):

npm install ./e2b-dockerfile-utils-0.1.1-devin-1791887064-python-request-deadline.0.tgz ./e2b-2.55.1-devin-1791887064-python-request-deadline.0.tgz

CLI (@e2b/cli@2.21.3-devin-1791887064-python-request-deadline.0):

npm install ./e2b-cli-2.21.3-devin-1791887064-python-request-deadline.0.tgz

Code Interpreter JS SDK (@e2b/code-interpreter@2.8.4-devin-1791887064-python-request-deadline.0):

npm install ./e2b-code-interpreter-2.8.4-devin-1791887064-python-request-deadline.0.tgz

Desktop JS SDK (@e2b/desktop@2.4.1-devin-1791887064-python-request-deadline.0):

npm install ./e2b-desktop-2.4.1-devin-1791887064-python-request-deadline.0.tgz

Python SDK (e2b==2.55.1+devin.1791887064.python.request.deadline, with e2b-dockerfile-utils==0.1.0+devin.1791887064.python.request.deadline):

pip install ./e2b_dockerfile_utils-0.1.0+devin.1791887064.python.request.deadline-py3-none-any.whl ./e2b-2.55.1+devin.1791887064.python.request.deadline-py3-none-any.whl

Code Interpreter Python SDK (e2b-code-interpreter==2.10.5+devin.1791887064.python.request.deadline):

pip install ./e2b_code_interpreter-2.10.5+devin.1791887064.python.request.deadline-py3-none-any.whl

Desktop Python SDK (e2b-desktop==2.6.1+devin.1791887064.python.request.deadline):

pip install ./e2b_desktop-2.6.1+devin.1791887064.python.request.deadline-py3-none-any.whl

This branch has not been deployed

No deployments
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.

[Bug]: run_code(timeout=N) is enforced at ~2*N seconds — the pyqwest transport derives one deadline by summing read+write

1 participant