Typed NATS client wrapper, subject builders, and JetStream KV bucket primitives for 3tears applications
Project description
3tears-nats
Typed NATS client wrapper, subject builders, and JetStream KV bucket primitives for 3tears applications.
What this package provides
NatsClient-- single canonical wrapper aroundnats-py. Handles connect (with bounded startup-timeout + bounded runtime reconnect ceiling), graceful shutdown/drain, typed publish, kw-only subscribe with optional Pydantic validation, request/reply withtimedeltatimeouts, and JetStream KV bucket access.Subject+Subjects-- opaque subject dataclass and factory of every canonical subject family used by 3tears applications. Replaces ad-hocf"{namespace}.tools.call"string-concatenation across the platform.NatsKvBucket-- operations against one JetStream KV bucket (get/put/delete/create/update/get_entry). Bucket name auto-prefixed by the connected client'snats_subject_namespace.nats_distributed_lock-- TTL-based distributed lock primitive built onNatsKvBucket.create(put-if-absent). Atomic acquisition + background heartbeat + automatic cleanup; on holder death the TTL expires the key.StreamTransport-- narrow Protocol used by streaming consumers; lets test fakes substitute for the live client.- Errors --
NatsClientError,SubscribeError,PublishError,RequestError,KvError.
Why a separate package
The wrapper is consumed by the platform services (broker, gateway, registry, channel adapters, agent SDK) and any 3tears-based application. Keeping it in 3tears-nats avoids forcing those apps to depend on a host application repo just for a NATS primitive.
Mistake-proofed API
- Subscribe is keyword-only after
self. A common production bug (nc.subscribe(subject, callback)silently treating the callback as a queue group innats-py2.10+) is impossible to reproduce against this wrapper. - Publish accepts
BaseModelinstances. Raw bytes go through the explicit escape hatchpublish_raw. - Subjects are typed
Subjectobjects, not strings. The factory owns subject formatting; callers cannot accidentally interpolate the wrong shape. - Default
deadletter_on_failure=True. Uncaught subscribe-callback exceptions auto-republish to{ns}.deadletter.{path}.
Usage
from datetime import timedelta
from threetears.nats import NatsClient, Subjects
nc = await NatsClient.connect(
nats_url="nats://localhost:4222",
nats_subject_namespace="myapp",
client_name="my-service",
)
# Typed publish
await nc.publish(
subject=Subjects.audit_event("workspace.doc_set"),
message=AuditEvent(...),
)
# Typed request/reply
response = await nc.request(
subject=Subjects.tools_call(),
message=ToolCallRequest(...),
response_type=ToolCallResponse,
timeout=timedelta(seconds=5),
)
# Subscribe with Pydantic validation
sub = await nc.subscribe_typed(
subject=Subjects.audit_wildcard(),
cb=on_audit_event,
message_type=AuditEvent,
queue="audit-consumer",
)
# JetStream KV
bucket = await nc.kv_bucket(name="agent_config", ttl=timedelta(hours=2))
await bucket.put(key="agent-1", value=b"config-payload")
await nc.shutdown()
Distributed locks
nats_distributed_lock is a TTL-backed lock for "only one pod should run this body" patterns (scheduled jobs, periodic ticks, exclusive resource access). It is layered on top of NatsKvBucket.create (atomic put-if-absent), with a background heartbeat that refreshes the entry while the body runs and a TTL that bounds the orphan-lock window after a pod death.
from datetime import timedelta
from threetears.nats import LockHeld, nats_distributed_lock
try:
async with nats_distributed_lock(
nc,
"backup-job",
bucket_name="scheduler-locks", # default; override per consumer
ttl=timedelta(seconds=60), # KV entry TTL
heartbeat=timedelta(seconds=20), # heartbeat MUST be < ttl
):
await run_backup()
except LockHeld:
# another pod owns the lock; skip this run cleanly
return
client=None is a graceful no-op that yields immediately. Single-pod dev environments without NATS work unchanged. LockHeld is distinct from KvError (transport / bucket failures); callers should treat the former as the expected "another pod is running" branch and surface the latter separately.
Enforcement
Direct from nats import / from nats.aio imports are flagged by the per-repo enforcement walker tests/enforcement/test_nats_wrapper_usage.py. Strict mode by default; exemptions require a # rationale: ... line.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file 3tears_nats-0.19.0.tar.gz.
File metadata
- Download URL: 3tears_nats-0.19.0.tar.gz
- Upload date:
- Size: 151.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02781ca946d42e01ec41e110eea8f941f341fc99b85ef8db1cdc4051b7ee6997
|
|
| MD5 |
0ace3c1c65728153c4b403bf29a9b6cc
|
|
| BLAKE2b-256 |
f71d61ad5d63b81afd44550c3817d57c479a334599c8b84c2eb23a3ca41e2cc4
|
Provenance
The following attestation bundles were made for 3tears_nats-0.19.0.tar.gz:
Publisher:
release.yml on pacepace/3tears
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
3tears_nats-0.19.0.tar.gz -
Subject digest:
02781ca946d42e01ec41e110eea8f941f341fc99b85ef8db1cdc4051b7ee6997 - Sigstore transparency entry: 2252476912
- Sigstore integration time:
-
Permalink:
pacepace/3tears@e04b8e99b585ebb10ce5de899a22d85e56280f02 -
Branch / Tag:
refs/tags/v0.19.0 - Owner: https://github.com/pacepace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e04b8e99b585ebb10ce5de899a22d85e56280f02 -
Trigger Event:
push
-
Statement type:
File details
Details for the file 3tears_nats-0.19.0-py3-none-any.whl.
File metadata
- Download URL: 3tears_nats-0.19.0-py3-none-any.whl
- Upload date:
- Size: 96.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3287ce7f53acbeffbdd414638ef8b0053761c8f9e87a085d86fb843cb86f838
|
|
| MD5 |
42d200b2fdc477c408912995cc0c512b
|
|
| BLAKE2b-256 |
efaf514acc81b4277d7a49d241d5e594b9e8fd606a566980317999b6738ee91a
|
Provenance
The following attestation bundles were made for 3tears_nats-0.19.0-py3-none-any.whl:
Publisher:
release.yml on pacepace/3tears
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
3tears_nats-0.19.0-py3-none-any.whl -
Subject digest:
b3287ce7f53acbeffbdd414638ef8b0053761c8f9e87a085d86fb843cb86f838 - Sigstore transparency entry: 2252483379
- Sigstore integration time:
-
Permalink:
pacepace/3tears@e04b8e99b585ebb10ce5de899a22d85e56280f02 -
Branch / Tag:
refs/tags/v0.19.0 - Owner: https://github.com/pacepace
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e04b8e99b585ebb10ce5de899a22d85e56280f02 -
Trigger Event:
push
-
Statement type: