caldav Python Client Library

repository·master·Indexed 19 days ago

https://github.com/python-caldav/caldav

A Python client library for the CalDAV protocol (RFC4791) that allows developers to create, modify, delete, and search for calendars and events. It features a Sans-I/O architecture to support both synchronous (DAVClient) and asynchronous (AsyncDAVClient via caldav.aio) operations, utilizing HTTP libraries such as niquests, httpx, and requests.

Tokens
70.8K
Snippets
195
Records
306
Agent score
64%

What's inside caldav

  1. Overview of urllib3.future

    master

    urllib3.future is an advanced fork of urllib3 designed as a powerful, user-friendly HTTP client for Python. It provides native support for multiple protocols and is intended as a drop-in replacement for existing urllib3 implementations (using the version 2.x.9PP scheme).

    Key Features:

    • Protocol Support: Native HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) via h11, jh2, and qh3 respectively.
    • Automatic Negotiation: Transparent ALPN negotiation and automatic HTTP/3 upgrades via Alt-Svc header handling.
    • Connection Management: Sophisticated connection pooling with multiplexing support and background keep-alive management.
    • Advanced DNS: Pluggable resolver architecture supporting DOH (DNS over HTTPS), DOQ (DNS over QUIC), DOT (DNS over TLS), and DOU (DNS over UDP).
    • Compatibility: Maintains API compatibility with standard urllib3 while extending functionality.
  2. Overview of Niquests HTTP Client

    master

    Niquests is a modern Python HTTP client library designed as a drop-in replacement for the requests library. It maintains full API compatibility with requests while providing advanced features including:

    • Protocol Support: HTTP/2 and HTTP/3 over QUIC.
    • Concurrency: Native async/await support.
    • Security: Enterprise-grade security, including OS truststore usage by default (avoiding outdated certifi bundles) and OCSP/CRL support for certificate revocation.
    • Compatibility: Supports Python 3.7+ (including Python 3.14 and PyPy).

    Note: When trust_env=True is used, the library reads from .netrc files.

  3. Current CalDAV Library Capabilities

    master

    As of the current roadmap, the caldav library already provides the following core functionalities:

    • Core CalDAV (RFC 4791): Basic calendar operations.
    • Basic Scheduling (RFC 6638): Initial support for scheduling events.
    • Service Discovery (RFC 6764): Locating CalDAV services.
    • WebDAV Sync (RFC 6578): Synchronizing calendar data.
    • Extensive Search: Capabilities for querying calendar data.
    • Async Support: Asynchronous API for non-blocking operations.
  4. Compare Sync and Async Client Architectures

    master

    The library provides two primary client paths. While they share underlying logic via caldav/base_client.py, they differ in their I/O implementation and API signatures.

    Sync Client (caldav/davclient.py)

    • Usage: Standard synchronous blocking calls.
    • Note: The propfind API accepts a props parameter which can be either an XML string or a property list.

    Async Client (caldav/async_davclient.py)

    • Usage: Non-blocking asynchronous calls.
    • Note: The propfind API uses separate body and props parameters, differing from the sync implementation.
    • Rate Limiting: Implements an adaptive backoff loop. If a request is rate-limited, it sleeps and retries recursively using self.request().
  5. Understanding WebDAV method wrappers and the request pattern

    master

    In caldav, WebDAV methods like PROPFIND, REPORT, PROPPATCH, MKCOL, and MKCALENDAR are often implemented as thin wrapper methods on the DAVClient class. These wrappers serve as adapters that call a core request() method while automatically injecting method-specific HTTP headers (such as Depth or Content-Type).

    While these wrappers provide a discoverable and convenient public API, the internal DAVObject._query() method should ideally call the low-level request() method directly rather than relying on these wrappers to avoid unnecessary dynamic dispatch and boilerplate.

    def propfind(self, url=None, props="", depth=0):
        return self.request(
            url or str(self.url),
            "PROPFIND",
            props,
            {"Depth": str(depth)}
        )
  6. Understand the core CalDAV workflow and classes

    master

    The library follows a hierarchical object model to interact with calendar servers. The typical workflow is:

    1. Initialize a Client: Start with a caldav.DAVClient (or use the recommended caldav.get_davclient() function) which holds authentication details.
    2. Access the Principal: From the client, obtain a caldav.Principal object representing the logged-in user.
    3. Access Calendars: From the principal, fetch or generate caldav.Calendar objects.
    4. Manage Calendar Objects: From a calendar, interact with caldav.Event, caldav.Todo, or caldav.Journal objects.

    Note: If you know the specific URLs, you can instantiate Calendar, Principal, or Event objects directly using their path, relative URL, or full URL (without authentication details).

    import caldav
    
    # Recommended way to get a client
    client = caldav.get_davclient(url='https://example.com/caldav', username='user', password='password')
    
    # Workflow
    principal = client.principal
    calendars = principal.calendars()
    calendar = calendars[0]
    events = calendar.events()
  7. Understand the urllib3.future project structure

    master

    The project is organized into core synchronous modules, an asynchronous mirror, and various extensions. Use this map to locate specific functionality:

    • Core (Sync): Located in src/urllib3/. Includes connection.py, connectionpool.py, poolmanager.py, and response.py.
    • Backend: src/urllib3/backend/ contains the protocol handling logic (e.g., hface.py).
    • Async Support: An asynchronous mirror of the core modules is located in src/urllib3/_async/.
    • Extensions (contrib/):
      • hface/: HTTP protocol implementations (http1, http2, http3).
      • resolver/: Advanced DNS resolution (doh, doq, dot, dou).
      • webextensions/: WebSocket and SSE support.
      • socks.py: SOCKS proxy support.
    • Utilities (util/): Includes traffic_police.py (connection queueing), ssl_.py, timeout.py, and retry.py.
    src/urllib3/
    ├── Core modules (sync):
    │   ├── connection.py
    │   ├── connectionpool.py
    │   ├── poolmanager.py
    │   ├── response.py
    │   └── backend/
    │       ├── _base.py
    │       └── hface.py
    │   └── _async/ 
    ├── _async/ 
    └── contrib/
        ├── hface/
        ├── resolver/
        ├── webextensions/
        ├── socks.py
        └── pyopenssl.py
    └── util/
        ├── traffic_police.py
        ├── ssl_.py
        ├── timeout.py
        └── retry.py
  8. Perform parallel I/O operations with asyncio.gather

    master

    The primary advantage of the async API is the ability to perform multiple I/O operations concurrently. You can use asyncio.gather to trigger multiple requests (like searching multiple calendars) at once, significantly improving performance when dealing with multiple servers or many independent fetches.

    import asyncio
    from caldav import aio
    
    async def main():
        async with await aio.get_calendars() as calendars:
            # Kick off all searches in parallel
            results = await asyncio.gather(
                *[cal.search(event=True) for cal in calendars]
            )
    
            for cal, events in zip(calendars, results):
                print(f"{await cal.get_display_name()}: {len(events)} event(s)")
    
    asyncio.run(main())
  9. Understand data representation properties on calendar objects

    master

    When working with calendar objects in caldav, you can access the underlying calendar data through several different properties depending on whether you need raw strings, icalendar objects, or vobject objects. These properties are defined in caldav/calendarobjectresource.py.

    Available Properties

    PropertyTypeDescription
    dataproperty()The string representation of the calendar data (e.g., iCalendar format string).
    wire_dataproperty()The raw data in its wire format.
    vobject_instanceproperty()The object representation using the vobject library.
    instanceproperty()Alias for vobject_instance.
    icalendar_instanceproperty()The full calendar object using the icalendar library.
    icalendar_componentproperty()The inner component (e.g., VEVENT, VTODO, or VJOURNAL) using the icalendar library.
    componentN/AAlias for icalendar_component.

    When to use which property

    • Use data when you need the raw iCalendar string to perform text-based operations (like count("BEGIN:VEVENT")) or when you need to pass the data to other functions that expect a string.
    • Use icalendar_instance when you need to manipulate the entire calendar structure or iterate over subcomponents using the icalendar library.
    • Use icalendar_component (or component) when you want to access or modify specific fields of a single event, task, or journal (e.g., UID, SUMMARY, DTSTART, RRULE, or STATUS).
    • Use vobject_instance (or instance) when your workflow requires the vobject library's object model.
  10. Understand HTTP method wrappers and dynamic dispatch

    master

    The caldav library uses HTTP method wrappers (like propfind, put, delete) to provide a clean API and handle method-specific concerns like headers (e.g., the Depth header for PROPFIND and REPORT).

    Crucially, the library uses dynamic method dispatch via DAVObject._query(). This means the library looks up these methods by name at runtime using getattr(self.client, query_method). Because of this, these wrappers are essential for the internal operation of the library and should not be bypassed or removed when interacting with the client's core logic.

  11. Understand the caldav architecture and layers

    master

    The caldav library is organized into four distinct layers that separate user logic from network I/O:

    1. Domain Objects (Dual-Mode): High-level objects like Calendar, Principal, Event, Todo, Journal, and FreeBusy. These objects are 'dual-mode', meaning they automatically detect if they are being used with a synchronous DAVClient or an asynchronous AsyncDAVClient and provide the appropriate interface.
    2. Operations Layer: Pure Python logic that builds request descriptors using the Protocol Layer. It does not perform I/O itself.
    3. Protocol Layer (Sans-I/O): Responsible for building XML bodies (via xml_builders.py) and parsing XML responses (via xml_parsers.py). This layer is independent of the transport mechanism.
    4. Client Layer: The execution engine. DAVClient (sync) uses niquests and AsyncDAVClient (async) uses httpx to perform the actual HTTP requests and handle authentication.