Skip to content

multicloud.docstore

Document operations and fluent action lists. The public entry point is DocStoreClient; query/filter/pagination APIs are not present in this public client. See the roadmap for the current surface.

Cloud-agnostic document store client package.

Public entry point — :class:DocStoreClient::

from multicloud.docstore.docstore_client import DocStoreClient

with DocStoreClient(
    provider="aws",
    table_name="my-table",
    partition_key="pk",
    region="us-west-2",
) as client:
    client.actions().create({"pk": "alice", "name": "Alice"}).run()

Provider classes (AwsDocStore, ...) live in sibling distributions (multicloud-docstore-aws, ...) and are discovered at runtime via setuptools entry points.

DocStoreClient

DocStoreClient(
    *,
    provider: str,
    table_name: str,
    partition_key: str,
    sort_key: Optional[str] = None,
    revision_field: Optional[str] = None,
    allow_scans: bool = True,
    max_outstanding_action_rpcs: int = 0,
    region: Optional[str] = None,
    endpoint: Optional[str] = None,
    credentials_overrider: Optional[
        Callable[..., Any]
    ] = None
)

Cloud-agnostic document store client.

Resolves a provider-specific driver from :data:DOCSTORE_REGISTRY at construction time and forwards every operation to it. All arguments are keyword-only; only provider, table_name, and partition_key are required.

:param provider: Provider id (e.g. "aws"). :param table_name: Name of the underlying table / collection. :param partition_key: Attribute name that holds the partition key. :param sort_key: Optional attribute name for the sort key. :param revision_field: Attribute used for optimistic-concurrency revisions. None uses the driver default ("DocstoreRevision"). :param allow_scans: Whether scans (unindexed full-table reads) are permitted on this collection. :param max_outstanding_action_rpcs: Upper bound on in-flight backend RPCs when run_actions fans out. 0 uses the executor default. :param region: Provider region (e.g. "us-west-2"). :param endpoint: Provider endpoint URI override. :param credentials_overrider: Optional provider-specific credentials callback. :raises KeyError: If provider is not registered.

store property

store: BaseDocStore

Underlying driver instance (mostly for tests / introspection).

actions

actions() -> ActionList

Return a fresh :class:ActionList bound to this client.

create

create(document: Any) -> None

Insert a new row for document.

Fails if a row with the same key already exists. The document must not carry the revision field; the driver stamps a fresh revision back onto document on success.

:param document: A dict (wrapped automatically) or a :class:Document instance. :raises ResourceAlreadyExistsException: If the key already exists. :raises InvalidArgumentException: If the document carries the revision field, or the primary key shape is invalid.

replace

replace(document: Any) -> None

Overwrite an existing row with document.

Requires the row to already exist. When document carries the revision field, the write additionally asserts that the stored revision matches; the driver stamps a fresh revision back onto document on success.

:param document: A dict (wrapped automatically) or a :class:Document instance. :raises ResourceNotFoundException: If the row does not exist or the revision precondition fails.

put

put(document: Any) -> None

Upsert document — insert or overwrite unconditionally.

When document carries the revision field the write is promoted to a conditional REPLACE that requires the stored revision to match. The driver stamps a fresh revision back onto document on success in that case.

:param document: A dict (wrapped automatically) or a :class:Document instance. :raises ResourceNotFoundException: If a revision was supplied and the stored revision does not match (or the row is absent).

delete

delete(document: Any) -> None

Delete the row identified by document's primary key.

When document carries the revision field the delete is conditional on the stored revision matching.

:param document: A dict (wrapped automatically) or a :class:Document carrying the primary key. :raises ResourceNotFoundException: If a revision was supplied and the stored revision does not match.

get

get(document: Any, *field_paths: str) -> None

Read a row by its primary key and merge fields into document.

The document's primary-key attributes are used as the lookup key; retrieved attributes are written back onto document in place.

:param document: A dict (wrapped automatically) or a :class:Document instance carrying the primary key. :param field_paths: Optional dotted projection paths (e.g. "a.b.c"). Empty means "return the full item".

batch_get

