Skip to content

Commit f76d107

Browse files
committed
docs: broken code blocks in docs/source/async.rst; add Manuel tests for them
The three runnable code blocks in docs/source/async.rst were broken: 1. `async with aio.get_async_davclient() as client:` fails with "coroutine object does not support the asynchronous context manager protocol" because get_async_davclient is an async function and must be awaited before it can be used as a context manager. Fixed to `async with await aio.get_async_davclient() as client:`. Same bug was present in the Quick Start, the "Working with Calendars", and the "Parallel Operations" examples, and in the Migration section. 2. `cal.name` triggers a DeprecationWarning (use get_display_name() instead); replaced with `await cal.get_display_name()` in the two code blocks that used it. The three runnable code blocks are now covered by the existing Manuel- based test harness: added `test_async_ref = manueltest(...)` to tests/test_docs.py (also fixed the double-assignment bug that made test_async_tutorial silently shadow test_tutorial). Confirmed tests fail before fix and pass after. Fixes: #661 prompt: Why is the "quick start" in docs/source/async.rst failing? followup-prompt: We need the code blocks in the documentation to be exercised by test code whenever possible. We already have some tests covering the tutorial. See if it's possible to get the code blocks that was changed just now tested. Revert the docfixes to verify that the tests are working correctly. Docfixes plus tests should be in the same commit, and remember to add prompts to the commit message. followup-prompt: Add a reference to #661 in the commit message
1 parent 9515bae commit f76d107

2 files changed

Lines changed: 18 additions & 7 deletions

File tree

docs/source/async.rst

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ The caldav library provides an async-first API for use with Python's
99
* Integrate with async web frameworks (FastAPI, aiohttp, etc.)
1010
* Build responsive applications that don't block on I/O
1111

12+
=======
13+
Caveats
14+
=======
15+
16+
Async IO was introduced in version 3.0, 2026-03-03, without being tested in any production environments, and it was done by a developer not having much experience with async usage. Rough edges are to be expected. Test it very well in a staging environment before using it in production environments. It's probably a good idea to wait some few releases before using it in sharp production settings.
17+
18+
A "Sans-IO" design pattern was followed, in a hope that it would make it possible to have one library serve both the async and sync use case through relatively similar APIs without duplicating too much code. In retro-perspective I'm not sure this was the best idea for the CalDAV library. Be aware that there are still exists code paths that works well with the sync code but will blow up if you try using it with the async code.
19+
20+
Async works very well when it's crispy clear what operations causes API calls. I've been a bit careless with the old sync library, there are many places where an API call is not expected, but anyway there are things like ``self.load(only_if_unloaded=True)`` buried in the code. Works well with sync, not so much with the async code. In a 4.0-version (perhaps 2027?) there may be some major changes to the API.
21+
1222
Quick Start
1323
===========
1424

@@ -20,11 +30,11 @@ The async API is available through the ``caldav.aio`` module:
2030
from caldav import aio
2131
2232
async def main():
23-
async with aio.get_async_davclient() as client:
33+
async with await aio.get_async_davclient() as client:
2434
principal = await client.get_principal()
2535
calendars = await principal.get_calendars()
2636
for cal in calendars:
27-
print(f"Calendar: {cal.name}")
37+
print(f"Calendar: {await cal.get_display_name()}")
2838
events = await cal.get_events()
2939
print(f" {len(events)} events")
3040
@@ -72,7 +82,7 @@ Example: Working with Calendars
7282
from datetime import datetime, date
7383
7484
async def calendar_demo():
75-
async with aio.get_async_davclient() as client:
85+
async with await aio.get_async_davclient() as client:
7686
principal = await client.get_principal()
7787
7888
# Create a new calendar
@@ -112,7 +122,7 @@ concurrently:
112122
from caldav import aio
113123
114124
async def fetch_all_events():
115-
async with aio.get_async_davclient() as client:
125+
async with await aio.get_async_davclient() as client:
116126
principal = await client.get_principal()
117127
calendars = await principal.get_calendars()
118128
@@ -121,7 +131,7 @@ concurrently:
121131
results = await asyncio.gather(*tasks)
122132
123133
for cal, events in zip(calendars, results):
124-
print(f"{cal.name}: {len(events)} events")
134+
print(f"{await cal.get_display_name()}: {len(events)} events")
125135
126136
asyncio.run(fetch_all_events())
127137
@@ -150,7 +160,7 @@ The async API closely mirrors the sync API. Here are the key differences:
150160
...
151161
152162
# Async
153-
async with aio.get_async_davclient() as client:
163+
async with await aio.get_async_davclient() as client:
154164
...
155165
156166
3. **Await all I/O operations:**

tests/test_docs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ def tearDown(self):
3333
os.environ.pop("PYTHON_CALDAV_USE_TEST_SERVER", None)
3434

3535
test_tutorial = manueltest("../docs/source/tutorial.rst")
36-
test_tutorial = manueltest("../docs/source/async_tutorial.rst")
36+
test_async_tutorial = manueltest("../docs/source/async_tutorial.rst")
37+
test_async_ref = manueltest("../docs/source/async.rst")

0 commit comments

Comments
 (0)