rust-py-monitor
High-performance Python monitoring library with a Rust core.
Collects CPU, memory, threads, and HTTP request metrics from Django and FastAPI applications with minimal overhead. Exports metrics to logs, JSON, and Prometheus.
Features
- Process snapshot — CPU %, RSS memory, virtual memory, thread count, PID
- FastAPI middleware — per-request latency, method, path, status code (errors recorded even when a handler raises)
- Django middleware — same, for WSGI and ASGI Django apps
- Aggregator — total requests, error count, error rate, avg/min/max/p50/p95/p99 latency
- Bounded store — request history is a capped ring buffer (default 10k) — constant memory under any traffic
- Multi-worker aggregation — opt-in shared store merges metrics across gunicorn/uvicorn workers (
RPY_MULTIPROC_DIR) - Prometheus exporter —
/metricsendpoint compatible with Prometheus scraper - Threshold alerts —
check_alerts(...)flags high CPU / memory against your limits - Rust core — collection and aggregation happen in Rust via PyO3; Python API stays simple
Requirements
- Python 3.10+
- No mandatory runtime dependencies
Optional, installed separately:
fastapi+starlette— forMonitorMiddlewareandmake_fastapi_router()django— forMonitorMiddlewareanddjango_metrics_view
Installation
pip install rust-py-monitor
With optional extras:
pip install "rust-py-monitor[fastapi]"
pip install "rust-py-monitor[django]"
pip install "rust-py-monitor[fastapi,django,prometheus]"
Quick Start
import rust_py_monitor
# Process snapshot
m = rust_py_monitor.snapshot()
print(m)
# Snapshot(pid=1234, cpu=0.3%, rss=45.2MB, virt=512.0MB, threads=4, ts=1718000000)
print(m.pid) # 1234
print(m.memory_rss_mb) # 45.2
print(m.to_dict()) # {"pid": 1234, "cpu_percent": 0.3, ...}
# Aggregated request metrics
stats = rust_py_monitor.aggregate()
print(stats.total_requests) # 0 (no middleware active yet)
print(stats.p95_latency_ms) # 0.0
FastAPI
Middleware
from fastapi import FastAPI
from rust_py_monitor.fastapi import MonitorMiddleware
app = FastAPI()
app.add_middleware(MonitorMiddleware)
@app.get("/")
async def root():
return {"status": "ok"}
Prometheus endpoint
from fastapi import FastAPI
from rust_py_monitor.fastapi import MonitorMiddleware
from rust_py_monitor.prometheus import make_fastapi_router
app = FastAPI()
app.add_middleware(MonitorMiddleware)
app.include_router(make_fastapi_router()) # GET /metrics
# app.include_router(make_fastapi_router("/prom")) # custom path
Inspect metrics programmatically
import rust_py_monitor
stats = rust_py_monitor.aggregate()
print(f"Requests: {stats.total_requests}")
print(f"Errors: {stats.total_errors} ({stats.error_rate:.1f}%)")
print(f"p95: {stats.p95_latency_ms:.1f}ms")
print(f"p99: {stats.p99_latency_ms:.1f}ms")
for req in rust_py_monitor.get_requests()[-5:]:
print(req)
# RequestMetric(GET /api/users 200 12.34ms)
Django
Middleware
# settings.py
MIDDLEWARE = [
"rust_py_monitor.django.MonitorMiddleware",
# ... other middlewares ...
]
Prometheus endpoint
# urls.py
from django.urls import path
from rust_py_monitor.prometheus import django_metrics_view
urlpatterns = [
path("metrics/", django_metrics_view),
# ...
]
The middleware supports both WSGI and ASGI Django applications automatically.
Prometheus Output
GET /metrics returns:
# HELP rpy_requests_total Total HTTP requests recorded
# TYPE rpy_requests_total counter
rpy_requests_total 1024
# HELP rpy_errors_total Total HTTP errors (status >= 400)
# TYPE rpy_errors_total counter
rpy_errors_total 12
# HELP rpy_error_rate_percent HTTP error rate as a percentage
# TYPE rpy_error_rate_percent gauge
rpy_error_rate_percent 1.171875
# HELP rpy_latency_p95_ms P95 request latency in milliseconds
# TYPE rpy_latency_p95_ms gauge
rpy_latency_p95_ms 47.3
# HELP rpy_process_memory_rss_bytes Process RSS memory in bytes
# TYPE rpy_process_memory_rss_bytes gauge
rpy_process_memory_rss_bytes 52428800
# ... (13 metrics total)
Content-Type: text/plain; version=0.0.4; charset=utf-8
API Reference
rust_py_monitor.snapshot() → Snapshot
Captures a point-in-time snapshot of the current process.
| Property | Type | Description |
|---|---|---|
pid |
int |
Process ID |
cpu_percent |
float |
CPU usage (0–100 × cores). First call may return 0.0. |
memory_rss |
int |
Resident Set Size in bytes |
memory_rss_mb |
float |
RSS in megabytes (convenience) |
memory_virtual |
int |
Virtual memory in bytes |
threads |
int |
Thread count (0 on macOS/Windows) |
timestamp |
int |
Unix timestamp in seconds |
to_dict() |
dict |
All fields as a plain dict |
rust_py_monitor.aggregate() → AggregatedMetrics
Computes statistics over all requests recorded since startup (or last clear_requests()).
| Property | Type | Description |
|---|---|---|
total_requests |
int |
Total request count |
total_errors |
int |
Requests with status ≥ 400 |
error_rate |
float |
total_errors / total_requests × 100 |
avg_latency_ms |
float |
Mean latency |
min_latency_ms |
float |
Minimum latency |
max_latency_ms |
float |
Maximum latency |
p50_latency_ms |
float |
Median latency |
p95_latency_ms |
float |
95th percentile latency |
p99_latency_ms |
float |
99th percentile latency |
to_dict() |
dict |
All fields as a plain dict |
rust_py_monitor.get_requests() → list[RequestMetric]
Returns all recorded requests. Each RequestMetric has:
| Property | Type |
|---|---|
method |
str |
path |
str |
status_code |
int |
duration_ms |
float |
timestamp |
int |
to_dict() |
dict |
rust_py_monitor.metrics_text() → str
Returns all metrics in Prometheus text exposition format (v0.0.4).
rust_py_monitor.check_alerts(cpu_percent=None, memory_rss_mb=None, memory_virtual_mb=None) → list[dict]
Simple, stateless threshold alerts over the current process snapshot. Pass the thresholds you want to watch; it returns the alerts that fired (a metric exceeds its threshold). Memory thresholds are in megabytes. Only the thresholds you provide are evaluated.
import rust_py_monitor
fired = rust_py_monitor.check_alerts(cpu_percent=80, memory_rss_mb=500)
# [{"metric": "memory_rss_mb", "value": 612.4, "threshold": 500, "severity": "warning"}]
for alert in fired:
print(f"[alert] {alert['metric']}={alert['value']} > {alert['threshold']}")
Each alert is a dict {"metric", "value", "threshold", "severity"}, where
metric is one of "cpu_percent", "memory_rss_mb", "memory_virtual_mb".
Being stateless, you decide when to call it (in a /health handler, a periodic
task, etc.) and what to do with the result.
rust_py_monitor.clear_requests()
Clears the request store. Useful for testing and periodic resets.
rust_py_monitor.set_max_requests(n) / get_max_requests() → int
The request store is a bounded ring buffer (default capacity 10 000). Once full, the oldest entries are evicted first, so memory never grows without bound. Use these to tune the retention window.
Multi-worker deployments (gunicorn / uvicorn)
By default each worker process keeps its own in-memory store. A Prometheus
scrape of /metrics reaches only one worker, so the numbers would reflect just
that worker's traffic.
Set the RPY_MULTIPROC_DIR environment variable to a writable directory to
enable shared aggregation. Each worker writes a small fixed-size shard file
(rpy-<pid>.shard); aggregate() and metrics_text() then merge all live
workers' shards at read time. Shards of dead workers are pruned automatically.
export RPY_MULTIPROC_DIR=/tmp/rpy-metrics
gunicorn -w 4 myapp:app
You can also configure it at runtime:
import rust_py_monitor
rust_py_monitor.set_multiproc_dir("/tmp/rpy-metrics")
rust_py_monitor.multiproc_enabled() # True
rust_py_monitor.get_multiproc_dir() # "/tmp/rpy-metrics"
Notes:
- Counters (
total_requests,total_errors) and latency histogram buckets are summed across workers. Latency percentiles (p50/p95/p99) are therefore approximated from the merged histogram rather than computed exactly. get_requests()always returns the local process's recent requests only.- Process metrics (CPU/memory/threads) reflect the worker that served the scrape.
Roadmap
rust-py-monitor is mature (v0.2.0): process snapshots, FastAPI/Django
middlewares, the latency aggregator, the bounded ring-buffer store, multi-worker
aggregation, and the Prometheus exporter are shipped. Directional ideas under
consideration (simple CPU/memory alerts, GC metrics, per-route labeled metrics,
more exporters/sinks, a Flask middleware) are tracked in
ROADMAP.md.
Building from Source
Requires Rust and maturin.
pip install maturin
git clone https://github.com/robertolima-dev/rust-py-monitor
cd rust-py-monitor
# Development build (installs into current Python environment)
maturin develop
# Release wheel
maturin build --release
Running tests
# Rust unit tests
cargo test
# Python integration tests
pip install pytest pytest-asyncio httpx fastapi django
pytest tests/
Architecture
Python API (rust_py_monitor)
├── snapshot() ──► src/snapshot.rs (sysinfo crate)
├── aggregate() ──► src/aggregator.rs (pure Rust math)
├── get_requests() ──► src/request_metrics.rs (static Mutex<VecDeque>, bounded)
├── metrics_text() ──► src/prometheus.rs (text formatter)
├── set_multiproc_dir() ──► src/multiproc.rs (mmap shard per worker)
│
├── fastapi.MonitorMiddleware ──► record_request() ──► Rust store
├── django.MonitorMiddleware ──► record_request() ──► Rust store
└── prometheus.make_fastapi_router() / django_metrics_view
The Rust core is compiled to a native .so / .pyd extension module by maturin and PyO3. The Python layer is thin — it just routes calls and provides framework-specific adapters.
License
MIT — see LICENSE.
Release files for rust-py-monitor 0.4.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rust_py_monitor-0.4.3.tar.gz | 60.9 kB | Details |
Built distributions (wheels)
Total release size: 4.4 MB
Release files / rust_py_monitor-0.4.3.tar.gz
| Download URL | rust_py_monitor-0.4.3.tar.gz |
|---|---|
| Size | 60.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7bb3a0f70e0820d8b4bb264c9f56c0e40fb53e4dbe7b1a229d49099bae2c6b54
|
|
BLAKE2b-256 checksum How to use checksums |
aac58e213e14b0629df10fcf3763865280c441588a437c3c74090382c845dd86
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rust_py_monitor-0.4.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 393.2 kB |
| Tags | Linux glibc 2.17+ x86-64 PyPy 3.11 PyPy 3.11 7.3 |
|
SHA-256 checksum How to use checksums |
e1dd20ff2824d6071d12000426db91317eab517c05e80b6cb64ace61c5c2db84
|
|
BLAKE2b-256 checksum How to use checksums |
3d8b769e4fc7caf5150d254cc975634fc44b8423b5060c49d9b02defbf0e4136
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | rust_py_monitor-0.4.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 384.1 kB |
| Tags | Linux glibc 2.17+ ARM64 PyPy 3.11 PyPy 3.11 7.3 |
|
SHA-256 checksum How to use checksums |
d556042951aa5702a14d03def0a3f995b8c4d1e4785a40a2c2b8114321464774
|
|
BLAKE2b-256 checksum How to use checksums |
ed99ce3918f6f93b7fe54132a0593bf002df48c680e0aafc5ff9278f6111d6dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rust_py_monitor-0.4.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 396.5 kB |
| Tags | Linux glibc 2.17+ x86-64 PyPy 3.10 PyPy 3.10 7.3 |
|
SHA-256 checksum How to use checksums |
8436205d5e8569183f3c85ba10e309dfb18d143167ac0fbb3a1dd0ba51a2da76
|
|
BLAKE2b-256 checksum How to use checksums |
6a685128925c66449be00ec42ce87bd9b8aaf39f50842580eb2a36650419ee3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | rust_py_monitor-0.4.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 386.6 kB |
| Tags | Linux glibc 2.17+ ARM64 PyPy 3.10 PyPy 3.10 7.3 |
|
SHA-256 checksum How to use checksums |
1abfbaa77fd8322583aa662f4187a9319e780842e1cd748087194e3971920b7d
|
|
BLAKE2b-256 checksum How to use checksums |
4050458d89e4202e720111a4f46ffa5cc98235551fd1fe28fa7db26fa044bce9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-win_amd64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 218.0 kB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
f6811b1f38e91a9e09837389efe722ee9d21bed5e6ca0d7e66d16c3c20bce85c
|
|
BLAKE2b-256 checksum How to use checksums |
bc067073ccb242ca054db801c0a5fe9d763c94ce161375ecca08e897f09e0be5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-musllinux_1_2_x86_64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 600.2 kB |
| Tags | CPython 3.10 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
1e86b0b0a0d85ed0954b6ee81e32141175b98d7585d8c0ecc2aba0f615286f08
|
|
BLAKE2b-256 checksum How to use checksums |
e3a9e946703b4f2e87e26a4946034efc52167de2e9d2d071248d4c77e9a3ae2e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-musllinux_1_2_aarch64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 564.0 kB |
| Tags | CPython 3.10 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
52b743c7f81a134dc6b94031ea51d0a55b385355e993846dc862a40ee2954892
|
|
BLAKE2b-256 checksum How to use checksums |
b17d766dbe961ebb85acd49e3832cf01ff0e66f577c98a5dd0b958887599f511
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 397.2 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
046485445348d69ae1a49467b8309ebccdb80d47be62aa4e0a5a5f99ff294078
|
|
BLAKE2b-256 checksum How to use checksums |
a5616be7321d1818da9c62905684f0b6587f0695b716f1cbd510bd447874662d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 387.3 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
1d080dc32ecc6eec1b5c4587ad7f87eca8537e5cc5d20b0d8b0a61a933cd3bf9
|
|
BLAKE2b-256 checksum How to use checksums |
7be15a4d7fc3cf73dc39375bc98cd75a61f60be49fbde55ecbe78c99dfe1d92b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 308.7 kB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
d1b54fae018ea5eafd289c0531bc4aab9760cddfecb8c4022d3ce9e32508b6b3
|
|
BLAKE2b-256 checksum How to use checksums |
269e2117b2a559b86492acb8bcc6d7c29fbc53cf4a8d49b5b884e7be17977d18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency logRelease files / rust_py_monitor-0.4.3-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | rust_py_monitor-0.4.3-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 311.6 kB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
e4e8081a65ac5514c87b6867e93dc83eb43566752a64d88c8990affce2120af8
|
|
BLAKE2b-256 checksum How to use checksums |
e446332b85ebf554c179cd15d0c65e1c5e12b5b123200daed7755f912b174e76
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.
Transparency log