batch_get(documents: Iterable[Any]) -> None

Read many rows in a single :class:ActionList run.

Each document must carry its primary key; retrieved attributes are merged back onto the same document instances in place. Projection is not supported here — use :meth:actions for per-row field_paths.

:param documents: Iterable of dict / :class:Document items.

batch_put

batch_put(documents: Iterable[Any]) -> None

Upsert many rows in a single :class:ActionList run.

Each row is inserted if absent or overwritten if present, using the same revision-aware semantics as :meth:put.

:param documents: Iterable of dict / :class:Document items.

batch_delete

batch_delete(documents: Iterable[Any]) -> None

Delete many rows in a single :class:ActionList run.

Each document must carry its primary key. When a document carries the revision field, that delete is conditional on the stored revision matching — same semantics as :meth:delete.

:param documents: Iterable of dict / :class:Document items.

Action dataclass

Action(
    kind: ActionKind,
    document: Document,
    field_paths: Optional[List[List[str]]] = None,
    mods: Optional[List[Any]] = None,
    in_atomic_write: bool = False,
    key: Any = None,
    index: int = 0,
)

A single pending operation on a document.

A value type produced by the fluent :class:ActionList and consumed by the driver's :meth:BaseDocStore.run_actions.

key and index are populated by :meth:BaseDocStore._to_driver_action and used by :func:group_actions to keep the caller-supplied order.

ActionKind

Bases: Enum

The kind of write / read that an :class:Action represents.

ActionList

ActionList(docstore: BaseDocStore)

Fluent builder for a batch of document operations.

Users call .create(doc) (etc.) any number of times, then .run(). run() first shapes each pending action via :meth:BaseDocStore._to_driver_action (which assigns keys and validates), then dispatches the whole batch through :meth:BaseDocStore.run_actions.

create

create(document: Any) -> 'ActionList'

Queue a CREATE action for document.

CREATE inserts a new row and fails with :class:ResourceAlreadyExistsException at :meth:run time if the key already exists. The document must not carry the revision field; the driver stamps a fresh revision on success.

:param document: A dict (wrapped automatically) or a :class:Document. :return: This :class:ActionList for fluent chaining.

replace

replace(document: Any) -> 'ActionList'

Queue a REPLACE action for document.

REPLACE requires the row to already exist. If document carries a revision, the write additionally asserts that the stored revision matches; the driver stamps a fresh revision on success.

:param document: A dict (wrapped automatically) or a :class:Document. :return: This :class:ActionList for fluent chaining.

put

put(document: Any) -> 'ActionList'

Queue a PUT (upsert) action for document.

PUT is an unconditional upsert. When document carries the revision field the action is promoted to REPLACE by :meth:BaseDocStore._to_driver_action, adding a matching-revision precondition.

:param document: A dict (wrapped automatically) or a :class:Document. :return: This :class:ActionList for fluent chaining.

delete

delete(document: Any) -> 'ActionList'

Queue a DELETE action for document.

The document's primary-key attributes are used as the target key. When document carries the revision field the write additionally asserts that the stored revision matches.

:param document: A dict (wrapped automatically) or a :class:Document carrying the primary key. :return: This :class:ActionList for fluent chaining.

get

get(document: Any, *field_paths: str) -> 'ActionList'

Queue a GET action for document with optional field projection.

The document's primary-key attributes are used as the lookup key; retrieved attributes are merged back into it in place at :meth:run time.

:param document: A dict (wrapped automatically) or a :class:Document carrying the primary key. :param field_paths: Optional dotted projection paths (e.g. "a.b.c"). Empty means "return the full item". :return: This :class:ActionList for fluent chaining.

run

run() -> None

Validate every queued action and dispatch the batch.

First shapes each pending action via :meth:BaseDocStore._to_driver_action (assigns keys, promotes PUT-with-revision to REPLACE, rejects duplicates), then hands the whole list to :meth:BaseDocStore.run_actions. Any provider-side error surfaces as a :class:SubstrateSdkException; unexpected exceptions are re-raised as :class:InvalidArgumentException.

:raises SubstrateSdkException: Any translated backend error.

BaseDocStore

BaseDocStore(
    collection_options: CollectionOptions,
    *,
    credentials_overrider: Optional[
        Callable[..., Any]
    ] = None
)

Bases: Provider, ABC

Abstract driver a provider (e.g. the AWS provider) implements.

Owns the collection options, the internal executor used to fan action lists out to their backend, and the CREATE-path helpers that are agnostic of the backing store (key extraction, validation, executor sizing).

check_closed

check_closed() -> None

Raise if the store has been closed.

actions

actions() -> 'ActionList'

Return a new fluent :class:ActionList bound to this store.

get_key

get_key(document: Document) -> Any

Extract the primary key from document.

Returns None when the partition key is missing so the CREATE path can decide whether to synthesize a UUID. On a table with a sort key, both parts must be present or the result is None.

:param document: The document being written or read. :return: The raw partition-key value when no sort key is configured, a (pk, sk) tuple when one is, or None when a required part is missing.

run_actions abstractmethod

run_actions(
    actions: Sequence[Action],
    before_do: Optional[
        Callable[[Callable[[Any], bool]], None]
    ] = None,
) -> None

Execute a driver-shaped list of actions.

CollectionOptions dataclass

CollectionOptions(
    table_name: str,
    partition_key: str,
    sort_key: Optional[str] = None,
    allow_scans: bool = True,
    revision_field: Optional[str] = None,
    max_outstanding_action_rpcs: int = 0,
)

Immutable configuration for a document collection (table).

table_name names the underlying table or collection, partition_key / sort_key name the primary key attributes, allow_scans gates full-table scans on the read path, revision_field is the attribute used for optimistic concurrency control (empty ⇒ use :data:DEFAULT_REVISION_FIELD), and max_outstanding_action_rpcs bounds the internal thread pool that services :meth:BaseDocStore.run_actions (0 ⇒ pick a sensible default).

Document

Document(source: Any)

Wraps either a dict or a plain object with attributes.

Provides the operations the driver needs on the caller's data: reading a top-level field, setting a top-level field, walking nested dotted paths, and enumerating field names for encoding.

A Document never represents an atomic value on its own — it is always a container of named fields.

source property

source: Any

Underlying wrapped object (dict or arbitrary object).

get

get(field_path: Sequence[str]) -> Any

Read a nested field by dotted-path parts.

Traverses dict values by key and objects by attribute; a missing segment short-circuits to None instead of raising.

:param field_path: Path segments; ["a", "b", "c"] walks source["a"]["b"]["c"]. :return: The leaf value, or None if any segment is missing.

set

set(field_path: Sequence[str], value: Any) -> None

Write a value at a nested dotted path, creating intermediates.

Any missing intermediate is replaced with a fresh dict so the write always succeeds when field_path is non-empty. On object sources, intermediates are set via setattr.

:param field_path: Path segments; must be non-empty. :param value: Leaf value to store at the last segment. :raises InvalidArgumentException: If field_path is empty.

group_actions

group_actions(
    actions: Sequence[Action],
) -> Tuple[
    List[Action],
    List[Action],
    List[Action],
    List[Action],
    List[Action],
]

Split an action list into five buckets, preserving user-provided order.

Returns (before_gets, get_list, write_list, atomic_write_list, after_gets).

Any GET targeting the same key as a pending write becomes an after_gets (must run after the write); any GET whose key is later written by a non-atomic write is promoted to before_gets. Actions with a None key stream through to the write bucket directly (they will be assigned a key later on the CREATE path).

group_by_field_path

group_by_field_path(
    gets: Sequence[Action],
) -> List[List[Action]]

Group GET actions with identical field-path sets into batches.

Backend batch-get APIs typically accept only a single projection per request, so gets that project different fields cannot ride in the same batch.

serialize_object

serialize_object(obj: Any) -> str

Serialize an object into a stable string for key comparison.

Used as the dedup key across an action list — two actions targeting the same key must produce the same string here even if the caller passed a fresh instance.

validate_field_path

validate_field_path(field_path: str) -> None

Validate that field_path is non-empty and has no empty segments.