azuredol#
Access Azure Blob Storage through a Mapping interface.
azuredol exposes Azure Blob Storage as dol-style Mapping /
MutableMapping interfaces.
Quick start:
from azuredol import azure_store
# Uses connection_string env var or Azurite (UseDevelopmentStorage=true)
store = azure_store(
'mycontainer',
connection_string='UseDevelopmentStorage=true',
create_container_if_missing=True,
)
store['k1'] = b'hello world'
store['k1'] # → b'hello world'
'k1' in store # → True
del store['k1']
See misc/docs/architecture.md for the layered design.
Functions
|
Resolve a credential into a normalized form that can build a |
|
Decorator: translate Azure SDK exceptions into |
|
Build a ready-to-use |
|
A |
|
|
|
Delete container |
|
Hierarchical walk ( |
|
The sub-store of |
|
Context manager to start an Azure Function host using |
Classes
|
Holds a resolved credential and a lazy |
|
Mapping over container names in an account (iter / contains / len). |
|
Adds |
|
Read-write Mapping over containers in an account. |
|
Thin facade over one |
|
Iteration / membership / cardinality ( |
|
Adds |
|
The primary read-write Mapping over an Azure container. |
Exceptions
Raised on strict-create write attempts when the blob already exists. |
|
Raised when a blob does not exist. |
|
Raised on strict-create attempts when the container already exists. |
|
Raised on |
|
Raised when a container does not exist. |
- class azuredol.AccountCollection(connection=None, *, container_store_cls=None, container_store_kwargs=None)[source]#
Bases:
_AccountStoreBase,KvReaderMapping over container names in an account (iter / contains / len).
- class azuredol.AccountReader(connection=None, *, container_store_cls=None, container_store_kwargs=None)[source]#
Bases:
AccountCollectionAdds
__getitem__returning aContainerStore(or configured subclass).
- class azuredol.AccountStore(connection=None, *, container_store_cls=None, container_store_kwargs=None)[source]#
Bases:
AccountReader,KvPersisterRead-write Mapping over containers in an account.
__setitem__creates a container; the value is ignored except when it’s a Mapping (in which case it is treated as an initial bulk-load).__delitem__refuses non-empty containers — callazuredol.ops.delete_container(store, name, force=True)to acknowledge a cascading delete. Seemisc/docs/design_decisions.md§12.
- class azuredol.AzureConnection(credential=None, connection_string=None, account_url=None, account_key=None, client_kwargs=dict())[source]#
Bases:
objectHolds a resolved credential and a lazy
BlobServiceClient.This is the dependency-injection seam for the whole package. Tests construct one pointing at Azurite without touching any store class.
- Parameters:
All four args are optional; the credential cascade in
resolve_credentialis consulted if none are provided.- blob_client(container, blob)[source]#
Cheap derivation of a
BlobClientfrom the shared service client.
- container_client(container)[source]#
Cheap derivation of a
ContainerClientfrom the shared service client.
- classmethod from_anything(source)[source]#
Convenience: build an
AzureConnectionfrom a thing-or-spec.Accepts:
AzureConnection(returned as-is)BlobServiceClient(wrapped without further resolution)str(connection string)dict(passed as kwargs)None(defer to env / AAD)
- Return type:
- property service_client: BlobServiceClient#
The lazily-constructed
BlobServiceClient. Cached for the connection’s lifetime.
- azuredol.AzureJsonStore#
alias of
ContainerStore
- azuredol.AzurePickleStore#
alias of
ContainerStore
- azuredol.AzureTextStore#
alias of
ContainerStore
- exception azuredol.BlobAlreadyExistsError[source]#
Bases:
KeyErrorRaised on strict-create write attempts when the blob already exists.
KeyErrorsubclass for symmetry withBlobNotFoundError— both signal a key-vs-store mismatch.
- class azuredol.BlobHandle(container, blob, *, connection=None)[source]#
Bases:
objectThin facade over one
BlobClient. Not a Mapping.Use when you need the full Azure-blob surface for a single blob: metadata, properties, conditional writes, leases, copy, append, SAS URL generation, streaming downloads.
- Parameters:
container (
Union[str,ContainerClient]) – An existingContainerClientor a container name string.blob (
str) – The blob name (relative to the container root; full path including ‘/’).connection (
Union[AzureConnection,BlobServiceClient,str,dict,None]) – Ignored ifcontaineris aContainerClient. Otherwise resolved viaAzureConnection.from_anything.
- acquire_lease(lease_duration=-1)[source]#
Acquire a lease (15-60s, or -1 for infinite). Returns a
BlobLeaseClient.
- copy_from(source_url, *, requires_sync=None)[source]#
Server-side copy from a URL. Same-account = sync; cross-account = async (poll).
- create(data, *, overwrite=False, **kwargs)[source]#
Strict-create variant of
write; raisesBlobAlreadyExistsErroron conflict.- Return type:
- download_stream(*, chunk_size=None)[source]#
Return a streaming downloader. Iterate chunks via
.chunks()or.readinto(fp).
- properties()[source]#
Return
BlobProperties(size, last_modified, etag, content_settings, metadata, …).
- read(*, offset=None, length=None)[source]#
Download (possibly a range) and return bytes.
- Return type:
- exception azuredol.BlobNotFoundError[source]#
Bases:
KeyErrorRaised when a blob does not exist.
KeyErrorsubclass soMappingconsumers work.
- exception azuredol.ContainerAlreadyExistsError[source]#
Bases:
KeyErrorRaised on strict-create attempts when the container already exists.
- class azuredol.ContainerCollection(container, *, prefix='', connection=None, create_container_if_missing=False, blob_type=BlobType.BLOCKBLOB, upload_kwargs=None, download_kwargs=None)[source]#
Bases:
_ContainerStoreBase,KvReaderIteration / membership / cardinality (
__len__deliberately omitted) over a container.__len__is intentionally NOT implemented — seemisc/docs/design_decisions.md§2. Pagination cost is unbounded; users wanting a count callsum(1 for _ in store).- walk_ids(delimiter='/')[source]#
Hierarchical walk of the leaf’s prefix, yielding
BlobProperties/BlobPrefixnodes.Thin pass-through to
ContainerClient.walk_blobs. The scope is the leaf’s ownprefixand each node’s.nameis an absolute blob id (never a store key), so under adolkey codec it disagrees with__iter__. For a scope that follows the caller’s (possibly wrapped) store useazuredol.ops.walk(store).
- exception azuredol.ContainerNotEmptyError[source]#
Bases:
RuntimeErrorRaised on
del account_store[name]when the container has blobs.The user must call
azuredol.delete_container(account_store, name, force=True)to acknowledge the cascading delete. Seemisc/docs/design_decisions.md§12.
- exception azuredol.ContainerNotFoundError[source]#
Bases:
KeyErrorRaised when a container does not exist.
- class azuredol.ContainerReader(container, *, prefix='', connection=None, create_container_if_missing=False, blob_type=BlobType.BLOCKBLOB, upload_kwargs=None, download_kwargs=None)[source]#
Bases:
ContainerCollectionAdds
__getitem__(returns bytes) overContainerCollection.The trailing-slash sub-store convention lives here:
store['sub/']returns a newContainerReaderwith extended prefix and zero round-trips. This works on a bare store and on typed-store classes, but not through every wrapper stack (adolwrapper placed on an instance is not carried into the sub-store);azuredol.substore()is the portable form and should be preferred (i2mint/azuredol#9).
- class azuredol.ContainerStore(container, *, prefix='', connection=None, create_container_if_missing=False, blob_type=BlobType.BLOCKBLOB, upload_kwargs=None, download_kwargs=None)[source]#
Bases:
ContainerReader,KvPersisterThe primary read-write Mapping over an Azure container.
Doctest below requires Azurite (or live Azure). It is shown but not auto-run.
Example (requires Azurite):
>>> from azuredol import ContainerStore >>> s = ContainerStore( ... 'my-test-container', ... connection='UseDevelopmentStorage=true', ... create_container_if_missing=True, ... ) >>> s['k1'] = b'v1' >>> s['k1'] b'v1' >>> 'k1' in s True >>> del s['k1']
For unit tests without Azurite, prototype with
dol’s in-memory pattern:wrap_kvs(dict(), ...).
- azuredol.azure_func_service(rootfolder, extra_args='', *, verbose=True, wait_for_log=None, timeout=30, print_output=True)[source]#
Context manager to start an Azure Function host using
func startin the specified folder.- Parameters:
rootfolder (str) – The directory containing the Azure Function App.
extra_args (str) – Additional command-line arguments for the
func startcommand.wait_for_log (str) – A substring or regular expression pattern that indicates when the service is ready. If provided, the context manager waits until a matching log line is detected.
timeout (int) – Maximum time in seconds to wait for the ready log message.
print_output (bool) – If True, prints the process output to stdout.
- Yields:
subprocess.Popen – The process object representing the running Azure Functions host.
- Usage Example:
- with azure_func_service(‘/path/to/azure_func’, extra_args=”–verbose”, wait_for_log=r’Host started’) as proc:
# Place code here to test the HTTP service. …
- azuredol.azure_store(container, *, prefix='', connection=None, credential=None, connection_string=None, account_url=None, account_key=None, create_container_if_missing=False, blob_type=BlobType.BLOCKBLOB, value_codec=None)[source]#
Build a ready-to-use
ContainerStore(or codec-wrapped variant) in one call.This is the “simple things simple” entry point. For complex configurations construct
AzureConnectionandContainerStoredirectly.- Parameters:
container (
str) – Container name.prefix (
str) – Blob-name prefix to scope all keys.connection (
Union[AzureConnection,str,dict,None]) – AnAzureConnection(or anythingAzureConnection.from_anythingaccepts) to reuse a service client. Mutually exclusive with the explicitcredential/connection_string/account_*kwargs.credential (
Any) – Forwarded toAzureConnectionwhenconnectionis None.connection_string (
Optional[str]) – Forwarded toAzureConnectionwhenconnectionis None.account_url (
Optional[str]) – Forwarded toAzureConnectionwhenconnectionis None.account_key (
Optional[str]) – Forwarded toAzureConnectionwhenconnectionis None.create_container_if_missing (
bool) – Create the container on first use if absent.blob_type (
BlobType) – Default blob type for writes.value_codec (
Optional[Callable]) – A decorator that, given a class, returns a wrapped class. Typicallytyped_codec(decode, encode). A baredol.wrap_kvs(...)breaks the trailing-slash sub-store (i2mint/azuredol#4). When None (default), bytes pass through.
- Returns:
A
ContainerStoreinstance, possibly codec-wrapped.
- azuredol.delete_container(store, name, *, force=False)[source]#
Delete container
name(a key ofstore); withforce=Truecascade-delete its blobs.The deletion is
del store[name]on the caller’s own store, so a key codec mapsname, a read-only wrapper refuses, and a scoped view cannot reach out of scope.- Raises:
ContainerNotFoundError – if
nameis not a container ofstoreContainerNotEmptyError – if the container has blobs and
forceis false
- Return type:
- azuredol.resolve_credential(*, credential=None, connection_string=None, account_url=None, account_key=None)[source]#
Resolve a credential into a normalized form that can build a
BlobServiceClient.Cascade (first hit wins):
Explicit
credential=Explicit
connection_string=Explicit
account_url=+account_key=(or justaccount_url=+ AAD)Env var
AZURE_STORAGE_CONNECTION_STRINGEnv vars
AZURE_STORAGE_ACCOUNT_URL+AZURE_STORAGE_ACCOUNT_KEYEnv var
AZURE_STORAGE_ACCOUNT_URLalone +DefaultAzureCredential
- Returns:
{"conn_str": "..."}# for from_connection_string{"account_url": "...", "credential": <obj>}# for __init__- Return type:
- Raises:
ValueError – if no source resolves.
- azuredol.substore(store, key)[source]#
The sub-store of
storeunderkey(a key ending in'/'), as the caller sees it.This is the portable form.
store['a/']is kept and works on bare stores and typed-store classes, but not through every wrapper stack; prefersubstoreeverywhere else.substore(store, 'a/')[k]isstore['a/' + k]for every wrapper stack onstore(up to the key limits below). On a bareContainerStoreit is the cheap, server-scopedstore['a/'](listing onlya/’s blobs). On anything wrapped (adolkey/value codec or scope, including the classestyped_store/AzureJsonStorebuild) it is aKeyCodecs.prefixed(key)view of that store, so the caller’s key and value mappings still apply; that view lists the wrapped store’s whole scope and keeps the keys underkey. (store['a/']on a store wrapped as an instance is minted by the innermost class and does not know about the wrappers: i2mint/azuredol#6.)The cheap path needs a leaf that does not override
_id_of_key(a subclass that maps keysgets the view). Attribute lookups on the view fall through to the wrapped store, so
.prefixand.walk_ids()of a view describe the parent’s scope: usewalk()on it. Needsdol>=0.3.75(its prefix view skips out-of-scope keys when iterating).- Parameters:
store – a
ContainerReader/ContainerStore, wrapped or notkey (
str) – a key ending in'/', without a leading'/'or an inner'//'
- azuredol.translate_azure_errors(*, key_arg=0, not_found_cls=<class 'azuredol.errors.BlobNotFoundError'>, exists_cls=<class 'azuredol.errors.BlobAlreadyExistsError'>)[source]#
Decorator: translate Azure SDK exceptions into
KeyErrorsubclasses.Auth errors (
ClientAuthenticationError) and all other Azure errors propagate untouched — they are never swallowed as “key absent”. Seemisc/docs/design_decisions.md§4.- Parameters:
key_arg (
int|str) – Position (int) or name (str) of the key argument in the wrapped method’s signature. Used to populateKeyError(key). The receiverselfis at index 0, so the user-facing key is typically at index 1 (the default).not_found_cls (
type[KeyError]) – Exception class to raise onResourceNotFoundError.exists_cls (
type[KeyError]) – Exception class to raise onResourceExistsError.
- Return type:
- azuredol.typed_codec(decode, encode)[source]#
typed_storeas a class decorator: the shapeazure_store(value_codec=...)takes.>>> import json >>> codec = typed_codec(json.loads, lambda o: json.dumps(o).encode()) >>> callable(codec) True
- Return type:
- azuredol.typed_store(decode, encode, *, base=<class 'azuredol.base.ContainerStore'>)[source]#
A
base(defaultContainerStore) class with valuesdecode(bytes)/encode(obj) -> bytes.Unlike a bare
wrap_kvs(ContainerStore, obj_of_data=..., data_of_obj=...), the trailing-slash sub-store convention keeps working:store['2026/']comes back as a sub-store of the same typed class, instead of being fed todecode(i2mint/azuredol#4).
- azuredol.walk_container(store, delimiter='/')#
Hierarchical walk (
BlobProperties/BlobPrefixnodes) ofstore’s own scope.The scope is the blob prefix the caller’s empty key resolves to, so a key codec’s prefix is included (a codec that is not a pure prefix, e.g. a suffix, raises
ValueError; unlikeContainerCollection.walk_ids, which sees only the leaf’s prefix).
Modules
Close-to-metal Mapping stores over Azure Blob Storage. |
|
Connection-layer for azuredol. |
|
Custom exceptions and error-translation decorator for azuredol. |
|
Azure functions tools |
|
Key-taking and scope-wide operations, as free functions over the caller's (possibly wrapped) store. |
|
Convenience wrappers and factories for azuredol. |
|
Testing helpers for azuredol — Azurite fixture and convenience builders. |