Skip to main content

S3-compatible storage library with automatic fallback support

Project description

fallbacks3

S3-compatible storage library with automatic fallback for the upload process.

Installation

pip install fallbacks3

For development:

pip install -e .

Configuration

You must configure a comma-separated list of providers, as well as the scheme of the fallback provider, with environment variables named PROVIDERS and FALLBACK_PROVIDER.

# Comma-separated list of provider URIs
PROVIDERS="ps3://access_key:secret_key@ps3.palver.com,r2://access_key:secret_key@account.r2.cloudflarestorage.com"

# Fallback provider scheme (must be one of the providers in PROVIDERS)
FALLBACK_PROVIDER="r2"

FALLBACK_PROVIDER may also be set as a <scheme>://<bucket> URI to redirect fallback uploads to a specific bucket instead of reusing the original request's bucket. The scheme must still be one of the providers in PROVIDERS.

# On fallback, upload to bucket "bucket-v2" on the r2 provider
FALLBACK_PROVIDER="r2://bucket-v2"

Provider URI Format

Provider URIs follow the format:

<provider_scheme>://<access_key>:<secret_key>@<endpoint>

Examples: r2://key:secret@account.r2.cloudflarestorage.com, local://test:testkey@localhost:9000

Remote File URI Format

File URIs should specify the exact location from a provider bucket:

<provider>://<bucket>/<file_path>

Examples: ps3://palver-whatsapp/audio.mp3, s3://my-bucket/documents/report.pdf

Usage

from fallbacks3 import Storage

# Initialize storage using environment variables (PROVIDERS and FALLBACK_PROVIDER)
storage = Storage()

# Upload a local file to the provided remote path
stored_file_uri = storage.upload_file(
    remote_file_uri="ps3://palver-whatsapp/audio.mp3",
    local_file_path="/local/path/audio.mp3"
)
print(uri)  # "ps3://palver-whatsapp/audio.mp3"

# Async upload (for async environments)
stored_file_uri = await storage.upload_file_async(
    remote_file_uri="ps3://palver-whatsapp/audio.mp3",
    local_file_path="/local/path/audio.mp3"
)

# Download a file
downloaded_file_path = storage.download_file(
    remote_file_uri="ps3://palver-whatsapp/audio.mp3",
    local_file_path="/local/path/audio.mp3"
)
print(local_path)  # "/local/path/audio.mp3"

# Generate a signed URL for temporary access (default: 60 seconds)
signed_url = storage.generate_signed_url(
    remote_file_uri="ps3://palver-whatsapp/audio.mp3",
    expiration=3600
)
print(signed_url)  # "https://ps3.palver.com/palver-whatsapp/audio.mp3?signature=..."

Upload with Automatic Fallback

If the primary provider fails for the upload method, the library automatically retries with the fallback provider (using the same bucket and file path, unless FALLBACK_PROVIDER specifies a bucket), as to ensure files are not lost.

# If ps3 fails, automatically falls back to r2
stored_file_path = storage.upload_file(
    remote_file_uri="ps3://palver-whatsapp/audio.mp3",
    local_file_path="/local/path/audio.mp3"
)
# Returns "r2://palver-whatsapp/audio.mp3" if ps3 failed

Raises an exception if both primary and fallback providers fail.

Async Upload

For async environments, use upload_file_async():

import asyncio
from fallbacks3 import Storage

storage = Storage()

async def upload():
    stored_file_uri = await storage.upload_file_async(
        remote_file_uri="ps3://palver-whatsapp/audio.mp3",
        local_file_path="/local/path/audio.mp3"
    )
    print(stored_file_uri)  # "ps3://palver-whatsapp/audio.mp3"

asyncio.run(upload())

The async method also supports automatic fallback, just like the synchronous version.

Download and Signed URLs

Download and signed URL generation use the provider specified in the remote file URI.

Development

Install development dependencies:

pip install -e ".[dev]"

You can also develop with uv if available.

uv pip install -e ".[dev]"

Run tests:

pytest --cov=fallbacks3

# alternatively, with uv:
uv run pytest --cov=fallbacks3

Publishing to PyPI

The project is set up to be automatically uploaded to PyPI when a new tag is pushed. For a successful push, all tests must pass and the project must pass the check and format requirements.

One-time setup

Before publishing for the first time:

  1. Configure Trusted Publishing on PyPI:

You must have access to the pypi GitHub environment or be approved by required reviewers (milasd or giancarlopro) to publish.

Publishing a new version

  1. Update version in pyproject.toml
  2. Create and push a tag:
    git tag v[new version] # eg.: git tag v0.1.0
    git push origin v[new version] # eg.: git push origin v0.1.0
    

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

fallbacks3-0.5.0.tar.gz (8.7 kB view details)

Uploaded Source

Built Distribution

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

fallbacks3-0.5.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file fallbacks3-0.5.0.tar.gz.

File metadata

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

File hashes

Hashes for fallbacks3-0.5.0.tar.gz
Algorithm Hash digest
SHA256 2535ca0f4d242adb3b390db8f53c7b7211983fbbf5799985b8834eb39b8d78c7
MD5 4ebf57dc0efd58b6db0278c9da6bd522
BLAKE2b-256 11f8b6185057bd75ad2eb2cbc3144ea90626959489b0e847a1b4ab237dd28c43

See more details on using hashes here.

Provenance

The following attestation bundles were made for fallbacks3-0.5.0.tar.gz:

Publisher: publish.yaml on palverdata/fallbacks3

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

File details

Details for the file fallbacks3-0.5.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for fallbacks3-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6a34644e0d14c87e095a02229917a8048ff79a6fb8206dd4d9434109dc25c8bf
MD5 7b0a035c0b10c263e88407f8b47ca7af
BLAKE2b-256 2464e4bc9da2acfd08886f089b747f510e8f91e4b36de911283a2aa4103a0f55

See more details on using hashes here.

Provenance

The following attestation bundles were made for fallbacks3-0.5.0-py3-none-any.whl:

Publisher: publish.yaml on palverdata/fallbacks3

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