Skip to main content

s3func

Simple functions for working with S3-compatible object storage

build codecov PyPI version


s3func is a lightweight Python library providing a simplified interface for interacting with S3-compatible object storage services (AWS S3, Backblaze B2, MEGA S4, and others). It removes the boto3 dependency in favor of a fast, urllib3-based client with custom SigV4 signing.

Key Features

  • Zero Boto3 Dependency: Minimal overhead and faster imports.
  • Provider-Agnostic: One S3Session for AWS S3 and any S3-compatible provider (Backblaze B2, MEGA S4, ...), with provider quirks handled portably (strict RFC3986 signing of paths and queries).
  • Distributed Locking: Verified shared/exclusive locking on plain object storage - no CAS required (see docs/locking.md).
  • Streaming Support: Efficiently stream large objects.
  • Automatic Retries: urllib3 retries with exponential backoff for connection errors and transient HTTP statuses (429/5xx); Retry-After honored; non-idempotent POSTs are never status-retried.
  • Slow-link-safe transfers (0.9.4): timeouts are true idle bounds, never total-request deadlines — large uploads are streamed in chunks (bytes bodies over 1 MiB are wrapped automatically), so a slow-but-progressing multi-minute transfer never times out while a genuinely stalled socket still dies within read_timeout/connect_timeout.

Installation

pip install s3func

Usage Examples

S3 Operations

from s3func import S3Session

# Initialize session for AWS S3
session = S3Session(
    access_key_id='YOUR_ACCESS_KEY',
    access_key='YOUR_SECRET_KEY',
    bucket='my-bucket',
    region='us-east-1'
)

# Also works with other S3-compatible providers (Contabo, Wasabi, DigitalOcean, etc.)
# by providing an endpoint_url.
session = S3Session(
    access_key_id='YOUR_ACCESS_KEY',
    access_key='YOUR_SECRET_KEY',
    bucket='my-bucket',
    endpoint_url='https://eu2.contabostorage.com'
)

# Upload an object
session.put_object('hello.txt', b'Hello, S3!')

# Download an object
resp = session.get_object('hello.txt')
print(resp.data.decode())

# List objects
for obj in session.list_objects(prefix='logs/').iter_objects():
    print(obj['key'], obj['content_length'])

Custom Metadata

You can easily read and write custom metadata headers.

# Upload with metadata
session.put_object(
    'data.csv', 
    b'col1,col2\n1,2', 
    metadata={'processed': 'false', 'source': 'sensor-1'}
)

# Read metadata
resp = session.head_object('data.csv')
print(resp.metadata['processed']) # 'false'

Distributed Locking

s3func provides a powerful distributed lock that mimics Python's threading.Lock API.

# Using S3 Lock via context manager
with session.lock('process-1'):
    # This block is protected by a distributed lock
    print("Doing some exclusive work...")

# Explicit acquire/release with timeout
lock = session.lock('my-resource')
if lock.acquire(blocking=True, timeout=10):
    try:
        # Perform operation
        pass
    finally:
        lock.release()

Performance Tips

  • Streaming: Set stream=True in the session (default) or individual requests to handle large files without loading them entirely into memory.
  • Retries: All sessions share one urllib3 Retry policy (max_attempts retries, exponential backoff): connection errors and transient statuses (429, 500, 502, 503, 504) are retried on idempotent methods; when retries exhaust, the final response is returned (never an exception) so callers can inspect resp.status/resp.error. POST requests (S3 multi-object-delete, B2-native uploads) are never status-retried.

Changes between releases are tracked in CHANGELOG.md.

How Distributed Locking Works

Full walk-through with diagrams: docs/locking.md.

