Cloud Storage Manager
One async interface over AWS S3, Google Cloud Storage and Azure Blob Storage. Application code calls the same methods everywhere; switching cloud is a configuration change.
Features
- One interface for AWS S3, Google Cloud Storage and Azure Blob Storage
- Async operations
- Upload from a file or from bytes, download, list, delete, signed read URLs
upload_bytesstores a content type, so a signed video URL plays in a browser- Type-safe configuration using Pydantic
- A drop-in upgrade from 0.1.x (see below)
Installation
The base install covers AWS S3 and Google Cloud Storage, as 0.1.x did. Azure is an extra:
pip install 10x-cloud-storage-manager # AWS S3 + Google Cloud Storage
pip install "10x-cloud-storage-manager[azure]" # adds Azure Blob Storage
pip install "10x-cloud-storage-manager[all]" # all three
The import name is still cloud_storage_manager. From 0.2.0 the package is published as
10x-cloud-storage-manager; cloud-storage-manager on PyPI stops at 0.1.2.
Requires Python 3.10+.
Quick start
import asyncio
from cloud_storage_manager import CloudStorageFactory, StorageConfig
async def main(config: StorageConfig):
storage = CloudStorageFactory.from_config(config)
await storage.upload("local/clip.mp4", "recordings/clip.mp4")
await storage.upload_bytes(b"# Notes", "notes/a.md", content_type="text/markdown")
data = await storage.download_bytes("notes/a.md")
names = await storage.list_objects("recordings/")
sizes = await storage.list_object_sizes("recordings/") # {name: bytes}
url = await storage.get_public_url("recordings/clip.mp4", expiration=3600)
deleted = await storage.delete_if_exists("notes/a.md") # False if it was already gone
CloudStorageFactory.from_config picks the provider from the config.
CloudStorageFactory.get_storage(StorageProvider.GCP, config) still works.
Configuration
CloudStorageFactory.get_storage(provider, config) uses the provider you name, so a config may
hold more than one. config.provider and from_config infer it, and raise ValueError unless
exactly one is set.
AWS S3
from cloud_storage_manager import AwsConfig, StorageConfig
config = StorageConfig(
aws=AwsConfig(
bucket_name="my-bucket",
access_key_id="...",
secret_access_key="...",
region="us-east-1", # stored but not used; boto resolves the region, as in 0.1.x
)
)
Google Cloud Storage
from cloud_storage_manager import GcpConfig, StorageConfig
config = StorageConfig(
gcp=GcpConfig(
bucket_name="my-bucket",
project_id="my-project",
credentials={...}, # service account JSON as a dict
)
)
Azure Blob Storage
Give exactly one of account_key or connection_string.
from cloud_storage_manager import AzureConfig, StorageConfig
config = StorageConfig(
azure=AzureConfig(
account_name="mystorageaccount",
container_name="my-container",
account_key="...",
# or: connection_string="DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;"
)
)
get_public_url on Azure returns a read-only blob SAS URL, signed with the account key. A
connection string that carries a SAS instead of an AccountKey can read and write but cannot
sign URLs.
Interface
Every provider implements BaseCloudStorage:
| Method | Returns | Notes |
|---|---|---|
upload(file_path, cloud_path) |
str |
GCP and Azure type the object from its file name; S3 does not (0.1.x behaviour) |
upload_bytes(data, cloud_path, content_type=None) |
str |
Content type guessed from cloud_path when omitted |
download_bytes(cloud_path) |
bytes |
NotFoundError if missing |
list_objects(prefix) |
list[str] |
Every page |
list_object_sizes(prefix) |
dict[str, int] |
Name to size in bytes |
delete(cloud_path) |
bool |
0.1.x behaviour: a missing object raises DeleteError on GCP and Azure, returns True on S3 |
delete_if_exists(cloud_path) |
bool |
False if it did not exist, on every cloud |
get_public_url(cloud_path, expiration=3600) |
str |
Signed, read-only, expires after expiration seconds |
Error handling
Every error is a CloudStorageError: UploadError, DownloadError, NotFoundError,
ListError, DeleteError. The SDK's own exception is kept as __cause__.
from cloud_storage_manager import NotFoundError
try:
data = await storage.download_bytes("missing.txt")
except NotFoundError:
data = None
Upgrading from 0.1.x
Change only the requirement: swap cloud-storage-manager for 10x-cloud-storage-manager
(uninstall the old package first, since both ship the cloud_storage_manager module). Your code
does not change: every 0.1.x call behaves as before.
pip uninstall -y cloud-storage-manager
pip install 10x-cloud-storage-manager
- The base install pulls the same packages as 0.1.x; Azure is the
[azure]extra. StorageConfigaccepts every combination 0.1.x accepted.upload,deleteandget_public_urlbehave as in 0.1.x on S3 and GCP; the newdelete_if_existsis the one to use for the same answer on every cloud.- Only those three methods are abstract, so a subclass written for 0.1.x still instantiates.
AWSClientStorageandGcpClientStorageare still importable fromcloud_storage_manager.storage_provider.- Python 3.10+ (0.1.x already used 3.10 syntax and did not import on 3.8 or 3.9).
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest --cov
ruff check . && ruff format --check .
Tests mock every cloud SDK and never call a real cloud.
Branches and releases
devis the default branch. Open feature PRs against it.prodis the release branch. Every merge intoprodpublishes to PyPI and creates av<version>GitHub release (.github/workflows/release.yml).- Bump
[project].versioninpyproject.tomlin the PR toprod. CI fails the PR if PyPI already has that version. - Publishing uses the
PYPI_API_TOKENsecret on thepypiGitHub environment, which only theprodbranch can deploy to. Never upload to PyPI by hand.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Metadata
Release files for 10x-cloud-storage-manager 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| 10x_cloud_storage_manager-0.2.0.tar.gz | 19.7 kB | Without provenance | Details for 10x_cloud_storage_manager-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 10x_cloud_storage_manager-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for 10x_cloud_storage_manager-0.2.0-py3-none-any.whl |
Total release size: 38.0 kB
Release files / 10x_cloud_storage_manager-0.2.0.tar.gz
| Download URL | 10x_cloud_storage_manager-0.2.0.tar.gz |
|---|---|
| Size | 19.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b4fac0acde7d6994e16f24008461b4a633d24ff8c7f34d0a0d7453ea15fbb140
|
|
BLAKE2b-256 checksum How to use checksums |
ecc3a00c12c5b2c85b74bea88b91d08fc8d066901484e36c18bb0f74f13f638b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / 10x_cloud_storage_manager-0.2.0-py3-none-any.whl
| Download URL | 10x_cloud_storage_manager-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2df2f95710c415cf79b0b3f322ddbd67739e9876e5c7a7b396e03aba60e6d28a
|
|
BLAKE2b-256 checksum How to use checksums |
fb40b222b1cc5e6df2018bf98deafa50e7c3f5f0bbc5263e062def365f45376a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for 10x-cloud-storage-manager 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.