cachekit 0.20.0__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. LICENSE +21 -0
  2. README.md +539 -0
  3. cachekit/__init__.py +136 -0
  4. cachekit/_rust_serializer.cp314-win_amd64.pyd +0 -0
  5. cachekit/backends/__init__.py +114 -0
  6. cachekit/backends/base.py +450 -0
  7. cachekit/backends/base_config.py +104 -0
  8. cachekit/backends/cachekitio/__init__.py +12 -0
  9. cachekit/backends/cachekitio/backend.py +911 -0
  10. cachekit/backends/cachekitio/client.py +187 -0
  11. cachekit/backends/cachekitio/config.py +225 -0
  12. cachekit/backends/cachekitio/error_handler.py +134 -0
  13. cachekit/backends/cachekitio/session.py +59 -0
  14. cachekit/backends/errors.py +198 -0
  15. cachekit/backends/file/__init__.py +30 -0
  16. cachekit/backends/file/backend.py +1159 -0
  17. cachekit/backends/file/config.py +191 -0
  18. cachekit/backends/memcached/__init__.py +25 -0
  19. cachekit/backends/memcached/backend.py +255 -0
  20. cachekit/backends/memcached/config.py +137 -0
  21. cachekit/backends/memcached/error_handler.py +98 -0
  22. cachekit/backends/provider.py +255 -0
  23. cachekit/backends/redis/__init__.py +12 -0
  24. cachekit/backends/redis/backend.py +285 -0
  25. cachekit/backends/redis/client.py +263 -0
  26. cachekit/backends/redis/config.py +117 -0
  27. cachekit/backends/redis/error_handler.py +185 -0
  28. cachekit/backends/redis/provider.py +804 -0
  29. cachekit/cache_handler.py +2301 -0
  30. cachekit/config/__init__.py +48 -0
  31. cachekit/config/decorator.py +603 -0
  32. cachekit/config/nested.py +393 -0
  33. cachekit/config/settings.py +382 -0
  34. cachekit/config/singleton.py +100 -0
  35. cachekit/config/validation.py +243 -0
  36. cachekit/decorators/__init__.py +15 -0
  37. cachekit/decorators/intent.py +283 -0
  38. cachekit/decorators/local_wrapper.py +145 -0
  39. cachekit/decorators/main.py +7 -0
  40. cachekit/decorators/orchestrator.py +419 -0
  41. cachekit/decorators/session.py +141 -0
  42. cachekit/decorators/stats_context.py +64 -0
  43. cachekit/decorators/tenant_context.py +256 -0
  44. cachekit/decorators/utils/__init__.py +3 -0
  45. cachekit/decorators/wrapper.py +2490 -0
  46. cachekit/di.py +55 -0
  47. cachekit/hash_utils.py +145 -0
  48. cachekit/health.py +647 -0
  49. cachekit/hiredis_compat.py +98 -0
  50. cachekit/imports.py +48 -0
  51. cachekit/interop.py +518 -0
  52. cachekit/key_generator.py +450 -0
  53. cachekit/l1_cache.py +642 -0
  54. cachekit/logging.py +386 -0
  55. cachekit/monitoring/__init__.py +8 -0
  56. cachekit/monitoring/pool_monitor.py +346 -0
  57. cachekit/monitoring/protocols.py +50 -0
  58. cachekit/object_cache.py +471 -0
  59. cachekit/py.typed +0 -0
  60. cachekit/reliability/__init__.py +36 -0
  61. cachekit/reliability/async_metrics.py +804 -0
  62. cachekit/reliability/circuit_breaker.py +459 -0
  63. cachekit/reliability/error_classification.py +91 -0
  64. cachekit/reliability/load_control.py +214 -0
  65. cachekit/reliability/metrics_collection.py +407 -0
  66. cachekit/reliability/profiles.py +236 -0
  67. cachekit/serializers/__init__.py +260 -0
  68. cachekit/serializers/arrow_serializer.py +450 -0
  69. cachekit/serializers/auto_serializer.py +1262 -0
  70. cachekit/serializers/base.py +480 -0
  71. cachekit/serializers/encryption_wrapper.py +885 -0
  72. cachekit/serializers/interop_serializer.py +64 -0
  73. cachekit/serializers/orjson_serializer.py +212 -0
  74. cachekit/serializers/standard_serializer.py +411 -0
  75. cachekit/serializers/wrapper.py +169 -0
  76. cachekit-0.20.0.dist-info/METADATA +596 -0
  77. cachekit-0.20.0.dist-info/RECORD +80 -0
  78. cachekit-0.20.0.dist-info/WHEEL +4 -0
  79. cachekit-0.20.0.dist-info/licenses/LICENSE +21 -0
  80. cachekit-0.20.0.dist-info/sboms/cachekit-rs.cyclonedx.json +3546 -0
LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 cachekit Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,539 @@
1
+ <div align="center">
2
+
3
+ # cachekit
4
+
5
+ > **Python caching, batteries included**
6
+
7
+ Backend-agnostic caching with intent-based decorators — circuit breaker, distributed locking, Prometheus metrics, and optional zero-knowledge encryption on a Rust-powered core. Zero config to start, any backend when you scale.
8
+
9
+ [![PyPI Version][pypi-badge]][pypi-url]
10
+ [![Python Versions][python-badge]][pypi-url]
11
+ [![codecov][codecov-badge]][codecov-url]
12
+ [![License: MIT][license-badge]][license-url]
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ > [!NOTE]
19
+ > **Status: beta** — CacheKit is in closed beta ahead of 1.0. APIs are stabilising; minor breaking changes may still occur between 0.x releases.
20
+ >
21
+ > 🐛 **Found a bug or a rough edge?** Please [open an issue][issues-url] — your feedback directly shapes the path to 1.0.
22
+
23
+ ---
24
+
25
+ ## Why cachekit?
26
+
27
+ **Simple to use, works out of the box.**
28
+
29
+ ```python
30
+ from cachekit import cache
31
+
32
+ @cache
33
+ def expensive_function():
34
+ return fetch_data()
35
+ ```
36
+
37
+ That's it. You get:
38
+
39
+ | Feature | Description |
40
+ |:--------|:------------|
41
+ | **Circuit breaker** | Prevents cascading failures |
42
+ | **Distributed locking** | Multi-pod safety |
43
+ | **Prometheus metrics** | Built-in observability |
44
+ | **MessagePack serialization** | Efficient with optional compression |
45
+ | **Zero-knowledge encryption** | Client-side AES-256-GCM |
46
+
47
+ ---
48
+
49
+ ## Quick Start
50
+
51
+ ### Installation
52
+
53
+ ```bash
54
+ pip install cachekit
55
+ ```
56
+
57
+ Or with [uv][uv-url] (recommended):
58
+
59
+ ```bash
60
+ uv add cachekit
61
+ ```
62
+
63
+ ### Setup (choose a backend)
64
+
65
+ cachekit exposes one decorator API over a pluggable backend abstraction. Pick the
66
+ backend that fits your infrastructure — they're peers behind the same `@cache` API:
67
+
68
+ | Backend | Best for | Select with |
69
+ |---------|----------|-------------|
70
+ | Redis | Self-hosted, full control | `REDIS_URL` / `CACHEKIT_REDIS_URL` |
71
+ | CachekitIO | Managed, zero-ops (beta) | `CACHEKIT_API_KEY` |
72
+ | Memcached | High-throughput, existing infra | `CACHEKIT_MEMCACHED_SERVERS` |
73
+ | File / L1-only | Local dev, tests, no external deps | `CACHEKIT_FILE_CACHE_DIR` / `backend=None` |
74
+
75
+ ```bash
76
+ # Run Redis locally or use your existing infrastructure
77
+ export REDIS_URL="redis://localhost:6379"
78
+ ```
79
+
80
+ ```python
81
+ from cachekit import cache
82
+
83
+ @cache # Auto-detects backend (defaults to Redis at localhost)
84
+ def expensive_api_call(user_id: int):
85
+ return fetch_user_data(user_id)
86
+ ```
87
+
88
+ > [!TIP]
89
+ > No Redis? No worries! Use `@cache(backend=None)` for L1-only in-memory caching, like `lru_cache`, but with all the bells and whistles.
90
+
91
+ ### More Backends
92
+
93
+ <details>
94
+ <summary><strong>CachekitIO — Managed SaaS (Beta)</strong></summary>
95
+
96
+ ```python notest
97
+ import os
98
+ from cachekit import cache
99
+
100
+ # Set your CachekitIO API key
101
+ # export CACHEKIT_API_KEY="your-api-key" # pragma: allowlist secret
102
+
103
+ @cache.io() # Uses CachekitIO SaaS backend — no Redis to manage
104
+ def expensive_api_call(user_id: int):
105
+ return fetch_user_data(user_id)
106
+ ```
107
+
108
+ *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
109
+
110
+ </details>
111
+
112
+ <details>
113
+ <summary><strong>Memcached — Optional</strong></summary>
114
+
115
+ ```python notest
116
+ from cachekit import cache
117
+ from cachekit.backends.memcached import MemcachedBackend
118
+
119
+ # pip install cachekit[memcached]
120
+
121
+ backend = MemcachedBackend() # Defaults to 127.0.0.1:11211
122
+
123
+ @cache(backend=backend)
124
+ def expensive_api_call(user_id: int):
125
+ return fetch_user_data(user_id)
126
+ ```
127
+
128
+ *Requires: `pip install cachekit[memcached]` or `uv add cachekit[memcached]`*
129
+
130
+ </details>
131
+
132
+ ---
133
+
134
+ > **CachekitIO Cloud (Beta)**
135
+ > Managed caching with zero infrastructure. L1+L2 caching, circuit breaker, and automatic failover — no Redis to manage.
136
+ > *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
137
+
138
+ ---
139
+
140
+ ## Intent-Based Optimization
141
+
142
+ cachekit provides **preset configurations** for different use cases:
143
+
144
+ ```python
145
+ # Speed-critical: trading, gaming, real-time
146
+ @cache.minimal
147
+ def get_price(symbol: str):
148
+ return fetch_price(symbol)
149
+
150
+ # Reliability-critical: payments, APIs
151
+ @cache.production
152
+ def process_payment(amount):
153
+ return payment_gateway.charge(amount)
154
+
155
+ # Security-critical: PII, medical, financial (needs a key: master_key= or CACHEKIT_MASTER_KEY)
156
+ @cache.secure(master_key=secret_key)
157
+ def get_user_profile(user_id: int):
158
+ return db.fetch_user(user_id)
159
+ ```
160
+
161
+ | Feature | `@cache.minimal` | `@cache.dev` | `@cache.test` | `@cache.production` | `@cache.secure` |
162
+ |:--------|:----------------:|:------------:|:-------------:|:-------------------:|:---------------:|
163
+ | Default TTL | 300 s | 300 s | 300 s | 600 s | 600 s |
164
+ | Circuit Breaker | - | ✅ | - | ✅ | ✅ |
165
+ | Backpressure | ✅ | ✅ | - | ✅ | ✅ |
166
+ | Integrity Checking | - | ✅ | - | ✅ | ✅ 🔒 |
167
+ | Encryption | - | - | - | - | ✅ Required |
168
+ | L1 SWR (L1-only mode) | - | ✅ | - | ✅ | - |
169
+ | L1 Invalidation | - | - | - | ✅ | ✅ |
170
+ | Prometheus Metrics | - | - | - | ✅ | ✅ |
171
+ | Tracing | - | ✅ | - | ✅ | ✅ |
172
+ | Structured Logging | - | ✅ | - | ✅ | ✅ |
173
+ | **Use Case** | High throughput | Local debugging | Deterministic tests | Production reliability | Compliance/security |
174
+
175
+ > 🔒 `@cache.secure` forces `integrity_checking=True` — passing `integrity_checking=False` raises `ConfigurationError` at decoration, including as an override next to `config=DecoratorConfig.secure(...)`. `@cache.secure` also rejects `config=`; the RORO form is `@cache(config=DecoratorConfig.secure(...))`.
176
+ >
177
+ > **Default TTL** follows the cross-SDK [intent-preset spec](https://github.com/cachekit-io/protocol/blob/main/spec/intent-presets.md#default-ttl) (`@cache.io` 3600 s) — the same numbers as cachekit-rs and cachekit-ts. `ttl=` overrides it; `ttl=None` is the explicit never-expire opt-in ([details](docs/configuration.md#intent-presets)).
178
+ >
179
+ > **L1 SWR** (within-TTL background refresh) runs only in L1-only mode (`backend=None`) — with a backend configured it has no effect. `@cache.io` additionally ships past-TTL SWR via `stale_ttl` ([docs](docs/configuration.md#stale-while-revalidate-stale_ttl)).
180
+ >
181
+ > **`@cache.io()`** mirrors `@cache.production` (full reliability + observability) but routes to the managed CachekitIO SaaS backend instead of Redis. **`@cache.local()`** is a separate in-process path backed by `ObjectCache` (raw object references, entry-count LRU, no serialization) — the reliability and encryption features listed above do not apply to it.
182
+
183
+ <details>
184
+ <summary><strong>Additional Presets: <code>@cache.dev</code> and <code>@cache.test</code></strong></summary>
185
+
186
+ See the comparison table above for the exact feature set of each preset.
187
+
188
+ ```python
189
+ # Development: verbose logging, integrity checks on, Prometheus off
190
+ @cache.dev
191
+ def debug_expensive_call():
192
+ return complex_computation()
193
+
194
+ # Testing: deterministic, all protections off (no circuit breaker, no backpressure)
195
+ @cache.test
196
+ def test_cached_function():
197
+ return fixed_test_value()
198
+ ```
199
+
200
+ </details>
201
+
202
+ ---
203
+
204
+ ## Architecture
205
+
206
+ ```
207
+ ┌─────────────────────────────────────────────────────────────┐
208
+ │ Application │
209
+ ├─────────────────────────────────────────────────────────────┤
210
+ │ @cache Decorator │
211
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
212
+ │ │ Circuit │ │ Adaptive │ │ Distributed │ │
213
+ │ │ Breaker │ │ Timeouts │ │ Locking │ │
214
+ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │
215
+ ├─────────────────────────────────────────────────────────────┤
216
+ │ L1 Cache (In-Memory) │ L2 Cache (Pluggable Backend) │
217
+ │ ~50ns │ Redis / CachekitIO / File / │
218
+ │ │ Memcached ~2-50ms │
219
+ ├─────────────────────────────────────────────────────────────┤
220
+ │ Rust Core (PyO3) │
221
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
222
+ │ │ LZ4 │ │ xxHash3 │ │ AES-256-GCM │ │
223
+ │ │ Compression │ │ Checksums │ │ Encryption │ │
224
+ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │
225
+ └─────────────────────────────────────────────────────────────┘
226
+ ```
227
+
228
+ > [!TIP]
229
+ > **Building in Rust?** The core compression, checksums, and encryption are available as a standalone crate: [`cachekit-core`](https://crates.io/crates/cachekit-core) [![Crates.io](https://img.shields.io/crates/v/cachekit-core.svg)](https://crates.io/crates/cachekit-core)
230
+
231
+ ---
232
+
233
+ ## Features
234
+
235
+ ### Production Hardened
236
+
237
+ - Circuit breaker with graceful degradation
238
+ - Connection pooling with thread affinity (+28% throughput)
239
+ - Distributed locking prevents cache stampedes
240
+ - Pluggable backend abstraction (Redis, CachekitIO, File, Memcached, custom)
241
+ - Untrusted-decode bounds: nesting depth and header-declared allocation are capped on every cache read (a forged entry is a bounded cache miss), verified against the protocol's shared [`decode-bounds.json`](https://github.com/cachekit-io/protocol/blob/2736a81f2f853cf08c5563c0fe7c8361331fa3ad/test-vectors/decode-bounds.json) vectors
242
+
243
+ > [!NOTE]
244
+ > All reliability features are **enabled by default** with `@cache.production`. Use `@cache.minimal` to disable them for maximum throughput.
245
+
246
+ ### Smart Serialization
247
+
248
+ | Serializer | Speed | Use Case |
249
+ |:-----------|:-----:|:---------|
250
+ | **StandardSerializer** | ★★★★☆ | General Python types, NumPy, Pandas |
251
+ | **OrjsonSerializer** | ★★★★★ | JSON APIs (2-5x faster than stdlib) — requires `cachekit[json]` |
252
+ | **ArrowSerializer** | ★★★★★ | Large DataFrames (6-23x faster for 10K+ rows) |
253
+ | **EncryptionWrapper** | ★★★★☆ | Wraps any serializer with AES-256-GCM |
254
+
255
+ <details>
256
+ <summary><strong>Serializer Examples</strong></summary>
257
+
258
+ ```python
259
+ from cachekit.serializers import OrjsonSerializer, ArrowSerializer
260
+
261
+ # Fast JSON for API responses
262
+ @cache.production(serializer=OrjsonSerializer())
263
+ def get_api_response(endpoint: str):
264
+ return {"status": "success", "data": fetch_api(endpoint)}
265
+
266
+ # Zero-copy DataFrames for large datasets
267
+ @cache(serializer=ArrowSerializer())
268
+ def get_large_dataset(date: str):
269
+ return pd.read_csv(f"data/{date}.csv")
270
+ ```
271
+
272
+ Encrypted DataFrames go through `@cache.secure`, which takes any serializer. A file backend keeps this example self-contained; production uses Redis or cachekit.io:
273
+
274
+ ```python
275
+ import tempfile
276
+ from cachekit.backends.file import FileBackend, FileBackendConfig
277
+ from cachekit.serializers import ArrowSerializer
278
+
279
+ calls = 0
280
+
281
+ @cache.secure(
282
+ master_key=secret_key,
283
+ serializer=ArrowSerializer(),
284
+ backend=FileBackend(FileBackendConfig(cache_dir=tempfile.mkdtemp())),
285
+ )
286
+ def get_patient_data(hospital_id: int):
287
+ global calls
288
+ calls += 1
289
+ return pd.DataFrame({"hospital_id": [hospital_id], "patients": [42]})
290
+
291
+ get_patient_data(7)
292
+ get_patient_data(7) # second call is served from the encrypted cache
293
+ assert calls == 1
294
+ ```
295
+
296
+ </details>
297
+
298
+ ### Integrity Checking
299
+
300
+ > [!IMPORTANT]
301
+ > All serializers support **configurable checksums** for corruption detection using xxHash3-64 (8 bytes). Enabled by default in `@cache.production` and `@cache.secure`.
302
+
303
+ **Performance Impact** (benchmark-proven):
304
+
305
+ | Data Type | Latency Reduction (disabled) | Size Overhead |
306
+ |:----------|:----------------------------:|:-------------:|
307
+ | MessagePack (default) | 60-90% | 8 bytes |
308
+ | Arrow DataFrames | 35-49% | 8 bytes |
309
+ | JSON (orjson) | 37-68% | 8 bytes |
310
+
311
+ ### Security
312
+
313
+ > [!CAUTION]
314
+ > When handling PII, medical, or financial data, always use `@cache.secure` to enforce encryption.
315
+
316
+ **Key rotation**: keep a retiring key readable with
317
+ `CACHEKIT_PREVIOUS_MASTER_KEYS` (comma-separated hex, max 3 decrypt-only keys)
318
+ while new writes use `CACHEKIT_MASTER_KEY`. A one-deploy key swap is not
319
+ zero-miss; follow the [key rotation
320
+ runbook](https://docs.cachekit.io/concepts/key-rotation/), including its Before
321
+ You Rotate checks. CK-framed entries are selected by exact key fingerprint —
322
+ never trial decryption; Interop-mode entries carry no CK frame and attempt
323
+ keyring keys sequentially instead.
324
+
325
+ cachekit employs comprehensive security tooling:
326
+
327
+ - **Dependency Security**: cargo-deny for license compliance + cargo-audit for RustSec scanning
328
+ - **Formal Verification**: Kani proves correctness of compression, checksums, encryption
329
+ - **Runtime Analysis**: Miri + sanitizers for memory safety
330
+ - **Fuzzing**: Coverage-guided testing with >80% code coverage
331
+ - **Zero CVEs**: Continuous vulnerability scanning
332
+
333
+ <details>
334
+ <summary><strong>Security Commands</strong></summary>
335
+
336
+ ```bash
337
+ make security-install # Install security tools (one-time)
338
+ make security-fast # Run fast checks (< 3 min)
339
+ ```
340
+
341
+ **Security Tiers:**
342
+
343
+ | Tier | Time | Coverage |
344
+ |:-----|:----:|:---------|
345
+ | Fast | < 3 min | Vulnerability scanning, license checks, linting |
346
+ | Medium | < 15 min | Unsafe code analysis, API stability, Miri subset |
347
+ | Deep | < 2 hours | Formal verification, extended fuzzing, full sanitizers |
348
+
349
+ See [SECURITY.md][security-url] for vulnerability reporting and detailed documentation.
350
+
351
+ </details>
352
+
353
+ ### Monitoring & Observability
354
+
355
+ - **Per-function statistics** - `cache_info()` on every decorated function, modelled on `functools.lru_cache`
356
+ - **Prometheus metrics** - Recorded by default (your app owns exposition)
357
+ - **Structured logging** - Context-aware, per-operation fields
358
+ - **Health checks** - Comprehensive status endpoints
359
+
360
+ Every decorated function exposes `cache_info()`, returning a `CacheInfo` named tuple with
361
+ hit/miss counts, the L1/L2 split, and average backend latency:
362
+
363
+ ```python
364
+ @cache()
365
+ def get_score(x):
366
+ return x ** 2
367
+
368
+ get_score(2)
369
+ get_score(2) # served from cache
370
+
371
+ info = get_score.cache_info()
372
+ # CacheInfo has 9 fields: hits, misses, l1_hits, l2_hits, maxsize,
373
+ # currsize, l2_avg_latency_ms, last_operation_at, session_id
374
+ assert info.l1_hits + info.l2_hits == info.hits # every hit is L1 or L2
375
+ ```
376
+
377
+ `maxsize` and `currsize` are always `None` (the cache lives in an external store, not a
378
+ bounded in-process dict); they exist only for `lru_cache` API parity. See the
379
+ [API Reference](docs/api-reference.md#per-function-statistics-via-cache_info) for the full
380
+ field reference and a sample stats endpoint, and the
381
+ [Prometheus Metrics guide](docs/features/prometheus-metrics.md) for metric names and
382
+ exposition setup.
383
+
384
+ <details>
385
+ <summary><strong>Thread Safety Details</strong></summary>
386
+
387
+ **Free-threaded CPython (3.14t):** the core suites run green on
388
+ free-threaded 3.14 with the GIL verified disabled (CI job
389
+ `test-freethreaded`), and the Rust extension declares free-threaded safety
390
+ (`gil_used = false`). Free-threaded wheels are **not yet published** and
391
+ free-threaded builds are not officially supported — blocked upstream on
392
+ orjson (no free-threaded wheels) and hiredis (re-enables the GIL on import). See
393
+ [measured performance results](docs/free-threading.md#measured-performance) and the
394
+ full concurrency audit: [docs/free-threading.md](docs/free-threading.md).
395
+
396
+ **Per-Function Statistics:**
397
+ - Statistics tracked per function identity (`module.qualname`), shared across all calls and across re-decorations of the same function
398
+ - Thread-safe via RLock (all methods safe for concurrent access)
399
+ - Fork-safe: a forked child starts with zeroed counters and its own session ID
400
+
401
+ ```python
402
+ from concurrent.futures import ThreadPoolExecutor
403
+
404
+ @cache()
405
+ def expensive_func(x):
406
+ return x ** 2
407
+
408
+ # All threads share same stats
409
+ with ThreadPoolExecutor(max_workers=10) as executor:
410
+ results = list(executor.map(expensive_func, range(100)))
411
+
412
+ info = expensive_func.cache_info()
413
+ # CacheInfo(hits=..., misses=..., l1_hits=..., l2_hits=...,
414
+ # maxsize=None, currsize=None, l2_avg_latency_ms=...,
415
+ # last_operation_at=..., session_id=...)
416
+ ```
417
+
418
+ </details>
419
+
420
+ ---
421
+
422
+ ## Documentation
423
+
424
+ ### Start Here
425
+
426
+ | Guide | Description |
427
+ |:------|:------------|
428
+ | [Comparison Guide][comparison-url] | How cachekit compares to lru_cache, aiocache, cachetools |
429
+ | [Getting Started][getting-started-url] | Progressive tutorial from basics to advanced |
430
+ | [API Reference][api-reference-url] | Complete API documentation |
431
+ | [Skyline (live example)][skyline-url] | Canonical example project: this SDK ingests the Bluesky firehose and writes the analytics entries a TypeScript edge Worker serves live, on one shared interop namespace |
432
+
433
+ ### Feature Deep Dives
434
+
435
+ | Feature | Description |
436
+ |:--------|:------------|
437
+ | [Serializer Guide][serializer-guide-url] | ArrowSerializer vs StandardSerializer benchmarks |
438
+ | [Circuit Breaker][circuit-breaker-url] | Prevent cascading failures |
439
+ | [Distributed Locking][distributed-locking-url] | Cache stampede prevention |
440
+ | [Prometheus Metrics][prometheus-url] | Built-in observability |
441
+ | [Zero-Knowledge Encryption][encryption-url] | Client-side security |
442
+ | [Interop Mode][interop-url] | Cross-SDK cache sharing with cachekit-ts/rs |
443
+ | [L1 Invalidation & SWR][l1-invalidation-url] | Invalidation scope (incl. cross-process whole-function on tenant-scoped Redis from the environment or `RedisBackendProvider`), stale-while-revalidate |
444
+ | [Reference Caching][reference-caching-url] | `@cache.local()` for non-serializable objects |
445
+ | [Rust Serialization][rust-serialization-url] | ByteStorage layer: LZ4, xxHash3, AES-256-GCM |
446
+ | [SSRF Protection][ssrf-url] | URL allowlisting for the CachekitIO backend |
447
+
448
+ ---
449
+
450
+ ## Configuration
451
+
452
+ ### Environment Variables
453
+
454
+ ```bash
455
+ # Redis Connection (priority: CACHEKIT_REDIS_URL > REDIS_URL)
456
+ CACHEKIT_REDIS_URL="redis://localhost:6379" # Primary (preferred)
457
+ REDIS_URL="redis://localhost:6379" # Fallback
458
+
459
+ # CachekitIO SaaS Backend (closed beta — request access at cachekit.io)
460
+ CACHEKIT_API_KEY="your-api-key" # For @cache.io() — or pass api_key= directly # pragma: allowlist secret
461
+ CACHEKIT_API_URL="https://api.cachekit.io" # Default SaaS endpoint
462
+
463
+ # Memcached Backend (optional: pip install cachekit[memcached])
464
+ CACHEKIT_MEMCACHED_SERVERS='["mc1:11211", "mc2:11211"]' # Default: 127.0.0.1:11211
465
+ CACHEKIT_MEMCACHED_CONNECT_TIMEOUT=2.0 # Default: 2.0 seconds
466
+ CACHEKIT_MEMCACHED_TIMEOUT=1.0 # Default: 1.0 seconds
467
+ CACHEKIT_MEMCACHED_KEY_PREFIX="myapp:" # Default: "" (none)
468
+
469
+ # Optional Configuration
470
+ CACHEKIT_MAX_VALUE_SIZE=104857600
471
+ CACHEKIT_ARROW_COMPRESSION=zstd
472
+ ```
473
+
474
+ > [!NOTE]
475
+ > If both `CACHEKIT_REDIS_URL` and `REDIS_URL` are set, `CACHEKIT_REDIS_URL` takes precedence.
476
+
477
+ ---
478
+
479
+ ## Development
480
+
481
+ ```bash
482
+ git clone https://github.com/cachekit-io/cachekit-py.git
483
+ cd cachekit-py
484
+ uv sync && make install
485
+ make quick-check # format + lint + critical tests
486
+ ```
487
+
488
+ See [CONTRIBUTING.md][contributing-url] for full development guidelines.
489
+
490
+ ---
491
+
492
+ ## Requirements
493
+
494
+ | Component | Version |
495
+ |:----------|:--------|
496
+ | Python | 3.10+ |
497
+
498
+ ---
499
+
500
+ ## License
501
+
502
+ MIT License - see [LICENSE][license-file-url] for details.
503
+
504
+ ---
505
+
506
+ <div align="center">
507
+
508
+ **[PyPI][pypi-url]** · **[GitHub][github-url]** · **[Issues][issues-url]**
509
+
510
+ </div>
511
+
512
+ <!-- Reference Links -->
513
+ [pypi-badge]: https://img.shields.io/pypi/v/cachekit.svg
514
+ [python-badge]: https://img.shields.io/pypi/pyversions/cachekit.svg
515
+ [license-badge]: https://img.shields.io/badge/License-MIT-yellow.svg
516
+ [pypi-url]: https://pypi.org/project/cachekit/
517
+ [license-url]: https://opensource.org/licenses/MIT
518
+ [uv-url]: https://github.com/astral-sh/uv
519
+ [security-url]: SECURITY.md
520
+ [comparison-url]: docs/comparison.md
521
+ [skyline-url]: https://github.com/cachekit-io/bluesky-thinking
522
+ [getting-started-url]: docs/getting-started.md
523
+ [api-reference-url]: docs/api-reference.md
524
+ [serializer-guide-url]: docs/serializers/index.md
525
+ [circuit-breaker-url]: docs/features/circuit-breaker.md
526
+ [distributed-locking-url]: docs/features/distributed-locking.md
527
+ [prometheus-url]: docs/features/prometheus-metrics.md
528
+ [encryption-url]: docs/features/zero-knowledge-encryption.md
529
+ [interop-url]: docs/features/interop-mode.md
530
+ [l1-invalidation-url]: docs/features/l1-invalidation.md
531
+ [reference-caching-url]: docs/features/reference-caching.md
532
+ [rust-serialization-url]: docs/features/rust-serialization.md
533
+ [ssrf-url]: docs/features/ssrf-protection.md
534
+ [contributing-url]: CONTRIBUTING.md
535
+ [license-file-url]: LICENSE
536
+ [github-url]: https://github.com/cachekit-io/cachekit-py
537
+ [issues-url]: https://github.com/cachekit-io/cachekit-py/issues
538
+ [codecov-badge]: https://codecov.io/github/cachekit-io/cachekit-py/graph/badge.svg
539
+ [codecov-url]: https://codecov.io/github/cachekit-io/cachekit-py
cachekit/__init__.py ADDED
@@ -0,0 +1,136 @@
1
+ r"""cachekit - Caching decorator for Python applications.
2
+
3
+ Backend-agnostic caching with intent-based decorators — circuit breaker,
4
+ distributed locking, Prometheus metrics, and optional zero-knowledge
5
+ AES-256-GCM encryption, on a Rust-powered core.
6
+
7
+ Zero-config L1 in-memory by default; pluggable backends: Redis, Memcached,
8
+ File, CachekitIO SaaS, and custom via the BaseBackend protocol.
9
+
10
+ Key Features:
11
+ - **Intelligent @cache decorator** with auto-detection and intent-based optimization
12
+ - **Circuit breaker protection** against cascading failures
13
+ - **Backpressure control** to prevent backend overload
14
+ - **Connection pooling** for optimized performance
15
+ - **Health check methods** for comprehensive monitoring
16
+ - **Structured logging** with per-operation fields
17
+ - **Statistics collection** for Prometheus metrics integration
18
+
19
+ Architecture Overview:
20
+ cachekit provides a modular decorator architecture with intelligent
21
+ auto-detection and intent-based optimization:
22
+
23
+ - FeatureOrchestrator: Manages enterprise-grade reliability and monitoring features
24
+ - Flexible configuration interface with intelligent auto-detection
25
+ - Enhanced error handling with comprehensive safety checks
26
+ - Pluggable backend abstraction (Redis, Memcached, File, CachekitIO, custom)
27
+
28
+ Born from production debugging of caching failures:
29
+ - UTF-8 corruption prevention with intelligent binary data handling
30
+ - Binary data magic byte detection and validation
31
+ - Chunked storage for large objects with atomic operations
32
+ - Comprehensive checksum validation and recovery
33
+
34
+ Example Usage:
35
+ ```python
36
+ from cachekit import cache
37
+ from cachekit.config.nested import CircuitBreakerConfig
38
+
39
+ # Intelligent cache with zero configuration (90% of use cases)
40
+ @cache
41
+ def expensive_function():
42
+ return compute_result()
43
+
44
+ # Intent-based optimization (9% of use cases)
45
+ @cache.minimal # Speed-critical functions
46
+ def get_price(symbol: str):
47
+ return fetch_price(symbol)
48
+
49
+ @cache.production # Reliability-critical functions
50
+ def process_payment(amount: Decimal):
51
+ return payment_gateway.charge(amount)
52
+
53
+ @cache.secure # Security-critical functions
54
+ def get_user_data(user_id: int) -> UserProfile:
55
+ return db.fetch_user(user_id)
56
+
57
+ @cache.io() # Managed SaaS backend (cachekit.io, closed beta)
58
+ def get_remote_data(key: str):
59
+ return fetch_remote(key)
60
+
61
+ # Manual configuration when needed (1% of use cases)
62
+ @cache(ttl=3600, namespace="custom", circuit_breaker=CircuitBreakerConfig(failure_threshold=10))
63
+ def custom_function():
64
+ return special_computation()
65
+
66
+ # Health monitoring
67
+ health = custom_function.get_health_status()
68
+ full_health = custom_function.check_health()
69
+ ```
70
+ """
71
+
72
+ __version__ = "0.20.0"
73
+
74
+ from collections.abc import Callable
75
+ from typing import Any, TypeVar
76
+
77
+ # Configure hiredis compatibility BEFORE any Redis imports
78
+ # This prevents GIL warnings in Python 3.13+ free-threading mode
79
+ try:
80
+ from . import hiredis_compat
81
+ except ImportError:
82
+ pass
83
+
84
+ # Import the configuration classes
85
+ # Redis client is automatically fast - no special import needed
86
+ from .config import DecoratorConfig
87
+
88
+ # Import the intelligent cache decorator and CacheInfo
89
+ from .decorators import cache
90
+ from .decorators.wrapper import CacheInfo
91
+
92
+ # Import health check functionality
93
+ from .health import (
94
+ HealthCheckResult,
95
+ HealthLevel,
96
+ HealthStatus,
97
+ async_health_check_handler,
98
+ get_health_checker,
99
+ health_check_handler,
100
+ )
101
+
102
+ # Interop mode (interop/v1): cross-SDK key generation and value codec.
103
+ # The decorator API is @cache(interop="op", namespace="users"); these helpers are
104
+ # for manual key/value handling (debugging, out-of-band writers, migrations).
105
+ from .interop import (
106
+ InteropError,
107
+ decode_interop_value,
108
+ encode_interop_value,
109
+ generate_interop_key,
110
+ )
111
+
112
+ # L1/L2 architecture integrated into standard cache interface
113
+ # No separate imports needed - cache.minimal/.production/.secure handle L1+L2 transparently
114
+ # Import reliability configuration
115
+ from .reliability import CircuitBreakerConfig
116
+
117
+ F = TypeVar("F", bound=Callable[..., Any])
118
+
119
+
120
+ __all__ = [
121
+ "__version__",
122
+ "async_health_check_handler",
123
+ "cache",
124
+ "CacheInfo",
125
+ "CircuitBreakerConfig",
126
+ "DecoratorConfig",
127
+ "decode_interop_value",
128
+ "encode_interop_value",
129
+ "generate_interop_key",
130
+ "get_health_checker",
131
+ "health_check_handler",
132
+ "HealthCheckResult",
133
+ "HealthLevel",
134
+ "HealthStatus",
135
+ "InteropError",
136
+ ]