Skip to content

Latest commit

 

History

History
236 lines (165 loc) · 6.4 KB

File metadata and controls

236 lines (165 loc) · 6.4 KB

Async API

The caldav library provides an async-first API for use with Python's asyncio. This is useful when you need to:

  • Make concurrent requests to the server
  • Integrate with async web frameworks (FastAPI, aiohttp, etc.)
  • Build responsive applications that don't block on I/O

Quick Start

The async API is available through the caldav.aio module:

import asyncio
from caldav import aio

async def main():
    async with aio.get_async_davclient() as client:
        principal = await client.get_principal()
        calendars = await principal.get_calendars()
        for cal in calendars:
            print(f"Calendar: {cal.name}")
            events = await cal.get_events()
            print(f"  {len(events)} events")

asyncio.run(main())

The async API mirrors the sync API, but all I/O operations are async methods that must be awaited.

Available Classes

The caldav.aio module exports:

Client:

  • AsyncDAVClient - The main client class
  • AsyncDAVResponse - Response wrapper
  • get_async_davclient() - Factory function (recommended)

Calendar Objects:

  • AsyncEvent - Calendar event
  • AsyncTodo - Task/todo item
  • AsyncJournal - Journal entry
  • AsyncFreeBusy - Free/busy information

Collections:

  • AsyncCalendar - A calendar
  • AsyncCalendarSet - Collection of calendars
  • AsyncPrincipal - User principal

Scheduling (:rfc:`6638`):

  • AsyncScheduleInbox - Incoming invitations
  • AsyncScheduleOutbox - Outgoing invitations

Example: Working with Calendars

import asyncio
from caldav import aio
from datetime import datetime, date

async def calendar_demo():
    async with aio.get_async_davclient() as client:
        principal = await client.get_principal()

        # Create a new calendar
        my_calendar = await principal.make_calendar(
            name="My Async Calendar"
        )

        # Add an event
        event = await my_calendar.add_event(
            dtstart=datetime(2025, 6, 15, 10, 0),
            dtend=datetime(2025, 6, 15, 11, 0),
            summary="Team meeting"
        )

        # Search for events
        events = await my_calendar.search(
            event=True,
            start=date(2025, 6, 1),
            end=date(2025, 7, 1)
        )
        print(f"Found {len(events)} events")

        # Clean up
        await my_calendar.delete()

asyncio.run(calendar_demo())

Example: Parallel Operations

One of the main benefits of async is the ability to run operations concurrently:

import asyncio
from caldav import aio

async def fetch_all_events():
    async with aio.get_async_davclient() as client:
        principal = await client.get_principal()
        calendars = await principal.get_calendars()

        # Fetch events from all calendars in parallel
        tasks = [cal.get_events() for cal in calendars]
        results = await asyncio.gather(*tasks)

        for cal, events in zip(calendars, results):
            print(f"{cal.name}: {len(events)} events")

asyncio.run(fetch_all_events())

Migration from Sync to Async

The async API closely mirrors the sync API. Here are the key differences:

  1. Import from ``caldav.aio``:

    # Sync
    from caldav import DAVClient, get_davclient
    
    # Async
    from caldav import aio
    # Use: aio.AsyncDAVClient, aio.get_async_davclient()
  2. Use ``async with`` for context manager:

    # Sync
    with get_davclient() as client:
        ...
    
    # Async
    async with aio.get_async_davclient() as client:
        ...
  3. Await all I/O operations:

    # Sync
    principal = client.get_principal()
    calendars = principal.get_calendars()
    events = calendar.get_events()
    
    # Async
    principal = await client.get_principal()
    calendars = await principal.get_calendars()
    events = await calendar.get_events()
  4. Property access for cached data remains sync:

    Properties that don't require I/O (like url, name, data) are still regular properties:

    # These work the same in both sync and async
    print(calendar.url)
    print(calendar.name)
    print(event.data)

Method Reference

The async classes have the same methods as their sync counterparts. All methods that perform I/O are async and must be awaited:

AsyncDAVClient:

  • await client.get_principal() - Get the principal
  • client.calendar(url=...) - Get a calendar by URL (no await, no I/O)

AsyncPrincipal:

  • await principal.get_calendars() - List all calendars
  • await principal.make_calendar(name=...) - Create a calendar
  • await principal.calendar(name=...) - Find a calendar

AsyncCalendar:

  • await calendar.get_events() - Get all events
  • await calendar.get_todos() - Get all todos
  • await calendar.search(...) - Search for objects
  • await calendar.add_event(...) - Create an event
  • await calendar.add_todo(...) - Create a todo
  • await calendar.get_event_by_uid(uid) - Find event by UID
  • await calendar.delete() - Delete the calendar
  • await calendar.get_supported_components() - Get supported types

AsyncEvent, AsyncTodo, AsyncJournal:

  • await obj.load() - Load data from server
  • await obj.save() - Save changes to server
  • await obj.delete() - Delete the object
  • await todo.complete() - Mark todo as complete

Backward Compatibility

The sync API (caldav.DAVClient, caldav.get_davclient()) continues to work exactly as before. The sync API now uses the async implementation internally, with a thin sync wrapper.

This means:

  • Existing sync code works without changes
  • You can migrate to async gradually
  • Both sync and async code can coexist in the same project

Example Files

See the examples/ directory for complete examples:

  • examples/async_usage_examples.py - Comprehensive async examples
  • examples/basic_usage_examples.py - Sync examples (for comparison)