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.1.0.tar.gz (46.4 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.1.0-py3-none-any.whl (14.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hawkapi_storage-0.1.0.tar.gz
  • Upload date:
  • Size: 46.4 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.1.0.tar.gz
Algorithm Hash digest
SHA256 af4fd51de063083f452da8f022288850244596ed0b4247ec97887f6c2b1f3faa
MD5 2976929f15bd0206dcb4d1c46bb506d4
BLAKE2b-256 b7f1c472fa3bf32d3ad8ec8a957380130a670a23c4cf3972454b5ddbd3e32123

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.1.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.1.0-py3-none-any.whl.

File metadata

  • Download URL: hawkapi_storage-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 015c7b0aa4ab8649200298567c0a1d2d6523cdcbbd89e8bbbcbb3784f4375427
MD5 561a3cd4cba3291ad1d87ed7433f8617
BLAKE2b-256 0649455d985d162557c81b7189bb914456a64f37dec47bb796ba535a58f671c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_storage-0.1.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