calendarobjectresource – Calendar Object Resources (Todo, Event, Journal, FreeBusy)#

Calendar Objects Resources, as defined in the RFC 4791.

There are three subclasses Todo, Journal and Event. Those mirrors objects stored on the server. The word CalendarObjectResource is long, complicated and may be hard to understand. When you read the word “event” in any documentation, issue discussions, etc, then most likely it should be read as “a CalendarObjectResource, like an event, a task or a journal”. Do not make the mistake of going directly to the Event-class if you want to contribute code for handling “events” - consider that the same code probably will be appicable to Joural and Todo events, if so, CalendarObjectResource is the right class! Clear as mud?

FreeBusy is also defined as a Calendar Object Resource in the RFC, and it is a bit different . Perhaps there should be another class layer between CalendarObjectResource and Todo/Event/Journal to indicate that the three latter are closely related, while FreeBusy is something different.

Alarms and Time zone objects does not have any class as for now. Those are typically subcomponents of an event/task/journal component.

Users of the library should not need to construct any of those objects. To add new content to the calendar, use calendar.add_event, calendar.add_todo or calendar.add_journal. Those methods will return a CalendarObjectResource. To update an existing object, use event.save().

class caldav.calendarobjectresource.CalendarObjectResource(client=None, url=None, data=None, parent=None, id=None, props=None)[source][source]#

Ref RFC 4791, section 4.1, a “Calendar Object Resource” can be an event, a todo-item, a journal entry, or a free/busy entry

As per the RFC, a CalendarObjectResource can at most contain one calendar component, with the exception of recurrence components. Meaning that event.data typically contains one VCALENDAR with one VEVENT and possibly one VTIMEZONE.

In the case of expanded calendar date searches, each recurrence will (by default) wrapped in a distinct CalendarObjectResource object. This is a deviation from the definition given in the RFC.

accept_invite(calendar=None)[source][source]#

Accepts an invite - to be used on an invite object.

Return type:

None

add_attendee(attendee, no_default_parameters=False, **parameters)[source][source]#

For the current (event/todo/journal), add an attendee.

The attendee can be any of the following: * A principal * An email address prepended with “mailto:” * An email address without the “mailto:”-prefix * A two-item tuple containing a common name and an email address * (not supported, but planned: an ical text line starting with the word “ATTENDEE”)

Any number of attendee parameters can be given, those will be used as defaults unless no_default_parameters is set to True:

partstat=NEEDS-ACTION cutype=UNKNOWN (unless a principal object is given) rsvp=TRUE role=REQ-PARTICIPANT schedule-agent is not set

Return type:

None

add_organizer()[source][source]#

goes via self.client, finds the principal, figures out the right attendee-format and adds an organizer line to the event

Return type:

None

change_attendee_status(attendee=None, **kwargs)[source][source]#

Updates the attendee-line according to the arguments received

Return type:

None

check_reverse_relations(pdb=False)[source][source]#

Will verify that for all the objects we point at though the RELATED-TO property, the other object points back to us as well.

Returns a list of tuples. Each tuple contains an object that do not point back as expected, and the expected reltype

Return type:

list[tuple]

property component[source]#

icalendar component - this is the simplest way to access the event/task - it will give you the first component that isn’t a timezone component. For recurrence sets, the master component will be returned. For any non-recurring event/task/journal, there should be only one calendar component in the object. For results from an expanded search, there should be only one calendar component in the object

copy(keep_uid=False, new_parent=None)[source][source]#

Events, todos etc can be copied within the same calendar, to another calendar or even to another caldav server

Return type:

Self

property data[source]#

vCal representation of the object as normal string

decline_invite(calendar=None)[source][source]#

Declines an invite - to be used on an invite object.

Return type:

None

edit_icalendar_component()[source][source]#

Context manager to borrow the inner icalendar component for editing.

Like edit_icalendar_instance() but yields the first VEVENT / VTODO / VJOURNAL subcomponent directly, rather than the VCALENDAR wrapper. This is convenient when you only need to modify a single property on the component itself.

Usage:

with event.edit_icalendar_component() as comp:
    comp['SUMMARY'] = 'New Summary'
event.save()
Yields:

The first non-VTIMEZONE subcomponent of the icalendar object.

Raises:
  • RuntimeError – If another representation is currently borrowed.

  • StopIteration – If the calendar contains no non-timezone components.