The lock is a Lamport-bakery-style election over plain object storage (no compare-and-swap needed):

  1. Acquisition: A worker writes two small ticket objects (seq-0 and seq-1).
  2. Self-visibility gate (0.9.0): it polls the listing until its OWN ticket is visible - a listing that cannot show your own writes cannot be trusted to show competitors (raises after visibility_timeout, default 30s).
  3. Election: it lists all tickets and yields to older ones (seq-1 timestamp; lexicographic lock_id breaks ties). Shared tickets yield only to older exclusive tickets.
  4. Confirming re-list (0.9.0): winning requires a second clear listing taken settle_delay (default 1.0s) later - a violation now needs two independent stale listings.
  5. Own-ticket invariant (0.9.0): every decisive listing must still contain the worker's own ticket; if another client deleted it (e.g. break_other_locks), acquisition raises instead of "winning" without a ticket. Recovering a ticket via lock_id= restores the ticket only - acquire() re-runs the election.
  6. Holder re-verification (0.9.3): lock.verify() re-checks that a holder still holds the lock (a fresh listing must show both of its ticket objects) - call it at critical boundaries so a broken holder aborts instead of writing without mutual exclusion. break_other_locks() is age-gated by default (only tickets older than 2 hours are broken; the caller's own never are).
  7. Auto-Cleanup: weakref.finalize deletes ticket objects even if the process exits unexpectedly (best effort).

Guarantee and residual window: on storage with strongly consistent listings the election is safe. On eventually-consistent listings the hardening reduces the failure mode to two consecutive independently-stale listings (measured on B2: 80/80 listings were first-poll consistent - see benchmarks/results_visibility_lag.md). No provider we tested currently offers atomic conditional writes (benchmarks/conditional_write_probe.py is the qualification gate for adding a true CAS lock per provider; MEGA S4 accepts the headers but is not atomic under concurrency). Tune via session.lock(key, settle_delay=..., visibility_timeout=...).

Development

Setup environment

We use uv to manage the development environment and production build.

uv sync --all-extras --dev

Running Tests

uv run pytest

License

This project is licensed under the terms of the Apache Software License 2.0.

Download files

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

Source Distribution

s3func-0.9.4.tar.gz (49.1 kB view details)

Uploaded Source

Built Distribution

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

s3func-0.9.4-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

File details

Details for the file s3func-0.9.4.tar.gz.

File metadata

  • Download URL: s3func-0.9.4.tar.gz
  • Upload date:
  • Size: 49.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.7

File hashes

Hashes for s3func-0.9.4.tar.gz
Algorithm Hash digest
SHA256 5d4c8b2ec1e66e1e5b9fc33b6548842be22ec26d24cabf4b2f85eccd1cc6dba3
MD5 e39c9d242692635868b0fb510726f586
BLAKE2b-256 fba70d344767250efacd846ddeb474af85d9f82a492a7e9d8233060631592ff8

See more details on using hashes here.

File details

Details for the file s3func-0.9.4-py3-none-any.whl.

File metadata

  • Download URL: s3func-0.9.4-py3-none-any.whl
  • Upload date:
  • Size: 57.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.7

File hashes

Hashes for s3func-0.9.4-py3-none-any.whl
Algorithm Hash digest
SHA256 751c4ca07086fc044c735a387c25448ced44742a7c00b79cc74b56147f9c21d8
MD5 83ee452024e678692f672805e7f327f3
BLAKE2b-256 92a8eabb676656f1bc84861e34ab285a6b9c8743873c8357afeb4ff78bdc6578

See more details on using hashes here.

Release history Release notifications | RSS feed

0.9.6

2 files

0.9.5

2 files

This release

0.9.4 This release

2 files

0.9.3

2 files

0.9.2

2 files

0.9.1

2 files

0.9.0

2 files

0.8.5

2 files

0.8.4

2 files

0.8.3

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.3

2 files

0.7.2

2 files

0.7.1

2 files

0.7.0

2 files

0.6.0

2 files

0.5.11

2 files

0.5.10

2 files

0.5.9

2 files

0.5.8

2 files

0.5.7

2 files

0.5.6

2 files

0.5.5

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

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