Migrate SDK HTTP stack from requests to httpx#221
Merged
goncalossilva merged 6 commits intomainfrom Feb 20, 2026
Merged
Conversation
6167b48 to
f9dc425
Compare
Move the async client to native httpx.AsyncClient I/O and remove\nexecutor-based async wrappers from API flows.\n\nAlso add respx-backed test infrastructure and migrate API tests\nthat exercise async behavior through the new transport stack.
26fde1d to
3112144
Compare
There was a problem hiding this comment.
Pull request overview
This PR successfully migrates the Todoist Python SDK's HTTP stack from requests to httpx, enabling true async HTTP I/O and removing the problematic executor-based async wrappers that blocked the event loop. The migration is comprehensive, covering both sync and async clients, authentication helpers, tests, and documentation.
Changes:
- Replaced
requestswithhttpxfor HTTP operations (sync:httpx.Client, async:httpx.AsyncClient) - Removed executor-based async wrappers (
run_async,generate_async) in favor of native async HTTP calls - Migrated test mocking from
responsestorespx
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Added httpx dependencies (httpx, httpcore, h11, anyio); removed requests and types-requests; replaced responses with respx |
| pyproject.toml | Updated dependencies: httpx>=0.28.1 instead of requests; respx instead of responses; removed types-requests |
| todoist_api_python/types.py | New shared type aliases module for ColorString, LanguageCode, ViewStyle (extracted from api.py) |
| todoist_api_python/_core/type_aliases.py | Re-exports type aliases from types.py for backwards compatibility |
| todoist_api_python/_core/utils.py | Removed run_async and generate_async functions; added kwargs_without_none helper |
| todoist_api_python/_core/http_requests.py | Complete rewrite using httpx.Client/AsyncClient; added true async variants (get_async, post_async, delete_async); uses httpx.Timeout instead of tuple |
| todoist_api_python/_core/http_headers.py | Removed Content-Type header creation (httpx handles this automatically with json= parameter) |
| todoist_api_python/api.py | Updated to use httpx.Client; replaced manual dict building with kwargs_without_none; removed noqa PLR0912 comments |
| todoist_api_python/api_async.py | Complete rewrite with true async HTTP; added proper client lifecycle management (aenter, aexit, close, del with ResourceWarning); uses AsyncIterator instead of AsyncGenerator; implements AsyncResultsPaginator |
| todoist_api_python/authentication.py | Migrated to httpx with context managers for client lifecycle; added async variants; uses _managed_client and _managed_async_client helpers |
| tests/* | Migrated all tests from responses to respx; updated mocking utilities; added async client lifecycle test; fixtures now use context managers |
| docs/* | Added async usage guidance; documented breaking changes; updated authentication examples |
| README.md | Added migration guide from 3.x to 4.x; documented async client lifecycle requirements |
| CHANGELOG.md | Documented all breaking changes for version 4.0.0 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lefcha
reviewed
Feb 16, 2026
lefcha
reviewed
Feb 16, 2026
lefcha
reviewed
Feb 16, 2026
lefcha
reviewed
Feb 16, 2026
lefcha
approved these changes
Feb 16, 2026
Collaborator
lefcha
left a comment
There was a problem hiding this comment.
Overall looks good. I only had some comments about minor things (see inline comments).
Switch TodoistAPI and authentication sync paths from requests\nto httpx.Client and align sync error semantics with\nhttpx.HTTPStatusError.\n\nUpdate sync-focused tests to use the respx-backed request\nmocking setup.
Update docs and changelog to describe the requests-to-httpx\ntransition, constructor client arguments, and new HTTP error\nhandling guidance.
Use declarative respx lookups (params__eq, headers__contains, json__eq) via mock_route request_* arguments and response_* payload arguments. Set API fixtures to deterministic request IDs so request-header assertions remain fully declarative and consistent across sync and async tests.
Add a pure kwargs_without_none helper and reuse it across TodoistAPI and TodoistAPIAsync to reduce duplicated None-filtering logic in request params/data preparation.
70708cf to
af93217
Compare
No more blind type casting and type witnesses.
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.
Summary
This PR migrates the SDK HTTP stack from
requeststohttpxacross async AND sync clients.What changed
httpx:TodoistAPI(..., client: httpx.Client | None = None)TodoistAPIAsync(..., client: httpx.AsyncClient | None = None)httpx(get_auth_token,revoke_auth_token, async variants)._core/http_requests.py) tohttpx.responses/requests-style mocks torespx.Breaking changes
sessionconstructor arg is replaced byclient(no compatibility alias):TodoistAPI(session=...)->TodoistAPI(client=...)TodoistAPIAsync(session=...)->TodoistAPIAsync(client=...)httpx.Client/httpx.AsyncClientinstances instead ofsession: requests.Session.httpx.HTTPStatusError.Next steps
Major version bump and release 4.0.0.