cosmodol
Access Azure Cosmos DB (NoSQL/Core API) through a Mapping interface.
cosmodol exposes Azure Cosmos DB as dol-style Mapping /
MutableMapping interfaces, layered over the official azure-cosmos SDK.
Quick start:
from cosmodol import cosmos_store
store = cosmos_store(
connection_string="AccountEndpoint=https://localhost:8081/;AccountKey=...",
database="mydb",
container="mycontainer",
partition_key_value="tenant-X",
)
store["k1"] = {"name": "Alice", "age": 30}
store["k1"] # → {"id": "k1", "<pk>": "tenant-X", "name": "Alice", "age": 30}
"k1" in store # → True
del store["k1"]
See misc/docs/architecture.md for the layered design.
If your Cosmos account was provisioned with the MongoDB API, use pymongo +
mongodol directly — this package only targets the NoSQL/Core API.
- exception cosmodol.ContainerNotEmptyError[source]
Raised on
del db_store[name]when the container has items. Seemisc/docs/design_decisions.md§8.
- class cosmodol.CosmosAccount(connection: Any = None, *, database_factory: Callable | None = None)[source]
Mapping of database name →
CosmosDatabase.__setitem__is disabled — useadd_database(name, throughput=...).__delitem__refuses non-empty databases; useself.delete(name, force=True).
- class cosmodol.CosmosConnection(credential: str | dict | Any | None = None, connection_string: str | None = None, endpoint: str | None = None, key: str | None = None, consistency_level: str | None = None, client_kwargs: dict = <factory>)[source]
Holds a resolved credential and a lazy
CosmosClient.This is the dependency-injection seam for the package. Tests construct one pointing at the emulator without touching any store class.
- Parameters:
credential – explicit credential object or account-key string.
connection_string – full
AccountEndpoint=...;AccountKey=...string.endpoint – Cosmos endpoint URL (with or without credential).
key – account master key.
consistency_level – optional override; defaults to inheriting the account default.
client_kwargs – extra kwargs forwarded to
CosmosClient.
- property client: CosmosClient
The lazily-constructed
CosmosClient. Cached for the connection’s lifetime.
- classmethod from_anything(source) CosmosConnection[source]
Convenience: build a
CosmosConnectionfrom a thing-or-spec.- Accepts:
CosmosConnection(returned as-is)CosmosClient(wrapped without further resolution)str(connection string)dict(passed as kwargs)None(defer to env / AAD)
- class cosmodol.CosmosDatabase(database: DatabaseProxy | str, *, connection: Any = None, store_factory: Callable | None = None)[source]
Mapping of container name →
ContainerProxy(or store-wrapped form).__setitem__is disabled — useadd_container(name, partition_key_path=..., ...).__delitem__refuses non-empty containers; useself.delete(name, force=True).- Parameters:
database – Either an existing
DatabaseProxy, a database name string (resolved viaconnection), orNone(useconnection.database_name).connection –
CosmosConnection(or anythingfrom_anythingaccepts).store_factory – Optional callable
(ContainerProxy) -> Mappingused to wrap the value returned by__getitem__. Defaults to passing theContainerProxythrough.
- add_container(name: str, *, partition_key_path: str = '/id', throughput: int | None = None, indexing_policy: dict | None = None, default_ttl: int | None = None, unique_key_policy: dict | None = None, conflict_resolution_policy: dict | None = None, **extra)[source]
Create a container.
- Parameters:
name – Container id.
partition_key_path – Partition-key path (e.g.
"/id","/tenantId"). Defaults to"/id"permisc/docs/design_decisions.md§3.throughput – Provisioned RU/s (e.g. 400).
Noneuses the database’s shared throughput if any, otherwise the account default.indexing_policy – Standard Cosmos container kwargs.
default_ttl – Standard Cosmos container kwargs.
unique_key_policy – Standard Cosmos container kwargs.
conflict_resolution_policy – Standard Cosmos container kwargs.
**extra – Forwarded to
DatabaseProxy.create_container.
- class cosmodol.CosmosItems(container: ContainerProxy | dict | tuple, *, partition_key_value: Any, partition_key_path: str | None = None, connection: Any = None, inject_id: bool = True, inject_partition_key: bool = True, strict_keys: bool = True, strip_system_fields: bool = True, record_ru: Callable[[str, float], None] | None = None)[source]
MutableMapping[str, dict]over one fixed partition of a Cosmos container.Keys are item
idstrings; values are JSON-dict items. The store auto-injectsidand the partition-key property on writes.- Parameters:
container – An already-built
ContainerProxyor a(database, container)tuple /{"database": ..., "container": ...}dict to resolve viaconnection.partition_key_value – The single partition-key value this store is scoped to.
partition_key_path – Path of the partition-key property in items (e.g.
"/_pk","/id"). Read from the container if absent.connection –
CosmosConnection(or anythingfrom_anythingaccepts). Ignored ifcontaineris aContainerProxy.inject_id – If True, auto-inject
"id"into bodies on writes.inject_partition_key – If True, auto-inject the partition-key property into bodies.
strict_keys – Validate
idchars + length on writes.strip_system_fields – Strip
_etag/_ts/_rid/_self/_attachmentsfrom returned items.record_ru – Optional callback
(op_name, ru) -> Noneinvoked after each metal-layer op. Useful for Prometheus / logging.
- batch(operations: list[tuple]) list[dict][source]
Transactional batch within this partition. See
base.batch.
- class cosmodol.CosmosPartitionedItems(container: ContainerProxy | dict | tuple, *, partition_key_path: str | None = None, connection: Any = None, inject_id: bool = True, inject_partition_key: bool = True, strict_keys: bool = True, strip_system_fields: bool = True, record_ru: Callable[[str, float], None] | None = None, len_via_query: bool = False, silent_full_scan: bool = False)[source]
MutableMapping[tuple[str, str], dict]over all partitions of a container.Keys are
(partition_key_value, id)tuples. UseCosmosItemsinstead if you have a fixed partition.Iteration is cross-partition; the first call emits a
UserWarning(silence withsilent_full_scan=True).__len__is not implemented by default; opt-in vialen_via_query=True. Seemisc/docs/design_decisions.md§§4, 6.- Parameters:
container – As in
CosmosItems.partition_key_path – Path of the partition-key property in items. Read from the container if absent.
connection – As in
CosmosItems.inject_id – As in
CosmosItems.inject_partition_key – As in
CosmosItems.strict_keys – As in
CosmosItems.strip_system_fields – As in
CosmosItems.record_ru – As in
CosmosItems.len_via_query – If True,
__len__runs a (cross-partition) COUNT query.silent_full_scan – If True,
__iter__does not emit the cross-partition warning.
- partition(pk_value) CosmosItems[source]
Narrow to a single partition; zero round-trips. Returns
CosmosItems.
- exception cosmodol.CosmosThrottleError[source]
Wraps
CosmosHttpResponseErrorwith HTTP 429 (RU exhaustion).
- exception cosmodol.DatabaseNotEmptyError[source]
Raised on
del account_store[name]when the database has containers. Seemisc/docs/design_decisions.md§8.
- exception cosmodol.ItemAlreadyExistsError[source]
Raised on strict-create attempts when the item already exists.
- exception cosmodol.ItemNotFoundError[source]
Raised when an item does not exist for a (partition_key, id).
- exception cosmodol.KeyMismatchError[source]
Raised when a written body’s
id(or partition-key value) disagrees with the inferred value (from the dict key). Seemisc/docs/design_decisions.md§9.
- class cosmodol.ResponseHeaders(request_charge: float | None, etag: str | None)[source]
Subset of Cosmos response headers we surface for observability.
- request_charge
RU consumed by the operation.
Nonewhen the emulator (which does not populate this header) was the backend.- Type:
float | None
- etag
ETag of the (created / read / replaced) item, when applicable.
- Type:
str | None
- etag: str | None
Alias for field number 1
- request_charge: float | None
Alias for field number 0
- cosmodol.batch(container: ContainerProxy, operations: list[tuple], partition_key: Any) list[dict][source]
Transactional batch within one logical partition.
- Parameters:
operations – List of
(op_name, args_tuple, kwargs_dict)triples. Cosmos op names:"create","upsert","replace","patch","read","delete". ≤ 100 ops, ≤ 1.2 MB total.partition_key – All ops must share this partition-key value.
- Returns:
List of per-op result dicts as returned by the SDK.
- cosmodol.cosmos_store(*, database: str, container: str, connection: CosmosConnection | Any | None = None, credential: Any = None, connection_string: str | None = None, endpoint: str | None = None, key: str | None = None, partition_key_value: Any = None, partition_key_path: str | None = None, value_codec: Callable | None = None, strip_system_fields: bool = True)[source]
Build a ready-to-use Cosmos store (
CosmosItemsorCosmosPartitionedItems).The store flavor is chosen based on which partition-key kwarg is passed:
partition_key_value=...→CosmosItems(keys = id strings, fixed partition).partition_key_path=...→CosmosPartitionedItems(keys = (pk, id) tuples).Neither given → raises
ValueError.
- Parameters:
database – Database name.
container – Container name.
connection – A
CosmosConnection(or anythingfrom_anythingaccepts) to reuse a client. Mutually exclusive with explicit credential kwargs.credential – Forwarded to
CosmosConnectionwhenconnectionis None.connection_string – Forwarded to
CosmosConnectionwhenconnectionis None.endpoint – Forwarded to
CosmosConnectionwhenconnectionis None.key – Forwarded to
CosmosConnectionwhenconnectionis None.partition_key_value – Fixed partition key value → returns
CosmosItems.partition_key_path – Partition key property path → returns
CosmosPartitionedItems.value_codec – A decorator that, given a class, returns a wrapped class. Typically a partially-applied
dol.wrap_kvs(...).strip_system_fields – Strip Cosmos system fields from returned items.
- Returns:
A
CosmosItemsorCosmosPartitionedItemsinstance, possibly codec-wrapped.
- cosmodol.point_contains(container: ContainerProxy, id: str, partition_key: Any) tuple[bool, ResponseHeaders][source]
Existence check via point read.
True/False. Never raisesKeyError.Cheaper than a
SELECT VALUE COUNT(1)query — ~1 RU vs ≥ 2.3 RU.
- cosmodol.point_delete(container: ContainerProxy, id: str, partition_key: Any, *, etag: str | None = None) ResponseHeaders[source]
Point delete. Raises
ItemNotFoundErroron missing.
- cosmodol.point_get(container: ContainerProxy, id: str, partition_key: Any) tuple[dict, ResponseHeaders][source]
Point read of one item. ~1 RU/KB. Raises
ItemNotFoundErroron missing.
- cosmodol.point_replace(container: ContainerProxy, id: str, body: dict, partition_key: Any, *, etag: str | None = None) tuple[dict, ResponseHeaders][source]
Full replace of an item. With
etag, performs an If-Match conditional write.
- cosmodol.point_upsert(container: ContainerProxy, body: dict, *, etag: str | None = None) tuple[dict, ResponseHeaders][source]
Insert-or-replace an item.
idand the partition-key value are extracted frombody.Note:
etagparameter accepted for symmetry but Cosmos does not honor it onupsert_item; usepoint_replacefor ETag-conditional writes.
- cosmodol.query(container: ContainerProxy, sql: str, *, parameters: list[dict] | None = None, partition_key: Any = None, cross_partition: bool = False, max_item_count: int | None = None) Iterator[dict][source]
Run a SQL query. Yields dicts.
Either pass
partition_key=(cheap, single-partition) orcross_partition=True(RU scales with data). One of the two is required by Cosmos.
- cosmodol.resolve_credential(*, credential: str | dict | Any | None = None, connection_string: str | None = None, endpoint: str | None = None, key: str | None = None) dict[source]
Resolve a credential into a normalized form that can build a
CosmosClient.Cascade (first hit wins):
Explicit
credential=(withendpoint=for the URL)Explicit
connection_string=Explicit
endpoint=+key=(or justendpoint=+ AAD)Env var
AZURE_COSMOS_CONNECTION_STRINGEnv vars
AZURE_COSMOS_ENDPOINT+AZURE_COSMOS_KEYEnv var
AZURE_COSMOS_ENDPOINTalone +DefaultAzureCredential
- Returns:
{"url": "...", "credential": <obj>}- Return type:
A dict with
- Raises:
ValueError – if no source resolves.
- cosmodol.strip_system_fields(item: dict) dict[source]
Return
itemwith Cosmos system fields removed.
- cosmodol.translate_cosmos_errors(*, key_arg: int | str = 0, not_found_cls: type[KeyError] = <class 'cosmodol.errors.ItemNotFoundError'>, exists_cls: type[KeyError] = <class 'cosmodol.errors.ItemAlreadyExistsError'>) Callable[source]
Decorator: translate Cosmos SDK exceptions into
KeyErrorsubclasses.Auth errors and any other Cosmos errors propagate untouched. HTTP 429 throttling is wrapped in
CosmosThrottleErrorso callers can distinguish it. Seemisc/docs/design_decisions.md§11.- Parameters:
key_arg – Position (int) or name (str) of the key argument in the wrapped method’s signature. Used to populate
KeyError(key). Default 0; for methods the user-facing key is typically at index 1 (selfat 0).not_found_cls – Exception class to raise on
CosmosResourceNotFoundError.exists_cls – Exception class to raise on
CosmosResourceExistsError.