Unified Backblaze B2 client. Combines uploads with retry logic, bucket-aware downloads, deletion, existence checks, and authorized-URL generation into a single B2Client class.
As a git dependency with uv:
uv add "b2lib @ git+https://github.com/0jc1/b2lib.git"Or in pyproject.toml:
dependencies = [
"b2lib @ git+https://github.com/0jc1/b2lib.git",
]Pin to a tag or commit for reproducible builds:
"b2lib @ git+https://github.com/0jc1/b2lib.git@v0.1.0"from b2lib import B2Client
client = B2Client(
key_id="...", # B2 application key ID
application_key="...", # B2 application key
bucket_name="autovod1", # default bucket for uploads
)
# Upload (retries with exponential backoff; multipart handled by the SDK)
result = client.upload_file("/tmp/video.mp4", "vods/1/video.mp4")
# {'file_name': ..., 'file_path': 'b2://example/vods/1/video.mp4',
# 'file_size': ..., 'file_id': ...}
# All read operations accept either a full b2://bucket/key URI or a
# relative key. URIs are routed to the bucket they name, so files keep
# working even if the configured default bucket changes.
client.file_exists("b2://example/vods/1/video.mp4")
client.download_to_path("b2://example/vods/1/video.mp4", "/tmp/out.mp4")
temp_path = client.download_to_temp("vods/1/video.mp4") # caller cleans up
data = client.download_bytes("vods/1/video.mp4")
# Download URLs
client.get_download_url("vods/1/video.mp4") # plain (public buckets)
client.get_scoped_download_url("vods/1/video.mp4", valid_duration_seconds=7200)
client.get_authorized_download_url("vods/1/video.mp4") # account-token variant
# Deletion
client.delete_by_path("b2://example/vods/1/video.mp4") # all versions
client.delete_file_version(file_id, file_name) # single version by IDPath helpers are also exported for code that manipulates b2:// URIs directly:
from b2lib import is_b2_path, build_b2_uri, extract_b2_bucket, strip_b2_prefix
extract_b2_bucket("b2://example/vods/1/video.mp4") # 'autovod1'
strip_b2_prefix("b2://example/vods/1/video.mp4", "autovod1") # 'vods/1/video.mp4'The library deliberately ships no singleton; each application should wrap B2Client in its own factory wired to its config:
from functools import lru_cache
from b2lib import B2Client
from app.config import Config
@lru_cache(maxsize=1)
def get_b2_client() -> B2Client:
return B2Client(
key_id=Config.B2_APPLICATION_KEY_ID,
application_key=Config.B2_APPLICATION_KEY,
bucket_name=Config.B2_BUCKET_NAME,
)Uploads internally use an isolated, short-lived B2Api instance with its own auth-token pool, so concurrent uploads from different threads don't trigger UploadTokenUsedConcurrently errors against the shared client. For fully independent connection state per task (e.g. RQ jobs), construct a fresh B2Client instead of sharing the singleton.
b2lib logs to the standard logging module under the b2lib logger name. Configure it like any other library logger:
import logging
logging.getLogger("b2lib").setLevel(logging.INFO)uv sync
uv run pytest