feat(python): support timeout=None to disable timeout#402
Merged
Conversation
When `timeout=None` is explicitly passed to any per-request method, the timeout is now actively disabled (wait indefinitely), matching HTTPX behaviour. A `USE_CLIENT_DEFAULT` sentinel constant is introduced so users can also explicitly say "use the client-level timeout" when needed. Changes: - `impit/src/request.rs`: `RequestOptions.timeout` is now `Option<Option<Duration>>` (None = use default, Some(None) = disabled, Some(Some(d)) = specific value) - `impit/src/impit.rs`: translates the three-state timeout before passing to reqwest - `impit-node/src/lib.rs`: trivially wraps the existing timeout value in `Some(…)` to match the new type - `impit-python/src/request.rs`: adds `USE_CLIENT_DEFAULT_SENTINEL`, `default_timeout()` and `timeout_from_pyobj()` helpers - `impit-python/src/client.rs` / `async_client.rs` / `lib.rs`: per-request `timeout` parameter changed from `Option<f64>` to `Py<PyAny>` with the sentinel as default; parsed via `timeout_from_pyobj` - `impit-python/python/impit/__init__.py`: exports `USE_CLIENT_DEFAULT` - `impit-python/python/impit/impit.pyi`: updated type stubs and docs - `impit-python/test/basic_client_test.py`: new `TestTimeoutBehaviour` class Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
… quality Co-authored-by: barjin <61918049+barjin@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix timeout handling for None value in requests
feat(python): support Mar 5, 2026
timeout=None to disable timeout per-request
…L-acquired sentinel Replace Py<PyAny> timeout parameters with Option<Either<f64, &str>> leveraging pyo3's built-in either feature for type extraction. This removes the default_timeout() function that acquired the GIL just to create a Python string.
barjin
reviewed
Mar 6, 2026
Comment on lines
+17
to
+20
| /// - `None` — inherit the client-level default timeout set via [`ImpitBuilder::with_default_timeout`](crate::impit::ImpitBuilder::with_default_timeout). | ||
| /// - `Some(None)` — disable the timeout entirely for this request (wait indefinitely). | ||
| /// - `Some(Some(d))` — use the given duration, overriding the client-level default. | ||
| pub timeout: Option<Option<Duration>>, |
Member
There was a problem hiding this comment.
This is rather messy and a candidate for refactoring, but I suppose it's as good as we can do without changing the API too much.
The error mapping produces the base TimeoutException, not the ConnectTimeout/ReadTimeout subclasses.
timeout=None to disable timeout per-requesttimeout=None to disable timeout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
timeout=Noneon per-request methods fell back to the client-level default instead of disabling the timeout, making it impossible to issue truly unbounded requests and diverging from HTTPX conventions.Core changes
RequestOptions.timeoutchanged fromOption<Duration>toOption<Option<Duration>>:None→ inherit client default (unchanged)Some(None)→ disable timeout entirelySome(Some(d))→ specific overrideimpit.rs: mapsSome(None)→Duration::MAXbefore handing off to reqwest (the only per-request escape hatch reqwest exposes without rebuilding the client)impit-node: trivial one-liner wrap of the existing value inSome(…)Python binding changes
Distinguishing "not passed" from "explicitly
None" requires a sentinel because PyO3 maps both to RustNoneforOption<f64>. Solution:timeoutparameter changed fromOption<f64>toPy<PyAny>with an interned string sentinel ("__impit_use_client_default__") as the PyO3 defaulttimeout_from_pyobj()parses the three states;default_timeout()returns the sentinel for use in#[pyo3(signature = (...))]USE_CLIENT_DEFAULTis exposed as a public module constant (matching HTTPX's convention) so callers can pass it explicitlyUsage
Type stubs and docstrings updated to reflect the new
timeout: float | None = USE_CLIENT_DEFAULTsignature on all per-request methods.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.