feat!: Make HTTP client pluggable with abstract base classes#641
feat!: Make HTTP client pluggable with abstract base classes#641
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #641 +/- ##
==========================================
- Coverage 96.62% 96.59% -0.04%
==========================================
Files 45 46 +1
Lines 4271 4433 +162
==========================================
+ Hits 4127 4282 +155
- Misses 144 151 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR makes the Apify Python client’s HTTP transport layer pluggable by introducing public abstract base classes (HttpClient, HttpClientAsync) and an HttpResponse protocol, while moving the default impit-backed implementation into ImpitHttpClient / ImpitHttpClientAsync. It also wires the new http_client=... injection into ApifyClient / ApifyClientAsync and updates type hints and tests accordingly.
Changes:
- Add
HttpResponseprotocol plusHttpClient/HttpClientAsyncABCs, and moveimpitimplementation intoImpitHttpClient/ImpitHttpClientAsync. - Allow passing a custom
http_clientintoApifyClientandApifyClientAsync, and export the new public HTTP types fromapify_client. - Update resource-client type hints and expand unit tests to cover custom client behavior and streaming.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_pluggable_http_client.py | New unit tests validating ABC/protocol conformance and custom http_client injection behavior (sync + async). |
| tests/unit/test_http_clients.py | Updates tests to target ImpitHttpClient* for retry logic and adds concrete ABC implementations for base-helper testing. |
| tests/unit/test_client_timeouts.py | Switches tests to use ImpitHttpClient* (since HttpClient* are now abstract). |
| tests/unit/test_client_headers.py | Switches tests to use ImpitHttpClient* for header behavior. |
| tests/unit/test_client_errors.py | Switches tests to use ImpitHttpClient* and keeps streamed-error coverage. |
| src/apify_client/errors.py | Generalizes error types to accept HttpResponse instead of impit.Response. |
| src/apify_client/_utils.py | Generalizes response-parsing helpers to HttpResponse and updates docstrings accordingly. |
| src/apify_client/_resource_clients/task.py | Docstring formatting adjustment (no functional behavior change). |
| src/apify_client/_resource_clients/log.py | Generalizes streaming return types to HttpResponse. |
| src/apify_client/_resource_clients/key_value_store.py | Generalizes record parsing function signature to HttpResponse. |
| src/apify_client/_resource_clients/dataset.py | Generalizes streaming item types to HttpResponse. |
| src/apify_client/_http_clients/_impit.py | Introduces ImpitHttpClient* and factors retryability logic; keeps default impit implementation. |
| src/apify_client/_http_clients/_base.py | Adds HttpResponse protocol and HttpClient* ABCs; keeps shared request-prep helpers in _HttpClientBase. |
| src/apify_client/_http_clients/init.py | Re-exports HttpClient*, HttpResponse, and default ImpitHttpClient*. |
| src/apify_client/_apify_client.py | Adds http_client parameter, defaults to ImpitHttpClient*, and exposes http_client property. |
| src/apify_client/init.py | Exports the new HTTP types from the public apify_client package. |
Comments suppressed due to low confidence (2)
src/apify_client/_http_clients/_impit.py:16
_impit.pyimportsHttpClient/HttpClientAsyncfrom the package (apify_client._http_clients), but that package’s__init__also imports_impit, creating an avoidable circular import that can become fragile depending on import order. Import these symbols directly from._base(andHttpResponsefrom._baseunder TYPE_CHECKING) to break the cycle.
src/apify_client/_http_clients/_impit.py:44- There are now two sources of truth for retryability (
apify_client._utils.is_retryable_errorand_http_clients._impit._is_retryable_error) with identical logic. This duplication risks the rules drifting over time; consider reusing the existing helper from_utilshere, or otherwise removing/consolidating one of them so tests and the impit client validate the same implementation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Pijukatel
left a comment
There was a problem hiding this comment.
Nice. The only drawback I see is the initialization with conflicting arguments. Maybe we can figure out something better.
0b8ab15 to
46693a8
Compare
- Introduce `HttpClient` and `HttpClientAsync` ABCs that users can extend to provide custom HTTP client implementations via `ApifyClient(http_client=...)` and `ApifyClientAsync(http_client=...)` - Factor impit-specific logic into `ImpitHttpClient`/`ImpitHttpClientAsync`, keeping `_base.py` free of any `impit` dependency - Add `HttpResponse` protocol with full streaming support (`iter_bytes`, `aiter_bytes`, `read`, `aread`, `close`, `aclose`) - Export all public types (`HttpClient`, `HttpClientAsync`, `HttpResponse`, `ImpitHttpClient`, `ImpitHttpClientAsync`) from `apify_client`
5e574a3 to
fe5c92f
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
HttpClientandHttpClientAsyncABCs that users can extend to provide custom HTTP client implementations viaApifyClient(http_client=...)andApifyClientAsync(http_client=...)ImpitHttpClient/ImpitHttpClientAsync, keeping_base.pyfree of anyimpitdependencyHttpResponseprotocol with full streaming support (iter_bytes,aiter_bytes,read,aread,close,aclose)HttpClient,HttpClientAsync,HttpResponse,ImpitHttpClient,ImpitHttpClientAsync) fromapify_clientIssue
http_clienttoApifyClientandApifyClientAsync#416Test plan