Tutorial#
This tutorial covers basic usage of the python CalDAV client library.
Copy code examples into a file and add a breakpoint() inside the
with-block to inspect return objects. Do not name your file caldav.py
or calendar.py, as this may break imports.
Ad-hoc Configuration#
To run the tutorial examples against a test server, you need:
The caldav source with tests:
git clone https://github.com/python-caldav/caldav.git ; cd caldavRadicale installed:
pip install radicaleEnvironment variable set:
export PYTHON_CALDAV_USE_TEST_SERVER=1
With this setup, the with-blocks below will spin up a Radicale server.
Real Configuration#
The recommended way to configure caldav is through a config file or
environment variables. Create ~/.config/caldav/caldav.conf:
# ~/.config/caldav/caldav.conf
[default]
url = https://caldav.example.com/
username = alice
password = secret
Or set environment variables:
# export CALDAV_URL=https://caldav.example.com/
# export CALDAV_USERNAME=alice
# export CALDAV_PASSWORD=secret
With configuration in place, you can use caldav without hardcoding credentials:
from caldav import get_calendars
with get_calendars() as calendars:
for cal in calendars:
print(cal.get_display_name())
Getting Calendars#
Use caldav.get_calendars() to get all calendars or filter by name:
from caldav import get_calendars, get_calendar, get_davclient
# First create a calendar to work with
with get_davclient() as client:
my_principal = client.principal()
my_principal.make_calendar(name="Work")
# Get all calendars
with get_calendars() as calendars:
for cal in calendars:
print(cal.get_display_name())
# Get a specific calendar by name
with get_calendar(calendar_name="Work") as work_calendar:
if work_calendar:
events = work_calendar.search(event=True)
Creating Calendars and Events#
Create a test calendar and add an event:
from caldav import get_davclient
import datetime
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(name="Test calendar")
may17 = my_new_calendar.add_event(
dtstart=datetime.datetime(2020,5,17,8),
dtend=datetime.datetime(2020,5,18,1),
uid="may17",
summary="Do the needful",
rrule={'FREQ': 'YEARLY'})
Add an event from icalendar data:
from caldav import get_davclient
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(name="Test calendar")
may17 = my_new_calendar.add_event("""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:20200516T060000Z-123401@example.com
DTSTAMP:20200516T060000Z
DTSTART:20200517T060000Z
DTEND:20200517T230000Z
RRULE:FREQ=YEARLY
SUMMARY:Do the needful
END:VEVENT
END:VCALENDAR
""")
Searching#
Use search to find events, tasks, or journals:
from caldav import get_davclient
from datetime import date
import datetime
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(name="Test calendar")
my_new_calendar.add_event(
dtstart=datetime.datetime(2023,5,17,8),
dtend=datetime.datetime(2023,5,18,1),
uid="may17",
summary="Do the needful",
rrule={'FREQ': 'YEARLY'})
my_events = my_new_calendar.search(
event=True,
start=date(2026,5,1),
end=date(2026,6,1),
expand=True)
assert len(my_events) == 1
print(my_events[0].data)
The expand parameter expands recurring events into individual
occurrences within the search interval. The event=True parameter
filters results to events only (excluding tasks and journals).
Modifying Events#
The data property contains icalendar data as a string:
from caldav import get_davclient
from datetime import date
import datetime
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(name="Test calendar")
my_new_calendar.add_event(
dtstart=datetime.datetime(2023,5,17,8),
dtend=datetime.datetime(2023,5,18,1),
uid="may17",
summary="Do the needful",
rrule={'FREQ': 'YEARLY'})
my_events = my_new_calendar.search(
event=True,
start=date(2026,5,1),
end=date(2026,6,1),
expand=True)
assert len(my_events) == 1
my_events[0].data = my_events[0].data.replace("Do the needful", "Have fun!")
my_events[0].save()
Better practice is to use the icalendar library. The component
property gives access to the icalendar.cal.Event object:
from caldav import get_davclient
from datetime import date
import datetime
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(name="Test calendar")
my_new_calendar.add_event(
dtstart=datetime.datetime(2023,5,17,8),
dtend=datetime.datetime(2023,5,18,1),
uid="may17",
summary="Do the needful",
rrule={'FREQ': 'YEARLY'})
my_events = my_new_calendar.search(
event=True,
start=date(2026,5,1),
end=date(2026,6,1),
expand=True)
assert len(my_events) == 1
print(f"Event starts at {my_events[0].component.start}")
with my_events[0].edit_icalendar_instance() as cal:
cal.subcomponents[0]['summary'] = "Norwegian national day celebrations"
my_events[0].save()
Tasks#
Create a task list and work with tasks:
from caldav import get_davclient
from datetime import date
with get_davclient() as client:
my_principal = client.get_principal()
my_new_calendar = my_principal.make_calendar(
name="Test calendar", supported_calendar_component_set=['VTODO'])
my_new_calendar.add_todo(
summary="prepare for the Norwegian national day", due=date(2025,5,16))
my_tasks = my_new_calendar.search(
todo=True)
assert len(my_tasks) == 1
my_tasks[0].complete()
my_tasks = my_new_calendar.search(
todo=True)
assert len(my_tasks) == 0
my_tasks = my_new_calendar.search(
todo=True, include_completed=True)
assert len(my_tasks) == 1
Further Reading#
See the Examples folder for more code, including basic examples and scheduling examples for invites.
The test code covers most features.
There is also a command line interface built around the caldav library.