Skip to content

multicloud.common

Cross-service base infrastructure — the SubstrateSdkException hierarchy and the Provider / ProviderRegistry primitives every service builds on.

Exceptions

Cloud-agnostic exception hierarchy.

Providers translate their native errors into one of these types so client code can write a single except clause that works across substrates.

SubstrateSdkException

Bases: Exception

Root of all SDK exceptions — catch this to handle any SDK error generically.

InvalidArgumentException

Bases: SubstrateSdkException

Caller provided invalid input (empty key, bad type, failed validation).

ResourceNotFoundException

Bases: SubstrateSdkException

Target resource (bucket, object, role) does not exist.

ResourceExistsException

Bases: SubstrateSdkException

Attempted to create a resource that already exists (e.g., duplicate bucket).

ResourceConflictException

Bases: SubstrateSdkException

Resource state conflicts with the requested operation (e.g., delete role with attached policies).

Also raised when a concurrent write races a conditional write: the precondition may never have been evaluated and the write may not have happened, so this is a retry signal, distinct from the terminal ResourceExistsException / FailedPreconditionException. Retry the write.

UnAuthorizedException

Bases: SubstrateSdkException

Credentials missing, expired, or lack required permissions.

FailedPreconditionException

Bases: SubstrateSdkException

Operation rejected because a precondition wasn't met (e.g., non-empty bucket delete).

UnsupportedOperationException

Bases: SubstrateSdkException

The requested operation is not supported by this provider.

ResourceExhaustedException

Bases: SubstrateSdkException

Rate limit exceeded, quota exhausted, or throttling applied.

DeadlineExceededException

Bases: SubstrateSdkException

Operation timed out or deadline exceeded.

TransactionFailedException

Bases: SubstrateSdkException

Internal service failure during transaction.

ResourceAlreadyExistsException

Bases: SubstrateSdkException

Attempted to create a resource that already exists.

UnknownException

Bases: SubstrateSdkException

Catch-all for errors that don't map to a more specific type.

Provider registry

Provider discovery registry.

A small in-process registry keyed by provider id (e.g. "aws", "gcp"). Provider classes register themselves with the @registry.register("aws") decorator at import time; the user-facing client (e.g. BucketClient) looks up the right class by id at construction time.

This is a service-locator pattern — it emerges when a single API must dispatch to multiple backend implementations at runtime.

Provider modules in this SDK are loaded lazily from setuptools entry points. Each provider distribution declares an entry point in the multicloud.<svc>.providers group; :meth:ProviderRegistry.discover imports those modules on the first :meth:ProviderRegistry.get call so the @register decorators run and the providers become resolvable.

Provider

Bases: ABC

Base class for all provider implementations across all services.

Every provider (for blob, STS, or any other service) must implement error translation to map provider-specific exceptions to the common SubstrateSdkException hierarchy.

get_provider_id

get_provider_id() -> str

Return the provider identifier (e.g., 'aws', 'gcp', 'ali'). Read from the class attribute stamped by the registry decorator.

translate_error abstractmethod

translate_error(exc: Exception) -> SubstrateSdkException

Translate provider-specific exceptions to the common hierarchy.

Each provider maps its native SDK errors into SubstrateSdkException subtypes. This normalizes diverse error shapes into a single hierarchy that client code can catch uniformly.

Parameters:

Name Type Description Default
exc Exception

The provider-specific exception to translate.

required

Returns:

Type Description
SubstrateSdkException

A SubstrateSdkException subclass instance.

ProviderRegistry

ProviderRegistry(
    service_name: str, entry_point_group: str | None = None
)

A per-service registry that maps provider_id → provider class.

Each service (blob, sts, docstore, ...) owns its own registry instance so the namespaces don't collide. A lightweight service-locator that avoids hard imports of optional dependencies.

Provider discovery is via setuptools entry points. The registry is constructed with an entry_point_group (e.g. "multicloud.blob.providers") that provider distributions advertise themselves under. The registry imports those modules lazily on the first :meth:get call; each provider module's import side-effect is to call @registry.register(...) on its class.

register

register(
    provider_id: str,
) -> Callable[[Type[Provider]], Type[Provider]]

Class decorator that registers a provider implementation. Stamps provider_id on the class and adds it to the lookup table.

record_import_error

record_import_error(
    provider_id: str, exc: ImportError
) -> None

Stash an ImportError for deferred reporting (lazy-import pattern).

The error surfaces with a helpful "pip install" hint when the user first tries to use that provider.

discover

discover() -> None

Import every provider module advertised in our entry-point group.

Idempotent. Safe to call multiple times. Each provider module's import side-effect (the @registry.register("aws") decorator) populates the registry. Missing optional dependencies surface as ImportError and are recorded under the entry-point name so a later get(...) call yields a clear "pip install" hint.

get

get(provider_id: str) -> Type[Provider]

Look up a provider class by id — the core dispatch mechanism. Raises ValueError with actionable guidance if not found or import failed.

ids

ids() -> List[str]

Return sorted list of registered provider ids (for display/logging).