Skip to content

Repository files navigation

bluetape-py

English | 한국어

bluetape-py hero

Python-native bluetape libraries for backend services, testing, logging, and operational helpers.

bluetape-py follows the same ecosystem discipline as bluetape4k, bluetape-go, and bluetape-rs, but it is not a mechanical port. The Python line starts from Python 3.13+, keeps the default install thin, and splits heavier capabilities into explicit PyPI distributions and extras.

Current Status

v0.1.0 has been released as the first Python-native foundation: v0.1.0. PyPI publication remains on hold until package ownership and trusted publishing are confirmed. The collections, codec, compression, cache, Redis provider, serde, ID, JWT, measure, money, testcontainers, and audit packages are available from the source workspace; registry install commands describe the intended post-publication shape only.

The current planning track is milestone 0.2.0. It tracks ecosystem issues #7 through #34, serialization follow-ups #45/#46, local cache #50, compressor contracts #59, Redis provider #54, and coordination #55. Detailed planning lives in WIP.md, and completed user-facing changes are tracked in CHANGELOG.md.

Track Scope
v0.1.0 Released foundation: workspace, core, logging, testing, docs, and release preflight.
0.2.0 Active ecosystem work, including value packages #13, serde #45/#46, cache/Redis #50/#54/#55, observability #24, and compressor contracts #59.
PyPI publish On hold until project ownership and trusted publishing are confirmed.

Workspace Shape

bluetape-py workspace overview

The repository is a single uv workspace with multiple focused distributions. The meta package remains intentionally small: pip install bluetape installs only bluetape-core by default, so the core-only default is Redis-free.

Distribution Import path Default install Status Purpose
bluetape none yes active Thin meta distribution that depends on bluetape-core.
bluetape-core bluetape.core yes active Stdlib-only validation and foundation helpers.
bluetape-audit bluetape.audit no active, source workspace Stdlib-only immutable audit values, explicit limits, and preservation helpers.
bluetape-id bluetape.id no active, source workspace Stdlib-only UUIDv4/v7 and random/monotonic ULID values.
bluetape-jwt bluetape.jwt no active, source workspace Strict JWS verification, key rotation, issuance profiles, and optional bounded verification cache.
bluetape-measure bluetape.measure no active, source workspace Immutable runtime dimension-checked linear measurements.
bluetape-money bluetape.money no active, source workspace Current ISO 4217 currencies and exact Decimal money with caller-owned FX rates.
bluetape-async bluetape.asyncio no active, source workspace Stdlib-only bounded structured-concurrency helpers.
bluetape-codec bluetape.codec no active, source workspace Strict URL-safe Base64 and hexadecimal helpers.
bluetape-collections bluetape.collections no active, source workspace Stdlib-only eager iterable/list/dict helpers.
bluetape-compression bluetape.compression no active, source workspace Structural compressor contracts for bounded gzip, zlib, raw-DEFLATE, LZ4, Snappy, and Zstandard payloads.
bluetape-logging bluetape.logging no active Stdlib logging, contextvars, and redaction helpers.
bluetape-observability bluetape.observability no active, source workspace API-only OpenTelemetry adapters for resilience and Redis observer events.
bluetape-resilience bluetape.resilience no active, source workspace Stdlib-only sync/async retry, circuit breaker, bulkhead, and cooperative async timeout policies.
bluetape-testing bluetape.testing no active, internal-first Pytest helpers used first by this workspace, with a small stable public subset.
bluetape-serde bluetape.serde no active, source workspace Strict JSON v1 plus an explicit CPython 3.13 Apache Fory extra.
bluetape-cache bluetape.cache no active, source workspace Stdlib-only bounded sync and async local TTL loading caches.
bluetape-cache-redis bluetape.cache.redis no active, source workspace Byte-only Redis providers, bounded result envelopes, and sync/async load coordinators.
bluetape-leader bluetape.leader no active, source workspace Stdlib-only backend-neutral leader and distributed-lock contracts.
bluetape-leader-redis bluetape.leader.redis no active, source workspace Bounded single-primary Redis locks and leader electors with fencing.
bluetape-testcontainers bluetape.testcontainers no active, source workspace Ecosystem-owned Redis 8, PostgreSQL 18, and selected-service LocalStack test-server lifecycles and connection details.
bluetape-benchmark bluetape.benchmark no private, source-only Built/tested benchmark report and comparison contracts; never published.
bluetape-fastapi bluetape.fastapi no planned FastAPI integration helpers after the core/logging/testing layer stabilizes.

Design Position

  • Python-native APIs come first; Kotlin, Go, and Rust siblings are references, not source languages to copy.
  • bluetape-core stays stdlib-only.
  • bluetape-logging stays stdlib-first and builds on logging plus contextvars.
  • bluetape-id, bluetape-measure, and bluetape-money stay independent, stdlib-only value distributions. Callers own stronger ID coordination, custom unit registries, historical currency policy, and FX providers.
  • bluetape-resilience stays stdlib-only, separates sync and async policy families, and provides no synchronous timeout or hidden workers.
  • bluetape-observability is installed directly, depends only on the OpenTelemetry API at runtime, and leaves SDK/exporter lifecycle to applications.
  • bluetape-testing may depend on pytest, but starts as an internal support module before promising a broad public API.
  • The root bluetape distribution exposes extras, but it does not create a root bluetape/__init__.py import surface.
  • The default meta install remains core-only. Cache, Redis, leader coordination, compression providers, resilience, observability, serde, and Testcontainers are opt-in; Apache Fory is trusted-internal only and available solely through the explicit fory extra.

Install

PyPI publication is still on hold. Until publishing is enabled, use the local workspace commands below instead of registry install commands.

The public install shape after the first PyPI release is:

pip install bluetape
pip install "bluetape[audit]"
pip install "bluetape[asyncio]"
pip install "bluetape[cache]"
pip install "bluetape[cache-redis]"
pip install "bluetape[leader]"
pip install "bluetape[leader-redis]"
pip install "bluetape[codec]"
pip install "bluetape[collections]"
pip install "bluetape[compression]"
pip install "bluetape[compression-lz4]"
pip install "bluetape[compression-snappy]"
pip install "bluetape[compression-zstd]"
pip install "bluetape[compression-native]"
pip install "bluetape[id]"
pip install "bluetape[jwt]"
pip install "bluetape[logging]"
pip install "bluetape[measure]"
pip install "bluetape[money]"
pip install "bluetape[values]"
pip install "bluetape[resilience]"
pip install "bluetape[serde]"
pip install "bluetape[fory]"  # CPython 3.13 only
pip install "bluetape[testing]"
pip install "bluetape[testcontainers]"
pip install "bluetape[dev]"
pip install "bluetape[all]"

Focused distributions can also be installed directly:

pip install bluetape-core
pip install bluetape-audit
pip install bluetape-id
pip install bluetape-jwt
pip install bluetape-measure
pip install bluetape-money
pip install bluetape-async
pip install bluetape-cache
pip install bluetape-cache-redis
pip install bluetape-leader
pip install bluetape-leader-redis
pip install bluetape-codec
pip install bluetape-collections
pip install bluetape-compression
pip install "bluetape-compression[lz4]"
pip install "bluetape-compression[snappy]"
pip install "bluetape-compression[zstd]"
pip install "bluetape-compression[native]"
pip install bluetape-logging
pip install bluetape-observability
pip install bluetape-resilience
pip install bluetape-serde
pip install "bluetape-serde[fory]"  # CPython 3.13 only
pip install bluetape-testing
pip install bluetape-testcontainers
pip install "bluetape-testcontainers[postgres]"
pip install "bluetape-testcontainers[aws]"
pip install "bluetape-testcontainers[all]"

For local development from this repository:

uv sync --all-packages --locked
uv sync --package bluetape-compression --extra native --locked
uv run --package bluetape-cache python -c "from bluetape.cache import AsyncTTLCache, TTLCache; assert TTLCache and AsyncTTLCache"
uv run --package bluetape-cache-redis python -c "from bluetape.cache.redis import BinaryEnvelopeFormat, SyncRedisProvider; assert BinaryEnvelopeFormat().format_id == 'binary-v1' and SyncRedisProvider"
uv run --package bluetape-observability python -c "from bluetape.observability.redis import OpenTelemetryRedisObserver; from bluetape.observability.resilience import OpenTelemetryPolicyObserver; assert OpenTelemetryPolicyObserver and OpenTelemetryRedisObserver"
uv run --package bluetape-resilience python -c "from bluetape.resilience import AsyncResiliencePipeline, ResiliencePipeline; assert ResiliencePipeline and AsyncResiliencePipeline"
uv run --package bluetape-serde python -c "import bluetape.serde"
uv sync --all-packages --extra fory --python 3.13.14 --locked
uv run --package bluetape-serde --extra fory --python 3.13.14 python -c "import bluetape.serde.fory"
uv run --package bluetape-testcontainers --extra all --group test --python 3.13.14 pytest \
  -m testcontainers packages/bluetape-testcontainers -q

