adapters.caldav: Syncing a CalDAV calendar#
An adapter connects a Replica to one kind of server. CalDAVAdapter syncs one CalDAV calendar collection through a python-caldav client, and AsyncCalDAVAdapter does the same through an asynchronous one. Install them with the caldav extra. Adapters explains every rule they follow, and Server compatibility how each tested server behaves.
The CalDAV adapter, which syncs one calendar collection through python-caldav.
Install it with the caldav extra: pip install 'calendaring-sync[caldav]'.
Adapters explains every rule it follows, and
Server compatibility how each tested server behaves.
- class calendaring_sync.adapters.caldav.AsyncCalDAVAdapter(replica: Replica, calendar: Calendar, *, scope_id: str | None = None)[source]#
CalDAVAdapterfor a calendar on an asynchronous client.sync(),create()andchange()are coroutines; each store call runs in a worker thread, so a long ingest doesn’t block the event loop. Everything else is asCalDAVAdapterdescribes.- Raises:
TypeError – If the calendar is on a synchronous client, or an argument has the wrong type.
ValueError – If the calendar has no URL or no client.
- property needs_full_listings: bool#
Whether the server may leave changes out without refusing the scope’s token.
Read from the server profile configured on the client, python-caldav’s
sync-token.invalid-tokenfeature. When it’s true, schedule regular full listings withsync(full_listing=True): a listing only fetches objects whose ETag changed, so it costs little more than a sync.
- sync(*, full_listing: bool = False) None[source]#
See
CalDAVAdapter.sync().
- class calendaring_sync.adapters.caldav.CalDAVAdapter(replica: Replica, calendar: Calendar, *, scope_id: str | None = None)[source]#
Syncs one calendar collection with a
Replica.The calendar is a python-caldav
Calendaron a synchronous client, and it’s one scope. Your application callssync()when it decides to sync, and records its edits withcreate()andchange(), which build an object’s fingerprint and logical key the same way a sync does. Deletes and discards need nothing from the content, so they go todelete()anddiscard().With no server profile on the client, python-caldav searches the calendar for a create’s
UIDbefore sending it. Configuring the client’sfeatures(features="radicale", for example) lets the server check instead, in the same request.A change or delete of an object whose stored ETag is weak (
W/) waits, unsent, until a sync stores a strong one, sinceIf-Matchnever matches a weak ETag;waiting()lists such edits.Every method also raises the store’s errors, as
Replicadescribes.- Parameters:
replica – Where the sync state is kept.
calendar – The calendar collection to sync.
scope_id – The scope’s id. Defaults to the calendar’s URL, without a login and spelled one way, so calendars on two servers never share a scope and one calendar always gets the same one.
- Raises:
TypeError – If the calendar is on an asynchronous client, or an argument has the wrong type.
ValueError – If the calendar has no URL or no client.
Examples
A sync, an edit, and the sync that pushes it:
adapter = CalDAVAdapter(replica, calendar) adapter.sync() key = adapter.create(ics_bytes) adapter.sync()
- change(local_key: int, content: bytes) None[source]#
Record a change of an object to this iCalendar data; the next
sync()sends it.Reads the record, then writes the change, each in a transaction of its own.
- Raises:
ValueError – If the content isn’t UTF-8 iCalendar data, or the record is in another scope.
UnknownRecordError – If no record has this local key.
EditRefusedError – If the record can’t take an edit, such as one with an open conflict.
TransactionError – If a store transaction is open in this thread.
- create(content: bytes) int[source]#
Record a create of an object with this iCalendar data, and return its local key.
The object gets an href of its own; the next
sync()sends it. Runs in a write transaction.- Raises:
ValueError – If the content isn’t UTF-8 iCalendar data with a
UID.UnknownScopeError – If the calendar was never synced.
TransactionError – If a store transaction is open in this thread.
- property needs_full_listings: bool#
Whether the server may leave changes out without refusing the scope’s token.
Read from the server profile configured on the client, python-caldav’s
sync-token.invalid-tokenfeature. When it’s true, schedule regular full listings withsync(full_listing=True): a listing only fetches objects whose ETag changed, so it costs little more than a sync.
- sync(*, full_listing: bool = False) None[source]#
Sync the calendar: ingest what changed on the server, then push your edits.
Changes since the scope’s token come from a
sync-collectionreport, and a calendar that has none, or a scope that has no token, is listed in full instead. When the server refuses the token, the scope is reset and listed in full. Your edits are pushed only once everything the server reported was ingested. Each step runs in a store transaction of its own.- Parameters:
full_listing – List every object instead of asking for changes. Members whose ETag didn’t change aren’t fetched.
- Raises:
ScopeGoneError – If the server has no calendar at its URL.
IncompleteListingError – If the server cut a full listing short.
StaleChangeSetError – If another process ingested the scope first.
SessionError – If this replica already has a session open for the scope.
ScopeMismatchError – If the scope’s content has another media type.
TransactionError – If a store transaction is open in this thread.
caldav.lib.error.DAVError – For any other failure python-caldav reports, such as a refused login, unchanged. A push it interrupted is settled by the next sync.
- waiting() list[Record][source]#
Return the pending edits the next
sync()won’t send, and can’t yet.That’s a change or delete of an object whose stored ETag is weak. Runs in a read transaction.
- Raises:
UnknownScopeError – If the calendar was never synced.
TransactionError – If a store transaction is open in this thread.
- calendaring_sync.adapters.caldav.FINGERPRINT_SCHEME = 'caldav-canonical-1'#
The name of the canonical fingerprint this adapter computes. A change to how it’s computed gets a new name, so every scope is listed again.
- calendaring_sync.adapters.caldav.MEDIA_TYPE = 'text/calendar'#
iCalendar data.
- Type:
The media type of a CalDAV scope’s content
- calendaring_sync.adapters.caldav.MULTIGET_SIZE = 100#
How many objects one
calendar-multigetfetches, and one page of a full listing holds.