Repository navigation
Conversation
The Python SDK is the last server SDK that blocks on a synchronous transport. Move it to `httpx2` and `anyio` so every call is a coroutine and the SDK runs on both asyncio and trio. - The client owns a pooled `httpx2.AsyncClient`, supports `async with` and `aclose()`, and swaps the pool out when `set_self_signed()` changes the TLS settings - Chunked uploads run in an AnyIO task group with a semaphore. Every sibling chunk settles before a failure surfaces, so an error in one chunk never cancels the uploads that are still running, and a progress callback that raises is captured with the other failures - Service methods become `async def` and await the client - The `@deprecated` decorator keeps coroutine functions coroutines and guards the temporary warning filter with a lock - Docs examples await the call and run inside an async main - Dependencies move from `requests` to `httpx2` and `anyio`, and the generated tests mock the transport with `respx` - The minimum Python version moves to 3.11, which `httpx2` requires, so the 3.10 e2e job is dropped
🟢 Tier S · Ready to mergeThis PR adds an opt-in asynchronous Python SDK alongside the existing synchronous client and services, with generated async tests, optional dependencies, and Python runtime coverage. The latest updates make async redirect handling return location responses correctly, serialize chunk-upload progress callbacks, preserve cancellation during upload resumption, and show a runnable async README example. Latest changes: The newest commits distinguish redirect responses from HTTP errors, serialize progress updates during concurrent chunk uploads, allow cancellation to propagate during upload resumption, and wrap the README async usage in an entry point.
Note @Xharry01X does not have write access to this repository, so Hansi does not approve automatically. A maintainer can review and approve. 📂 Walkthrough · 16
✅ Fixed since the last review · 2
Reviewed the commits since |
httpx2 raises for 3xx responses, so the redirect error and the OAuth Location were never reached. Let a cancellation escape the upload resume probe, and serialize the async progress callbacks so they never overlap and always report the latest state.
The findings that requested changes are resolved. See the summary comment for what is still open.
|
Addressed the three review findings in 1dfb764.
Validation of the regenerated SDK:
The |
|
Thanks for the thorough work here. The async client itself looks solid, and the review fixes in 1dfb764 are right. We'd like async to be opt-in rather than replacing the sync client, though. Turning every service method into a coroutine breaks every existing integration (scripts, Django views, Celery tasks), and the |
|
Is that the only thing you'll need? Should be doable with Anyone who needs the sync version will be able to use it as before. We'll update to the latest SDK as well since we already have some conflicts. |
# Conflicts: # tests/e2e/languages/python/tests.py
The async client replaces the sync one instead of sitting next to it, which breaks every existing integration. Ship both instead: the sync `Client` and its services stay the default and are generated exactly as before, and `AsyncClient` plus the `Async*` services are added next to them as an opt-in. - The service template is shared by both, with the class name, the `async def` and the awaited client call driven by one flag - The async client keeps the pooled `httpx2` transport, the AnyIO task group for chunked uploads and the `async with`/`aclose()` lifecycle - `httpx2` and `anyio` ship in the `async` extra, so the base install and the 3.10 runtime are unchanged - The async suite mocks the transport with `respx` and runs in the e2e job on the Python versions that can install `httpx2`
The async client template was taken from the revision before the review fixes landed on the client. - A 3xx response is reported before the status is raised, so a web-auth call with response_type='location' returns the Location header again - The upload resume probe no longer swallows a cancellation, which is a BaseException and would restart the upload from chunk zero - The chunk workers take an anyio.Lock around the counters and the progress callback, the way the threaded client took its threading.Lock - The README example awaits inside an async main, so it is valid Python
The findings that requested changes are resolved. See the summary comment for what is still open.
What does this PR do?
Adds an asynchronous client to the generated Python SDK, next to the existing synchronous one.
The synchronous
Clientand the services stay the default and are generated exactly as before, so nothing changes for existing users. The asynchronous client is opt-in and ships with theasyncextra:Changes
Sync stays the default
templates/python/package/client.py.twigand the service template are unchanged, and the generated sync SDK is byte-identical tomainappwrite/utils/deprecated.py, whose@deprecateddecorator now keeps coroutine functions coroutinesAsync is added alongside
templates/python/base/service_class.twigholds the shared service body. One flag drives the class name (Asyncprefix),async defand the awaited client call, so the two trees cannot drift aparttemplates/python/package/async_client.py.twiggeneratesappwrite/async_client.pywithAsyncClienttemplates/python/package/async_services/service.py.twiggeneratesappwrite/async_services/<service>.pywithAsync<Service>httpx2.AsyncClientinstead of usingrequestsper call, supportsasync withandaclose(), and swaps the pool out whenset_self_signed()changes the TLS settingsChunked upload
ThreadPoolExecutoris replaced by an AnyIO task group with a semaphoreDependencies and tests
httpx2andanyioship in the newasyncextra. The base install keepsrequestsandurllib3, so Python 3.10 is still supported and the sync suite is unchangedtest_asyncholds the asynchronous suite, which mocks the transport withrespxand runs onunittest.IsolatedAsyncioTestCase.testis unchangedhttpx2Testing
php example.php python server, then the generated suites: 884 sync tests and 781 async tests, all passingblack --checkon the generated output: 553 files unchangedcomposer lint-twig: 641 templates, 0 errorscomposer refactor:check: cleanmainand is byte-identical apart from the additions listed abovetests/generation/GenerationTest.phpwere checked against the generated fixtureThe e2e job needs Docker, so it was not run locally.
Related
The same work is open against the generated SDK for review: appwrite/sdk-for-python#177