calendaring_sync.adapters.base module#
The base classes every adapter is built on.
Adapter holds the order of a sync, written once for every
protocol: open a session, read and ingest what changed,
then push the pending edits. An adapter subclasses it, fills in
how its protocol reads and sends, and adds SyncAdapter or
AsyncAdapter for the public methods.
Both variants run the same steps. A step is a generator that yields what
a client call returned, the answer itself from a synchronous client or a
coroutine from an asynchronous one, or an InStore for a call into
the store, and gets each result sent back. SyncAdapter
runs the steps as they come; AsyncAdapter awaits the coroutines
and runs the store calls as its description says.
- class calendaring_sync.adapters.base.Adapter(replica, scope_id)[source]#
Bases:
ABCSyncs one scope with a
Replica, for any protocol.A sync opens a session, reads what changed on the server and ingests it, then pushes the pending edits one at a time: claimed, sent, settled. When the server refuses the scope’s sync token, the scope is reset and listed in full in a new session. An adapter subclasses it together with
SyncAdapterorAsyncAdapter, which add the public methods.A subclass sets these class attributes, which open each session:
_fingerprint_scheme: the name of the adapter’s fingerprint scheme._media_type: the media type of the scope’s content._guarded_by_version:Trueif a push applies only while the object still has the version the edit was based on._unique_logical_key:Trueif no two objects in the scope can share a logical key._guard_covers_scope:Trueif the server checks a push against the whole scope rather than the object, so after one rejection every later push would be rejected too, and they wait for the next sync.
It writes these methods for its protocol, each a generator of
Stepsexcept_waits(),_new_object_id()and_edit_fields():_read()fetches what changed and ingests it._send()sends one claimed edit._waits()says whether a pending edit can’t be sent yet._new_object_id()picks the object id of a create._edit_fields()reads an edit’s fields from its content.
It may also write
_gone(), which says whether an error from the server means the scope is gone; a request made through_ask()then raisesScopeGoneErrorfor it.These names start with an underscore because your application calls the adapter’s public methods: a read or a send called on its own would skip the session that orders them.
- property needs_full_listings: bool#
Whether the server can lose changes without refusing the scope’s token.
If it’s
True, an incremental sync may miss a change or a removal, so schedule a sync withfull_listing=Truenow and then, such as once a day.Falseby default; an adapter whose server can lose changes says so.
- edit_fields(content)[source]#
Read an object’s fields from its content, the same way a sync reads the server’s copy.
Use it for content your application made itself, such as a
Merge. Reads nothing from the store or the server.- Raises:
ValueError – If the content can’t be an object of the scope, as for a create.
- Return type:
- _ask(request)[source]#
Make one request about the scope, and return the server’s answer.
requestis a function making the call, so a synchronous client’s error is raised inside this step too.
- _gone(error)[source]#
Say why the scope is gone, if
errormeans the server no longer has it.- Return type:
- Returns:
The
ScopeGoneErrormessage, orNonefor any other error, which is raised unchanged. The base class returnsNone.
- abstractmethod _read(session, scope, *, full_listing)[source]#
Fetch what changed in the scope, ingest it, and say how the read ended.
Without a token in
scope.token, or withfull_listing, list every object; otherwise ask for the changes since the token. Ingest eachChangeSetwithyield InStore(session.ingest, (change_set,)).- Parameters:
- Return type:
- Returns:
ReadResult.INVALID_TOKENif the server refused the token, before anything was ingested;ReadResult.TRUNCATEDif the server cut the changes short;ReadResult.COMPLETEotherwise.
- abstractmethod _send(claimed)[source]#
Send one claimed edit and return how it ended.
An error raised here reaches the caller and leaves the push in flight, since the request may have reached the server; the next complete ingest settles it.
- abstractmethod _waits(record)[source]#
Whether a pending edit can’t be sent yet, so it waits for a later sync.
- abstractmethod _new_object_id()[source]#
Return an object id for a create, or
Noneif the server picks it.
- abstractmethod _edit_fields(content, *, create)[source]#
Read an edit’s fields from its content, the same way a sync reads the server’s copy.
- Parameters:
- Raises:
ValueError – If the content can’t be an object of the scope.
- Return type:
- class calendaring_sync.adapters.base.SyncAdapter(replica, scope_id)[source]#
Bases:
AdapterThe public methods of an adapter for a synchronous client.
Your application calls
sync()when it decides to sync, and records its edits withcreate()andchange(). Deletes and discards need nothing from the content, so they go todelete()anddiscard(). The adapter’s class says what content it takes and which of its client’s errors reach you; the next sync settles a push such an error interrupted. Every method also raises the store’s errors, asReplicadescribes.- sync(*, full_listing=False)[source]#
Sync the scope: ingest what changed on the server, then push your edits.
A scope with no token is listed in full. 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 (
bool) – List every object instead of asking for changes.- Raises:
ScopeGoneError – If the server no longer has the scope.
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.
- Return type:
- create(content)[source]#
Record a create of an object with this content, and return its local key.
The next
sync()sends it. Runs in a write transaction.- Raises:
ValueError – If the content can’t be an object of the scope.
UnknownScopeError – If the scope was never synced.
TransactionError – If a store transaction is open in this thread.
- Return type:
- change(local_key, content)[source]#
Record a change of an object to this content; the next
sync()sends it.Reads the record, then writes the change, each in a transaction of its own. A change to a create that wasn’t sent yet stays a create, so the content must pass what a create’s does.
- Raises:
ValueError – If the content can’t be an object of the scope.
OtherScopeError – If 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.
- Return type:
- waiting()[source]#
Return the pending edits the next
sync()won’t send, because it can’t yet.Runs in a read transaction.
- Raises:
UnknownScopeError – If the scope was never synced.
TransactionError – If a store transaction is open in this thread.
- Return type:
- class calendaring_sync.adapters.base.AsyncAdapter(replica, scope_id)[source]#
Bases:
AdapterThe public methods of an adapter for an asynchronous client.
The same as
SyncAdapter’s, as coroutines. Each store call runs in a worker thread, so a long ingest doesn’t block the event loop.
- calendaring_sync.adapters.base.Steps#
The steps of a sync, as a generator that yields client answers or
InStorecalls, is sent back what each one returned, and returns its result.
- class calendaring_sync.adapters.base.InStore(call, args=(), kwargs=dict())[source]#
Bases:
objectA call into the store, which an asynchronous adapter runs in a worker thread.
Examples
A step that ingests a change set:
yield InStore(session.ingest, (change_set,))
- class calendaring_sync.adapters.base.ReadResult(*values)[source]#
Bases:
EnumHow the read part of a sync ended, which decides what the sync does next.
- COMPLETE = 'complete'#
Every change was ingested, so the pushes go out.
- TRUNCATED = 'truncated'#
The server cut the changes short. The pushes wait, and the next sync continues from the checkpoint.
- INVALID_TOKEN = 'invalid token'#
The server refused the scope’s sync token, so the scope is reset and listed in full.