Skip to content

Add fully async SDK on httpx and anyio - #177

Closed
Xharry01X wants to merge 15 commits into
appwrite:mainfrom
Xharry01X:feat/async-migration
Closed

Xharry01X wants to merge 15 commits into
appwrite:mainfrom
Xharry01X:feat/async-migration

Conversation

@Xharry01X

Copy link
Copy Markdown

What does this PR do?

Fully migrates the generated Python SDK to an async-first design on top of httpx and anyio, replacing the previous synchronous implementation.

Supersedes #176, which was auto-closed before the review fixes could land in its head. This PR is based on the same branch and includes the fixes addressing both review comments:

  • A raising async progress callback no longer cancels in-flight sibling chunk uploads; the error is captured with the other failures so every chunk settles before it surfaces
  • The README "Handle errors" example now creates the users service it uses

Note on the contributing guide: this repository is auto-generated by the SDK Generator and does not accept external pull requests directly. This PR serves as a reviewable reference implementation for the corresponding template work in appwrite/sdk-generator (Python Language class + Twig templates). Happy to continue the effort there, in an issue, or in the Appwrite Discord.

Changes

  • Replace the sync transport with async httpx and swap asyncio for anyio, so the SDK runs on both asyncio and trio
  • Regenerate all service classes, models and docs examples as async
  • Allow redirects for location-style responses (OAuth flows, file downloads)
  • Wait for all sibling chunks to finish when a chunked upload fails, so no orphaned tasks leak
  • Make the deprecation-warning filters thread-safe
  • Convert deprecation warning docstrings to plain comments so Sphinx does not pick them up as return docs
  • Add anyio to the package dependencies
  • Tests: adapt mocks for respx + async transport and add async client, deprecation and integration test suites, including regression coverage for sibling chunks when the progress callback fails

Breaking changes

  • All client and service calls are now async and must be awaited; migration notes are documented in the README

Testing

  • python -m unittest discover -s test locally: 1009 tests, all passing
  • The branch is based on main before the 27.1.0 regeneration, so it will need a rebase/regeneration pass once the overall approach is reviewed

Use return_exceptions with asyncio.gather so a failed chunk's
error is not raised while other chunk uploads are still in
flight.
Replaces asyncio.gather/to_thread with anyio task groups and
to_thread.run_sync. Retired HTTP clients are now closed in aclose()
since anyio has no fire-and-forget tasks. Adds anyio dependency.
Emit warnings in a scoped context so caller filters are preserved. Add
tests
for sync and async decorators, warning attribution, and filter
restoration.
Require all service methods, Client.call, and Client.chunked_upload to
be
awaited. Coroutine on_progress callbacks are also awaited.

The SDK now requires Python 3.11 and uses httpx2 instead of requests.
Client.aclose() and async context manager support are also documented.
Alias httpx to httpx2 before importing respx for root, service, and
standalone test discovery.

Treat falsey Appwrite Cloud error codes as unreachable responses.
The async client imports anyio, so an install without it fails at
import time.
Every service method is a coroutine now, so the snippets await the
call and run their main function.
A raising async progress callback escaped the chunk worker and
cancelled in-flight sibling uploads. Capture it with the other
failures so every chunk settles before the error surfaces.
@github-actions

Copy link
Copy Markdown

This library is auto-generated by the Appwrite SDK Generator, and does not accept pull requests directly. To learn more about how you can help us improve this SDK, please check the contributing guide.

@hansi-codes

hansi-codes Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🔵 Tier A · Mergeable after minor fixes

The async progress callbacks can race and the advertised Trio backend has no test coverage; both are limited-scope issues.

The SDK now uses an async HTTP transport and AnyIO-based chunk uploads, with service methods and examples converted to awaitable calls. The client gains async cleanup and context-manager support, while packaging metadata, deprecation handling, documentation, and tests are updated for the new design.

934 of 950 changed files were too large to include in full.

Verdict New comments Fixed Still open
💬 Commented 2 0 0

Note

Not approving while a bug finding is open: Serialize async progress callbacks

Finding Where
🟡 Serialize async progress callbacks appwrite/client.py:402
🟡 Exercise the Trio backend in tests appwrite/client.py:418
Fix with agent prompt
### Issue 1
appwrite/client.py:402-411
**Serialize async progress callbacks**

Previously, `progress_lock` held through each callback; these awaited callbacks can now overlap across chunk tasks and finish out of order. A callback that forwards progress to a UI or service can apply an older `sizeUploaded` after a newer update, or race on its own shared state.

### Issue 2
appwrite/client.py:418-420
**Exercise the Trio backend in tests**

The new async tests use `IsolatedAsyncioTestCase` and `asyncio`, so this task-group path is never exercised on Trio. Because Trio support is an explicit behavior change, a Trio-backed request or chunk-upload test would catch backend-specific incompatibilities that the current suite cannot.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
📂 Walkthrough · 6
File Change
appwrite/client.py Adds the async HTTP client lifecycle, request handling, redirect support, and concurrent chunk uploads.
appwrite/services/*.py Converts service methods and upload entry points to async calls.
appwrite/utils/deprecated.py Adds async-aware deprecation warnings with a guarded warning-filter update.
README.md, CHANGELOG.md, docs/examples/**/*.md Documents the async API, client cleanup, migration, and updated examples.
pyproject.toml, setup.py, requirements.txt, .gitignore Updates runtime requirements and Python version metadata, and adds ignore rules.
test/**/*.py Updates transport mocks and adds async client, upload, and integration coverage.

Reviewed 9c1ff16 · Details · Comment @hansi-codes review to re-run, or mention @hansi-codes with a question.

@github-actions github-actions Bot closed this Oct 28, 2026

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Tier A · See the inline comments. Summary

Comment thread appwrite/client.py
Comment on lines +402 to +411
await self._report_progress(
on_progress,
{
"$id": upload_id_header,
"progress": uploaded_size / size * 100,
"sizeUploaded": uploaded_size,
"chunksTotal": total_chunks,
"chunksUploaded": completed_count,
},
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serialize async progress callbacks

Previously, progress_lock held through each callback; these awaited callbacks can now overlap across chunk tasks and finish out of order. A callback that forwards progress to a UI or service can apply an older sizeUploaded after a newer update, or race on its own shared state.

Prompt To Fix With AI
This is a comment left during a code review.
Path: appwrite/client.py
Line: 402-411

Comment:
**Serialize async progress callbacks**

Previously, `progress_lock` held through each callback; these awaited callbacks can now overlap across chunk tasks and finish out of order. A callback that forwards progress to a UI or service can apply an older `sizeUploaded` after a newer update, or race on its own shared state.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · concurrency · Reply if this doesn't apply.

Comment thread appwrite/client.py
Comment on lines +418 to +420
async with anyio.create_task_group() as tg:
for chunk in chunks[1:]:
tg.start_soon(upload_remaining_chunk, chunk)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exercise the Trio backend in tests

The new async tests use IsolatedAsyncioTestCase and asyncio, so this task-group path is never exercised on Trio. Because Trio support is an explicit behavior change, a Trio-backed request or chunk-upload test would catch backend-specific incompatibilities that the current suite cannot.

Prompt To Fix With AI
This is a comment left during a code review.
Path: appwrite/client.py
Line: 418-420

Comment:
**Exercise the Trio backend in tests**

The new async tests use `IsolatedAsyncioTestCase` and `asyncio`, so this task-group path is never exercised on Trio. Because Trio support is an explicit behavior change, a Trio-backed request or chunk-upload test would catch backend-specific incompatibilities that the current suite cannot.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · testing · Reply if this doesn't apply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant