Skip to main content

File storage for HawkAPI — local, S3, GCS, Azure backends, pre-signed URLs, streaming uploads

Project description

hawkapi-storage

Pluggable file storage for HawkAPI. One Storage protocol, four backends: local filesystem, AWS S3 (and S3-compatible — MinIO, Wasabi, R2), Google Cloud Storage, Azure Blob Storage. Pre-signed URLs and streaming on all of them.

Install

pip install hawkapi-storage                  # local filesystem only
pip install 'hawkapi-storage[s3]'            # + AWS S3
pip install 'hawkapi-storage[gcs]'           # + Google Cloud Storage
pip install 'hawkapi-storage[azure]'         # + Azure Blob Storage

Quickstart

from hawkapi import Depends, HawkAPI
from hawkapi_storage import LocalConfig, LocalStorage, Storage, get_storage, init_storage

app = HawkAPI()
init_storage(app, storage=LocalStorage(LocalConfig(root="/var/data", base_url="https://cdn.example")))


@app.put("/files/{key}")
async def upload(key: str, body: bytes, s: Storage = Depends(get_storage)):
    obj = await s.put(key, body, content_type="application/octet-stream")
    return {"key": obj.key, "size": obj.size}


@app.get("/files/{key}/url")
async def signed(key: str, s: Storage = Depends(get_storage)):
    return {"url": await s.signed_url(key, expires_in=300)}

Swap LocalStorage for any other backend — every primitive is identical.

Backends

from hawkapi_storage import (
    LocalStorage, LocalConfig,
    S3Storage,    S3Config,        # extras: [s3]
    GCSStorage,   GCSConfig,       # extras: [gcs]
    AzureStorage, AzureConfig,     # extras: [azure]
)

local = LocalStorage(LocalConfig(root="/var/data"))
s3    = S3Storage(S3Config(bucket="my-bucket", region="eu-west-1"))
minio = S3Storage(S3Config(bucket="mb", endpoint_url="https://minio.example", use_path_style=True))
gcs   = GCSStorage(GCSConfig(bucket="my-bucket", project="my-project"))
azure = AzureStorage(AzureConfig(container="files", connection_string="..."))

The Storage protocol

class Storage(Protocol):
    name: str

    async def put(self, key, data, *, content_type=None, metadata=None) -> StoredObject: ...
    async def get(self, key) -> bytes: ...
    async def stream(self, key, *, chunk_size=65536) -> AsyncIterator[bytes]: ...
    async def exists(self, key) -> bool: ...
    async def delete(self, key) -> None: ...
    async def head(self, key) -> StoredObject: ...
    async def list(self, prefix="", *, limit=1000) -> AsyncIterator[StoredObject]: ...
    async def signed_url(self, key, *, expires_in=3600, method="GET", content_type=None) -> str: ...

put() accepts bytes, a file-like object, or an AsyncIterator[bytes] (for streaming uploads).

Streaming downloads

@app.get("/download/{key}")
async def download(key: str, s: Storage = Depends(get_storage)):
    return StreamingResponse(s.stream(key, chunk_size=65536),
                             media_type=(await s.head(key)).content_type)

Pre-signed URLs

Every backend supports signed_url(key, expires_in=..., method="GET" | "PUT"). For PUT/upload pre-signs, pass content_type= so the client must send the matching Content-Type header.

LocalStorage produces HMAC-signed URLs that you verify on download with local.verify_signed_url(key, expires, sig, method="GET") — useful when serving downloads through your own handler.

Local filesystem details

  • Path traversal (..) is rejected at put/get time.
  • LocalConfig(base_url=...) sets the prefix used by signed_url() — pair it with a Nginx alias or a HawkAPI download handler.
  • LocalConfig(signing_secret=...) lets you pin the HMAC secret (otherwise generated once at startup).

Errors

  • StorageError — base class.
  • NotFoundError(key)get / head / stream on a missing key.

Development

git clone https://github.com/ashimov/hawkapi-storage.git
cd hawkapi-storage
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hawkapi_storage-0.2.0.tar.gz (48.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hawkapi_storage-0.2.0-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file hawkapi_storage-0.2.0.tar.gz.

File metadata

  • Download URL: hawkapi_storage-0.2.0.tar.gz
  • Upload date:
  • Size: 48.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_storage-0.2.0.tar.gz
Algorithm Hash digest
SHA256 00d66cb2c39236dc956f98057a561c23055cee22e2311b7c753a4cb9a4ea2a6b
MD5 f1367745177f12d80d62f067e9ff5a58
BLAKE2b-256 0edd71cc9cadf144699cfb9556dce52ba1abf5e76fedbaf123e122af697f901c

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.2.0.tar.gz:

Publisher: release.yml on ashimov/hawkapi-storage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hawkapi_storage-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: hawkapi_storage-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_storage-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 33aca17e5456df4cacee033f99b2c76353222aca4aed1d0425d4428e91269d43
MD5 a02c0ce4c276721dc56cb2f393b5b720
BLAKE2b-256 fa9716da6ec94481ae07eef0c5d8f6a50dcb5fb375286db573c997558992795f

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.2.0-py3-none-any.whl:

Publisher: release.yml on ashimov/hawkapi-storage

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page