edit_icalendar_instance()[source][source]#

Context manager to borrow the icalendar object for editing.

Usage:

with event.edit_icalendar_instance() as cal:
    cal.subcomponents[0]['SUMMARY'] = 'New Summary'
event.save()

While inside the context, the icalendar object is the authoritative source. Accessing other representations (vobject) while borrowed will raise RuntimeError.

Yields:

The authoritative icalendar.Calendar object.

Raises:

RuntimeError – If another representation is currently borrowed.

edit_vobject_instance()[source][source]#

Context manager to borrow the vobject object for editing.

Usage:

with event.edit_vobject_instance() as vobj:
    vobj.vevent.summary.value = 'New Summary'
event.save()

While inside the context, the vobject object is the authoritative source. Accessing other representations (icalendar) while borrowed will raise RuntimeError.

Yields:

The authoritative vobject component.

Raises:

RuntimeError – If another representation is currently borrowed.

expand_rrule(start, end, include_completed=True)[source][source]#

This method will transform the calendar content of the event and expand the calendar data from a “master copy” with RRULE set and into a “recurrence set” with RECURRENCE-ID set and no RRULE set. The main usage is for client-side expansion in case the calendar server does not support server-side expansion. If doing a self.load, the calendar content will be replaced with the “master copy”.

Parameters:
Return type:

None

fix_reverse_relations(pdb=False)[source][source]#

Will ensure that for all the objects we point at though the RELATED-TO property, the other object points back to us as well.

Returns a list of tuples. Each tuple contains an object that did not point back as expected, and the expected reltype

Return type:

list

get_data()[source][source]#

Get raw iCalendar data as string.

This is always safe to call and returns the current data without side effects. If the current representation is a parsed object, it will be serialized.

Return type:

str

Returns:

The iCalendar data as a string, or empty string if no data.

get_dtend()[source]#

A VTODO may have due or duration set. Return or calculate due.

WARNING: this method is likely to be deprecated and moved to the icalendar library. If you decide to use it, please put caldav<3.0 in the requirements.

get_due()[source][source]#

A VTODO may have due or duration set. Return or calculate due.

WARNING: this method is likely to be deprecated and moved to the icalendar library. If you decide to use it, please put caldav<3.0 in the requirements.

get_duration()[source][source]#