The #54 Redis provider tests consume RedisServer from the ecosystem wrapper and verify Redis 8 commands, TTL, NX, Lua compare-and-delete, lifecycle, ACL, and redaction behavior. Values use explicit serialize -> compress -> store composition. Issue #55 provides bounded sync/async Redis lease coordination while preserving the opt-in package boundary.

Issue #15 adds caller-owned PostgresServer and LocalStackServer adapters using postgres:18-alpine and localstack/localstack:4.14.0. Their provider extras remain focused-package choices; the root bluetape[testcontainers] extra stays base-only.

Build the current focused wheel, install it into an isolated environment, and run a strict JSON roundtrip:

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
uv build --package bluetape-serde --out-dir "$tmp_dir/dist"
uv venv "$tmp_dir/venv"
uv pip install --python "$tmp_dir/venv/bin/python" "$tmp_dir"/dist/bluetape_serde-*.whl
"$tmp_dir/venv/bin/python" -c 'from bluetape.serde import PayloadMetadata, TrustProfile, json_deserialize, json_serialize; m = PayloadMetadata(format="json", version=1, content_type="application/json", trust_profile=TrustProfile.UNTRUSTED); p = json_serialize({"order_id": 42}, metadata=m); assert json_deserialize(p, expected_metadata=m) == {"order_id": 42}'

Only the local workspace and local-wheel paths are runnable today. The pip commands above remain unavailable from PyPI until publication is enabled. Fory is not pulled by the base, serde, dev, or all extras.

Usage

Core validation

from bluetape.core import require_not_blank

service_name = require_not_blank("orders", "service_name")

Logging context

import logging

from bluetape.logging import ContextLogFilter, log_context

logger = logging.getLogger("orders")
logger.addFilter(ContextLogFilter())

with log_context(trace_id="trace-123", tenant="blue"):
    logger.info("order accepted")

Testing waits

from bluetape.testing import eventually

eventually(lambda: cache.get("ready"), timeout=2.0)

Collections

from bluetape.collections import chunked, group_by

chunks = chunked(range(5), 2)
by_initial = group_by(["ant", "ape", "bee"], lambda value: value[0])

Codec and compression

from bluetape.codec import base64url_encode
from bluetape.compression import Compressor, GzipCompressor
from bluetape.compression.native import ZstdCompressor

token = base64url_encode(b"order:42")
compressor: Compressor = GzipCompressor()
assert compressor.decompress(compressor.compress(token.encode("ascii"))) == token.encode("ascii")
assert ZstdCompressor().algorithm == "zstd-frame"

bluetape.codec decodes only canonical URL-safe Base64 and strict hex text. bluetape.compression provides a structural Compressor Protocol and frozen implementations for gzip, zlib, raw DEFLATE, LZ4 frame, raw Snappy, and Zstandard frame. Decompression defaults to a 64 MiB logical returned-payload limit and rejects invalid, truncated, or trailing data. Native providers are available only through explicit extras. The feature and failure rules align with sibling bluetape libraries, but cross-language wire compatibility is not promised. Neither codec nor compression is part of the default install.

Bounded asyncio work

import asyncio

from bluetape.asyncio import map_bounded


async def fetch_order(order_id: int) -> str:
    await asyncio.sleep(0.01)
    return f"order-{order_id}"


async def main() -> None:
    print(await map_bounded([1, 2, 3], fetch_order, limit=2, timeout=1.0))


asyncio.run(main())

Use a synchronous loop for simple sequential work and asyncio.gather only when the coroutine set is already small and bounded. Use map_bounded when an input iterable can grow and the caller must set a cooperative concurrency cap.

Local TTL cache

from bluetape.cache import TTLCache

cache = TTLCache[str, object](default_ttl=30, max_size=100, max_inflight=8)
calls: list[str] = []


def load_order(key: str) -> object:
    calls.append(key)
    return {"key": key}


order = cache.get_or_load(
    "tenant-blue:42",
    load_order,
    ttl=5,
)
assert cache.get("tenant-blue:42") is order
assert cache.get_or_load("tenant-blue:42", load_order) is order
assert calls == ["tenant-blue:42"]
cache.set("none", None)
assert cache.get("none") is None
assert cache.invalidate("tenant-blue:42") is True
assert cache.invalidate("tenant-blue:42") is False
try:
    cache.get("tenant-blue:42")
except KeyError as error:
    assert error.args == ("tenant-blue:42",)
assert len(cache) == 1
import asyncio

from bluetape.cache import AsyncTTLCache


async def cache_example() -> None:
    cache = AsyncTTLCache[str, object](
        default_ttl=30,
        max_size=100,
        max_inflight=8,
    )

    calls: list[str] = []

    async def load_order(key: str) -> object:
        calls.append(key)
        return {"key": key}

    order = await cache.get_or_load("tenant-blue:42", load_order, ttl=5)
    assert await cache.get("tenant-blue:42") is order
    assert await cache.get_or_load("tenant-blue:42", load_order) is order
    assert calls == ["tenant-blue:42"]
    await cache.set("none", None)
    assert await cache.get("none") is None
    assert await cache.invalidate("tenant-blue:42") is True
    assert await cache.invalidate("tenant-blue:42") is False
    try:
        await cache.get("tenant-blue:42")
    except KeyError as error:
        assert error.args == ("tenant-blue:42",)
    assert await cache.size() == 1


asyncio.run(cache_example())

The sync and async caches share bounded LRU/TTL, cached-None, same-key loader coalescing, mutation supersession, and immutable statistics semantics. Include tenant and authorization context in keys, redact KeyError(key) and loader exceptions before logging, and give async callers deadlines because a cancellation-resistant loader keeps its max_inflight slot until terminal. Redis-backed storage and bounded cross-process load coordination are available through the opt-in provider below. RESP3 near-cache invalidation remains a separate upstream-blocked track in issue #56.

Redis byte provider

from bluetape.cache.redis import BinaryEnvelopeFormat, ResultEnvelopeCodec, SyncRedisProvider

# Supply an application-owned PayloadCodec.
codec = ResultEnvelopeCodec(
    payload_codec=payload_codec,
    envelope_format=BinaryEnvelopeFormat(),
)

with SyncRedisProvider.from_url("redis://localhost:6379/0") as provider:
    provider.set("orders:result:42", codec.encode("owner-42", result), ttl=30.0)
    stored = provider.get("orders:result:42")
    decoded = None if stored is None else codec.decode(
        stored, expected_owner_token="owner-42"
    )

The focused package also provides AsyncRedisProvider, JsonEnvelopeFormat, optional explicit ZstdCompressor, stable redacted errors, and borrowed or factory-owned client lifecycles. See its package README for the complete contract and rollout rules.

Strict JSON serde

from bluetape.serde import (
    PayloadMetadata,
    TrustProfile,
    json_deserialize,
    json_serialize,
)

producer_metadata = PayloadMetadata(
    format="json",
    version=1,
    content_type="application/json",
    trust_profile=TrustProfile.UNTRUSTED,
)
payload = json_serialize({"order_id": 42}, metadata=producer_metadata)

# Construct consumer policy independently from authenticated configuration;
# never copy expected policy from payload.metadata.
consumer_policy = PayloadMetadata(
    format="json",
    version=1,
    content_type="application/json",
    trust_profile=TrustProfile.UNTRUSTED,
)
assert json_deserialize(payload, expected_metadata=consumer_policy) == {"order_id": 42}

UNTRUSTED is the recommended default. TRUSTED_INTERNAL is only for a closed boundary with an authenticated and authorized producer; network location and payload claims are insufficient. Both profiles enforce identical strict UTF-8 JSON, exact metadata, duplicate-key/non-finite-number rejection, 16 MiB default input/output limits, depth 100 by default, a hard depth ceiling of 256, and a fixed 640-decimal-digit integer limit on both encode and decode. Strings must contain Unicode scalar values; decode normalizes valid escaped surrogate pairs to their non-BMP scalar and rejects unpaired surrogates. These byte limits are not a fixed process-memory guarantee. See the package README for error handling and versioned rollout/rollback guidance. SerdeError covers stable serde domain failures; caller type and configuration mistakes remain native TypeError or ValueError.

Trusted-internal Apache Fory

Install the explicit fory extra only on CPython 3.13. Each application route owns a fixed (schema_id, schema_version, type_id) tuple and one exact root type. Consumers construct expected metadata and registration independently; payloads never select an adapter, class, schema, or fallback. Deploy readers before writers, move schema changes to a new versioned route, and stop Fory writes when predeclared canary error or latency thresholds are breached. Keep the old codec on a separate route until drain evidence is complete.

Fory is limited to authenticated and authorized internal producers. Its byte, depth, schema, and concurrency limits are acceptance bounds, not hard CPU/RSS ceilings; use a separately constrained process for hard containment. Telemetry may include only operation, stable error code, envelope size, success/failure, latency, and a fixed route ID. Do not record payloads, decoded values, provider exception text, tracebacks, or caller-controlled high-cardinality names.

ID, measure, and money values

value package boundaries

Open the SVG source.

The three value packages are independent opt-ins. bluetape-id provides canonical UUID/ULID values without distributed-order or secrecy claims; bluetape-measure provides linear runtime dimensions with caller-owned custom units; bluetape-money provides exact Decimal arithmetic over the committed current ISO snapshot while callers own rates and historical policy.

from decimal import ROUND_HALF_UP

from bluetape.id import uuid7
from bluetape.measure import KILOMETER, METER, Measure
from bluetape.money import USD, Money

identifier = uuid7()
distance = Measure(1.25, KILOMETER).to(METER)
price = Money.of("12.345", USD).quantize(rounding=ROUND_HALF_UP)

assert identifier.version == 7
assert distance == Measure(1250, METER)
assert price == Money.of("12.35", USD)

Storage-neutral audit contracts

audit contract boundary

Open the audit boundary SVG source.

bluetape-audit is an independent stdlib-only opt-in for immutable audit event values, explicit limits, safe failures, and adapter preservation tests. Applications serialize payload bytes; caller-owned adapters invoke validate_audit_event immediately before their first side effect; caller-owned storage, outboxes, relays, and transports establish durability and delivery. The default bluetape install remains core-only. See the English package guide or Korean package guide.

Package Documentation

Package Documentation
bluetape packages/bluetape/README.md / 한국어
bluetape-async packages/bluetape-async/README.md
bluetape-audit packages/bluetape-audit/README.md / 한국어
bluetape-cache packages/bluetape-cache/README.md
bluetape-cache-redis packages/bluetape-cache-redis/README.md / 한국어
bluetape-leader packages/bluetape-leader/README.md / 한국어
bluetape-leader-redis packages/bluetape-leader-redis/README.md / 한국어
bluetape-jwt packages/bluetape-jwt/README.md / 한국어
bluetape-benchmark packages/bluetape-benchmark/README.md / 한국어
bluetape-codec packages/bluetape-codec/README.md
bluetape-collections packages/bluetape-collections/README.md
bluetape-compression packages/bluetape-compression/README.md / 한국어
bluetape-core packages/bluetape-core/README.md
bluetape-id packages/bluetape-id/README.md / 한국어
bluetape-measure packages/bluetape-measure/README.md / 한국어
bluetape-money packages/bluetape-money/README.md / 한국어
bluetape-logging packages/bluetape-logging/README.md
bluetape-observability packages/bluetape-observability/README.md / 한국어
bluetape-resilience packages/bluetape-resilience/README.md / 한국어
bluetape-serde packages/bluetape-serde/README.md
bluetape-testcontainers packages/bluetape-testcontainers/README.md
bluetape-testing packages/bluetape-testing/README.md

Roadmap

Track Plan
v0.1.0 Released the initial core, logging, testing, documentation, and release preflight foundation.
0.2.0 Track ecosystem issues #7-#34, including value packages #13 and observability #24, plus cache/Redis #50/#54/#55, compressor contracts #59, and serialization #45/#46.
Later Add FastAPI helpers and workshop examples only after the base packages are stable.

Project planning and release policy:

Ecosystem Backlog

The 0.2.0 milestone tracks Python-native equivalents for proven bluetape-go and bluetape4k ecosystem capabilities. Research issues intentionally come before implementation where the Python package boundary or dependency choice is not obvious. See WIP.md for the issue-by-issue task queue and docs/research/README.md for research gates.

Development

uv sync --all-packages
uv build --all-packages
uv run pytest
uv run ruff check .
uv run ruff format --check .

About

Python-native bluetape libraries for backend services, testing, logging, and operational helpers

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages