Skip to content

Add fully async SDK on httpx and anyio - #176

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

Xharry01X wants to merge 13 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.

Note on the contributing guide: this repository is auto-generated by the SDK Generator and does not accept external pull requests directly. This PR is opened 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

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: 1008 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.
@hansi-codes

hansi-codes Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🔵 Tier A · Mergeable after minor fixes

The async upload callback failure path can cancel sibling requests, and the README error example references an out-of-scope variable.

This PR converts the Python SDK services and generated examples to async APIs backed by an httpx2/AnyIO transport. It adds async client lifecycle management, redirect handling, progress callbacks, chunked-upload coordination, dependency updates, and async test coverage.

933 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: Progress callback errors cancel sibling uploads

Finding Where
🟡 Progress callback errors cancel sibling uploads appwrite/client.py:404
🟡 The error example uses an out-of-scope service README.md:139
Fix with agent prompt
### Issue 1
appwrite/client.py:404
**Progress callback errors cancel sibling uploads**

If an async progress callback raises while other chunks are in flight, this exception escapes the worker and AnyIO cancels its sibling tasks. The previous executor waited for all futures, so this path can abort other chunk requests instead of letting them settle before the error is surfaced.

### Issue 2
README.md:139
**The error example uses an out-of-scope service**

The preceding request example now creates `users` inside its `main()`, so this later snippet references an undefined name when following the README examples as written. Create the service in this example or keep it in a scope shared with it.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
📂 Walkthrough · 8
File Change
appwrite/client.py Adds the async HTTP client, async lifecycle methods, and AnyIO chunked uploads.
appwrite/services/*.py Converts generated service methods to coroutines that await client requests.
appwrite/utils/deprecated.py Supports deprecated async methods and protects warning-filter changes with a lock.
README.md Updates setup and usage examples for async calls and client cleanup.
docs/examples/**/*.md Converts generated API examples to await service calls.
test/**/*.py Adapts service tests and adds async client, deprecation, and integration coverage.
pyproject.toml, setup.py, requirements.txt Updates Python requirements and adds async transport and test dependencies.
CHANGELOG.md, .gitignore Documents async breaking changes and adds Python development ignores.

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

@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.

@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
last_result = chunk_result
if is_upload_complete(chunk_result):
final_result = chunk_result
await self._report_progress(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Progress callback errors cancel sibling uploads

If an async progress callback raises while other chunks are in flight, this exception escapes the worker and AnyIO cancels its sibling tasks. The previous executor waited for all futures, so this path can abort other chunk requests instead of letting them settle before the error is surfaced.

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

Comment:
**Progress callback errors cancel sibling uploads**

If an async progress callback raises while other chunks are in flight, this exception escapes the worker and AnyIO cancels its sibling tasks. The previous executor waited for all futures, so this path can abort other chunk requests instead of letting them settle before the error is surfaced.

---

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 README.md

async def main():
try:
user = await users.get(user_id='<USER_ID>')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The error example uses an out-of-scope service

The preceding request example now creates users inside its main(), so this later snippet references an undefined name when following the README examples as written. Create the service in this example or keep it in a scope shared with it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: README.md
Line: 139

Comment:
**The error example uses an out-of-scope service**

The preceding request example now creates `users` inside its `main()`, so this later snippet references an undefined name when following the README examples as written. Create the service in this example or keep it in a scope shared with it.

---

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

🟡 Minor · documentation · 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