According to the RFC, either DURATION or DUE should be set for a task, but never both - implicitly meaning that DURATION is the difference between DTSTART and DUE (personally I believe that’s stupid. If a task takes five minutes to complete - say, fill in some simple form that should be delivered before midnight at new years eve, then it feels natural for me to define “duration” as five minutes, DTSTART to “some days before new years eve” and DUE to 20xx-01-01 00:00:00 - but I digress.

This method will return DURATION if set, otherwise the difference between DUE and DTSTART (if both of them are set).

TODO: should be fixed for Event class as well (only difference is that DTEND is used rather than DUE) and possibly also for Journal (defaults to one day, probably?)

WARNING: this method is likely to be deprecated and moved to the icalendar library. If you decide to use it, please put caldav<3.0 in the requirements.

Return type:

timedelta

get_icalendar_component()[source][source]#

Get a COPY of the inner icalendar component (VEVENT/VTODO/VJOURNAL) for read-only access.

This is safe for inspection - modifications to the returned object will NOT be saved. For editing, use edit_icalendar_component().

For recurring events with multiple components, returns the first non-timezone component (the master RRULE component). Use search(..., expand=True) to get individual expanded occurrences.

Return type:

Component

Returns:

A copy of the first non-timezone subcomponent.

get_icalendar_instance()[source][source]#

Get a COPY of the icalendar object for read-only access.

This is safe for inspection - modifications to the returned object will NOT be saved. For editing, use edit_icalendar_instance().

Return type:

Calendar

Returns:

A copy of the icalendar.Calendar object.

get_relatives(reltypes=None, relfilter=None, fetch_objects=True, ignore_missing=True)[source][source]#

By default, loads all objects pointed to by the RELATED-TO property and loads the related objects.

It’s possible to filter, either by passing a set or a list of acceptable relation types in reltypes, or by passing a lambda function in relfilter.

TODO: Make it possible to also check up reverse relationships

TODO: this is partially overlapped by plann.lib._relships_by_type in the plann tool. Should consolidate the code.

TODO: should probably return some kind of object instead of a weird dict structure. (but due to backward compatibility requirement, such an object should behave like the current dict)

Return type:

defaultdict[str, set[str]]

get_vobject_instance()[source][source]#

Get a COPY of the vobject object for read-only access.

This is safe for inspection - modifications to the returned object will NOT be saved. For editing, use edit_vobject_instance().

Return type:

vobject.base.Component

Returns:

A copy of the vobject component.

has_component()[source][source]#

Returns True if there exists a VEVENT, VTODO or VJOURNAL in the data. Returns False if it’s only a VFREEBUSY, VTIMEZONE or unknown components.

Used internally after search to remove empty search results (sometimes Google return such)

Return type:

bool

property icalendar_component[source]#

icalendar component - this is the simplest way to access the event/task - it will give you the first component that isn’t a timezone component. For recurrence sets, the master component will be returned. For any non-recurring event/task/journal, there should be only one calendar component in the object. For results from an expanded search, there should be only one calendar component in the object

property icalendar_instance[source]#

icalendar instance of the object

property id: str | None[source]#

Returns the UID of the calendar object.

Extracts the UID from the calendar data using cheap accessors that avoid unnecessary parsing (issue #515, #613). Falls back to direct icalendar parsing if the cheap accessor fails. Does not trigger a load from the server.

property instance: vobject.base.Component | None[source]#

vobject instance of the object (DEPRECATED! This will yield an icalendar instance in caldav 3.0)

is_invite_reply()[source][source]#

Returns True if the object is a reply, see RFC 2446 Section 3.2.3.

Return type:

bool

is_invite_request()[source][source]#

Returns True if this object is a request, see RFC 2446 Section 3.2.2.

Return type:

bool

is_loaded()[source][source]#

Returns True if there exists data in the object. An object is considered not to be loaded if it contains no data but just the URL.

Optimized to use cheap accessors (issue #613).

load(only_if_unloaded=False)[source][source]#

(Re)load the object from the caldav server.

For sync clients, loads and returns self. For async clients, returns a coroutine that must be awaited.

Return type:

Self

Example (sync):

obj.load()

Example (async):

await obj.load()

load_by_multiget()[source][source]#

Some servers do not accept a GET, but we can still do a REPORT with a multiget query

Return type:

Self

save(no_overwrite=False, no_create=False, obj_type=None, increase_seqno=True, if_schedule_tag_match=False, only_this_recurrence=True, all_recurrences=False)[source][source]#

Save the object, can be used for creation and update.

no_overwrite and no_create will check if the object exists. Those two are mutually exclusive. Some servers don’t support searching for an object uid without explicitly specifying what kind of object it should be, hence obj_type can be passed. obj_type is only used in conjunction with no_overwrite and no_create.

is_schedule_tag_match is currently ignored. (TODO - fix or remove)

The SEQUENCE should be increased when saving a new version of the object. If this behaviour is unwanted, then increase_seqno should be set to False. Also, if SEQUENCE is not set, then this will be ignored.

The behaviour when saving a single recurrence object to the server is as far as I can understand not defined in the RFCs, but all servers I’ve tested against will overwrite the full event with the recurrence instance (effectively deleting the recurrence rule). That’s almost for sure not what the caller intended. only_this_recurrence and all_recurrences only applies when trying to save a recurrence object. They are by nature mutually exclusive, but since only_this_recurrence is True by default, it will be ignored if all_recurrences is set.

If you want to sent the recurrence as it is to the server, you should set both all_recurrences and only_this_recurrence to False.

Return type:

Self

Returns:

  • self

set_end(end, move_dtstart=False)[source][source]#

The RFC specifies that a VEVENT/VTODO cannot have both dtend/due and duration, so when setting dtend/due, the duration field must be evicted

WARNING: this method is likely to be deprecated and parts of it moved to the icalendar library. If you decide to use it, please put caldav<4.0 in the requirements.

set_relation(other, reltype=None, set_reverse=True)[source][source]#

Sets a relation between this object and another object (given by uid or object).

Return type:

None

split_expanded()[source][source]#

This was used internally for processing search results. Library users probably don’t need to care about this one.

The logic is now handled directly in the search method.

This method is probably used by nobody and nothing, but it can’t be removed easily as it’s exposed as part of the public API

Return type:

list[Self]

tentatively_accept_invite(calendar=None)[source][source]#

Tentatively accept an invite - to be used on an invite object.

Return type:

None

property vobject_instance: vobject.base.Component | None[source]#

vobject instance of the object

property wire_data[source]#

vCal representation of the object in wire format (UTF-8, CRLN)

class caldav.calendarobjectresource.Event(client=None, url=None, data=None, parent=None, id=None, props=None)[source][source]#

The Event object is used to represent an event (VEVENT).

As of 2020-12 it adds very little to the inheritated class. (I have frequently asked myself if we need those subclasses … perhaps not)

set_dtend(end, move_dtstart=False)[source]#

The RFC specifies that a VEVENT/VTODO cannot have both dtend/due and duration, so when setting dtend/due, the duration field must be evicted

WARNING: this method is likely to be deprecated and parts of it moved to the icalendar library. If you decide to use it, please put caldav<4.0 in the requirements.

class caldav.calendarobjectresource.FreeBusy(parent, data, url=None, id=None)[source][source]#

The FreeBusy object is used to represent a freebusy response from the server. __init__ is overridden, as a FreeBusy response has no URL or ID. The inheritated methods .save and .load is moot and will probably throw errors (perhaps the class hierarchy should be rethought, to prevent the FreeBusy from inheritating moot methods)

Update: With RFC6638 a freebusy object can have a URL and an ID.

class caldav.calendarobjectresource.Journal(client=None, url=None, data=None, parent=None, id=None, props=None)[source][source]#

The Journal object is used to represent a journal entry (VJOURNAL).

As of 2020-12 it adds nothing to the inheritated class. (I have frequently asked myself if we need those subclasses … perhaps not)

class caldav.calendarobjectresource.Todo(client=None, url=None, data=None, parent=None, id=None, props=None)[source][source]#

The Todo object is used to represent a todo item (VTODO). A Todo-object can be completed.

There is some extra logic here - arguably none of it belongs to the caldav library, and should be moved either to the icalendar library or to the plann library (plann is a cli-tool, should probably be split up into one library for advanced calendaring operations and the cli-tool as separate packages)

complete(completion_timestamp=None, handle_rrule=False, rrule_mode='safe')[source][source]#

Marks the task as completed.

Return type:

None

Parameters:
  • completion_timestamp (datetime) – Defaults to datetime.now().

  • handle_rrule (Bool) – If set to True, the library will try to be smart if the task is recurring. The default is False, for backward compatibility. I may consider making this one mandatory.

  • rrule_mode (str) – The RFC leaves a lot of room for interpretation on how to handle recurring tasks, and what works on one server may break at another. The following modes are accepted: * this_and_future - see doc for _complete_recurring_thisandfuture for details * safe - see doc for _complete_recurring_safe for details

set_due(due, move_dtstart=False, check_dependent=False)[source][source]#

The RFC specifies that a VTODO cannot have both due and duration, so when setting due, the duration field must be evicted

check_dependent=True will raise some error if there exists a parent calendar component (through RELATED-TO), and the parents due or dtend is before the new dtend).

WARNING: this method may become deprecated and parts of it moved to the icalendar library at some point in the future.

WARNING: the check_dependent-logic may be rewritten to support RFC9253 in 3.x

set_duration(duration, movable_attr='DTSTART')[source][source]#

If DTSTART and DUE/DTEND is already set, one of them should be moved. Which one? I believe that for EVENTS, the DTSTART should remain constant and DTEND should be moved, but for a task, I think the due date may be a hard deadline, hence by default we’ll move DTSTART.

TODO: can this be written in a better/shorter way?

WARNING: this method may be deprecated and moved to the icalendar library at some point in the future.

set_end(due, move_dtstart=False, check_dependent=False)[source]#

The RFC specifies that a VTODO cannot have both due and duration, so when setting due, the duration field must be evicted

check_dependent=True will raise some error if there exists a parent calendar component (through RELATED-TO), and the parents due or dtend is before the new dtend).

WARNING: this method may become deprecated and parts of it moved to the icalendar library at some point in the future.

WARNING: the check_dependent-logic may be rewritten to support RFC9253 in 3.x

uncomplete()[source][source]#

Undo completion - marks a completed task as not completed

Return type:

None