====================
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:

.. code-block:: python

    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
===============================

.. code-block:: python

    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:

.. code-block:: python

    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``:**

   .. code-block:: python

       # 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:**

   .. code-block:: python

       # Sync
       with get_davclient() as client:
           ...

       # Async
       async with aio.get_async_davclient() as client:
           ...

3. **Await all I/O operations:**

   .. code-block:: python

       # 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:

   .. code-block:: python

       # 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)
