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.

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. 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.3.tar.gz (44.5 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.3-py3-none-any.whl (51.9 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for s3func-0.9.3.tar.gz
Algorithm Hash digest
SHA256 98d7b2bc58ad4f2d6a4014827b1cc7f857735a245e17be69ebb8e538f0d59682
MD5 b0b2438ad1c7c0419494b04a131e1b4e
BLAKE2b-256 93ebf90d3039f090af4d15799ed8347ee88cd5b4255582cec90e28a5ee3ab3aa

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for s3func-0.9.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ad06de92fabc7adceacc30b46a2e3502da3d8cf4e905c0915ecd384b53d95bd8
MD5 c45c85d41ea5a1c383df95bcddebb1d4
BLAKE2b-256 328ae386d99002f2b8e112d041307065b80386a53168cdca03da1a8e2382faa1

See more details on using hashes here.

Release history Release notifications | RSS feed

0.9.6

2 files

0.9.5

2 files

0.9.4

2 files

This release

0.9.3 This release

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