expiringdict
Thread-safe dictionary with auto-expiring keys — provides a dict subclass where entries automatically expire after a TTL. expiringdict features: ExpiringDict(max_len, max_age_seconds) constructor, standard dict interface (get/set/del/in/items/values), thread-safe via RLock, max_len for LRU-style eviction, max_age_seconds for TTL expiry, expired keys invisible on access (lazy expiry), copy() creates snapshot, and len() excludes expired entries. Simpler than cachetools TTLCache with fewer features but easier API.
Score Breakdown
⚙ Agent Friendliness
🔒 Security
Pure Python in-memory dict with no network calls. Do not cache secrets beyond their required lifespan — set max_age_seconds to match secret TTL. Thread-safe via RLock. Expired entries still in memory until lazily cleaned — sensitive data may persist beyond TTL until accessed.
⚡ Reliability
Best When
Simple TTL-expiring dict for rate limiting, deduplication windows, and short-lived session storage — expiringdict provides the simplest possible API for auto-expiring keys.
Avoid When
Complex eviction policies (use cachetools), high-throughput (use functools.lru_cache), or disk persistence (use cachelib).
Use Cases
- • Agent rate limiting tracker — from expiringdict import ExpiringDict; rate_tracker = ExpiringDict(max_len=10000, max_age_seconds=60); def check_rate(user_id): count = rate_tracker.get(user_id, 0); if count >= 100: raise RateLimitError(); rate_tracker[user_id] = count + 1 — rate limiting; agent tracks per-user request counts that reset after 60s automatically
- • Agent session token store — tokens = ExpiringDict(max_len=1000, max_age_seconds=3600); tokens[session_id] = user_data; user = tokens.get(session_id) — returns None if expired — session store; agent stores short-lived session tokens; expired tokens automatically invisible; no cleanup thread needed
- • Agent deduplication window — seen = ExpiringDict(max_len=50000, max_age_seconds=300); def is_duplicate(msg_id): if msg_id in seen: return True; seen[msg_id] = True; return False — dedup; agent deduplicates messages within 5-minute window; old IDs expire automatically; memory bounded by max_len
- • Agent cache with TTL — results = ExpiringDict(max_len=500, max_age_seconds=600); def get_result(key): cached = results.get(key); if cached is not None: return cached; result = expensive_compute(key); results[key] = result; return result — simple TTL cache; agent caches computation results for 10 minutes; simpler than cachetools for basic TTL
- • Agent connection pool health — health = ExpiringDict(max_len=100, max_age_seconds=30); health[endpoint] = True; if endpoint not in health: check_and_add() — freshness tracking; agent marks endpoints as healthy; entry expires after 30s forcing recheck; simpler than background health check threads
Not For
- • Complex eviction policies — expiringdict only does TTL+maxlen; for LRU, LFU, sized eviction use cachetools
- • High-performance caching — expiringdict is pure Python with per-access lock overhead; for high-throughput use cachetools or functools.lru_cache
- • Persistent caching — expiringdict is in-memory only; for persistence use cachelib or diskcache
Interface
Authentication
No auth — in-memory data structure library.
Pricing
expiringdict is Apache 2.0 licensed. Free for all use.
Agent Metadata
Known Gotchas
- ⚠ get() returns None for both missing AND expired — cannot distinguish expired from never-set; agent code: use cache.get(key) is None as cache miss indicator; do NOT store None as a valid value in expiringdict — ambiguous with cache miss; if None is needed as a value, use a sentinel: MISSING = object(); if cache.get(key, MISSING) is MISSING: compute
- ⚠ items() and values() include only non-expired at call time — expiringdict.items() filters expired entries; but entries can expire between items() call and iteration; agent code iterating items() should be aware of this race condition; for atomic snapshot: dict(expiringdict.items()) creates copy but still races
- ⚠ max_len eviction is approximate and FIFO-like — when max_len exceeded, oldest entries evicted (insertion order); not LRU; agent code assuming LRU behavior (most-used entries stay) will be surprised; expiringdict evicts oldest regardless of access frequency; for LRU behavior use cachetools.LRUCache instead
- ⚠ No bulk expiry or cleanup API — expiringdict has no expire_all() or cleanup() method; expired entries removed lazily on access; agent code needing immediate cleanup of all expired entries must iterate: [k for k in cache.keys()] to trigger lazy cleanup; or recreate the ExpiringDict
- ⚠ Thread safety via RLock not asyncio-safe — expiringdict uses threading.RLock; not safe for concurrent asyncio tasks in same event loop without additional sync; agent async code: use asyncio.Lock wrapping expiringdict access; or use per-task instances without sharing; RLock only protects threading.Thread concurrency
- ⚠ ExpiringDict is not a defaultdict — cache.get(key, default) returns default but does NOT store default in cache; agent code expecting cache population on get() miss must: value = cache.get(key); if value is None: value = compute(); cache[key] = value; returning default without storing is intentional but differs from defaultdict behavior
Alternatives
Full Evaluation Report
Comprehensive deep-dive: security analysis, reliability audit, agent experience review, cost modeling, competitive positioning, and improvement roadmap for expiringdict.
AI-powered analysis · PDF + markdown · Delivered within 30 minutes
Package Brief
Quick verdict, integration guide, cost projections, gotchas with workarounds, and alternatives comparison.
Delivered within 10 minutes
Score Monitoring
Get alerted when this package's AF, security, or reliability scores change significantly. Stay ahead of regressions.
Continuous monitoring
Scores are editorial opinions as of 2026-03-06.