Simple, fast async session management for Python
Dead simple session management with automatic expiration, multiple storage backends, and multi-tenant isolation. Perfect for web apps, APIs, and any system needing reliable sessions.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Application β
βββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββ
β
βββββββββββββββ΄ββββββββββββββ
β Convenience API Layer β
β get_session() / session β
βββββββββββββββ¬ββββββββββββββ
β
βββββββββββββββ΄ββββββββββββββ
β SessionManager β
β β’ Lifecycle Management β
β β’ TTL & Expiration β
β β’ Metadata & Validation β
β β’ Multi-tenant Isolation β
βββββββββββββββ¬ββββββββββββββ
β
βββββββββββββββ΄ββββββββββββββ
β Provider Factory β
β Auto-detect from env β
βββββββββββββββ¬ββββββββββββββ
β
βββββββββββββββββββ΄ββββββββββββββββββ
β β
βββββββββββββΌβββββββββββ βββββββββββββΌβββββββββββ
β Memory Provider β β Redis Provider β
β β’ In-process cache β β β’ Persistent store β
β β’ 1.3M ops/sec β β β’ Distributed β
β β’ Dev/Testing β β β’ Persistent store β
ββββββββββββββββββββββββ ββββββββββββββββββββββββ
Features:
β Pydantic models with validation β Type-safe enums (no magic strings)
β Automatic TTL expiration β Multi-sandbox isolation
β CSRF protection utilities β Cryptographic session IDs
β 202 tests, 90% coverage β Battle-tested
# Basic installation (memory provider only)
pip install chuk-sessions
# With Redis support
pip install chuk-sessions[redis]
# Full installation with all optional dependencies
pip install chuk-sessions[all]
# Development installation
pip install chuk-sessions[dev]import asyncio
from chuk_sessions import get_session
async def main():
async with get_session() as session:
# Store with auto-expiration
await session.setex("user:123", 3600, "Alice") # 1 hour TTL
# Retrieve
user = await session.get("user:123") # "Alice"
# Automatically expires after TTL
asyncio.run(main())That's it! Sessions automatically expire and you get instant performance.
Session Lifecycle:
βββββββββββββββ
β 1. Create β mgr.allocate_session(user_id="alice")
ββββββββ¬βββββββ
β β Returns session_id: "sess-alice-1234..."
βΌ
βββββββββββββββ
β 2. Validate β mgr.validate_session(session_id)
ββββββββ¬βββββββ
β β Returns: True (session exists & not expired)
βΌ
βββββββββββββββ
β 3. Use β mgr.get_session_info(session_id)
ββββββββ¬βββββββ mgr.update_session_metadata(...)
β β Access/modify session data
βΌ
βββββββββββββββ
β 4. Extend β mgr.extend_session_ttl(session_id, hours=2)
ββββββββ¬βββββββ (optional - keep session alive)
β
βΌ
βββββββββββββββ
β 5. Expire β Automatic after TTL
ββββββββ¬βββββββ or mgr.delete_session(session_id)
β
βΌ
[Done]
- π― Pydantic Native: All models are Pydantic-based with automatic validation
- π Type-Safe Enums: No more magic strings -
SessionStatus.ACTIVE,ProviderType.REDIS - π¦ Exported Types: Full IDE autocomplete for
SessionMetadata,CSRFTokenInfo, etc. - β‘ Async Native: Built from ground-up for async/await
- π Backward Compatible: Existing code works unchanged
- ποΈ Bounded LRU Cache: In-process session cache is capped at 1024 entries (evicts LRU) β no unbounded memory growth
- β 90%+ Test Coverage: 264 tests, battle-tested
from chuk_sessions import SessionManager, SessionStatus, SessionMetadata
# Type-safe with IDE autocomplete
mgr = SessionManager(sandbox_id="my-app")
session_id = await mgr.allocate_session()
# Pydantic models with validation
info: dict = await mgr.get_session_info(session_id)
metadata = SessionMetadata(**info)
print(metadata.status) # SessionStatus.ACTIVEfrom chuk_sessions import get_session
async with get_session() as session:
await session.set("key", "value") # Default 1hr expiration
await session.setex("temp", 60, "expires") # Custom 60s expiration
value = await session.get("key") # Auto-cleanup when expiredfrom chuk_sessions import SessionManager
# Each app gets isolated sessions
web_app = SessionManager(sandbox_id="web-portal")
api_service = SessionManager(sandbox_id="api-gateway")
# Full session lifecycle
session_id = await web_app.allocate_session(
user_id="alice@example.com",
custom_metadata={"role": "admin", "login_time": "2024-01-01T10:00:00Z"}
)
# Validate, extend, update
await web_app.validate_session(session_id)
await web_app.extend_session_ttl(session_id, additional_hours=2)
await web_app.update_session_metadata(session_id, {"last_activity": "now"})# Development - blazing fast in-memory (default)
export SESSION_PROVIDER=memory
# Persistent - Redis standalone (requires chuk-sessions[redis])
export SESSION_PROVIDER=redis
export SESSION_REDIS_URL=redis://localhost:6379/0
# Persistent - Redis Cluster with automatic detection
export SESSION_PROVIDER=redis
export SESSION_REDIS_URL=redis://node1:7000,node2:7001,node3:7002Actual performance from examples/performance_test.py:
| Provider | Operation | Throughput | Avg Latency | P95 Latency |
|---|---|---|---|---|
| Memory | GET | 1,312,481 ops/sec | 0.001ms | 0.001ms |
| Memory | SET | 1,141,011 ops/sec | 0.001ms | 0.001ms |
| Memory | DELETE | 1,481,848 ops/sec | 0.001ms | 0.001ms |
| Redis | GET | ~20K ops/sec | 0.05ms | 0.08ms |
| Redis | SET | ~18K ops/sec | 0.06ms | 0.09ms |
Concurrent Access (5 sessions, 500 ops):
- Overall Throughput: 406,642 ops/sec
- Average Latency: 0.002ms
Based on examples/chuk_session_example.py:
web_app = SessionManager(sandbox_id="my-web-app")
# Login
session_id = await web_app.allocate_session(
user_id="alice@example.com",
ttl_hours=8,
custom_metadata={"role": "admin", "theme": "dark"}
)
# Middleware validation
if not await web_app.validate_session(session_id):
raise Unauthorized("Please log in")api = SessionManager(sandbox_id="api-gateway", default_ttl_hours=1)
session_id = await api.allocate_session(
user_id="client_123",
custom_metadata={"tier": "premium", "requests": 0, "limit": 1000}
)
# Check/update rate limits
info = await api.get_session_info(session_id)
requests = info['custom_metadata']['requests']
if requests >= info['custom_metadata']['limit']:
raise RateLimitExceeded()
await api.update_session_metadata(session_id, {"requests": requests + 1})from chuk_sessions import get_session
async with get_session() as session:
# Email verification code (10 minute expiry)
await session.setex(f"verify:{email}", 600, "ABC123")
# Later: verify and consume
code = await session.get(f"verify:{email}")
if code == user_code:
await session.delete(f"verify:{email}") # One-time use
return TrueSet via environment variables:
# Provider selection
export SESSION_PROVIDER=memory # Default - no extra dependencies
export SESSION_PROVIDER=redis # Requires: pip install chuk-sessions[redis]
# TTL settings
export SESSION_DEFAULT_TTL=3600 # 1 hour default
# Redis config (if using redis provider)
# Standalone Redis
export SESSION_REDIS_URL=redis://localhost:6379/0
# Redis Cluster (comma-separated hosts - automatically detected)
export SESSION_REDIS_URL=redis://node1:7000,node2:7001,node3:7002
# Redis with TLS
export SESSION_REDIS_URL=rediss://localhost:6380/0
export REDIS_TLS_INSECURE=1 # Set to 1 to skip certificate verification (dev only)| Command | Includes | Use Case |
|---|---|---|
pip install chuk-sessions |
Memory provider only | Development, testing, lightweight apps |
pip install chuk-sessions[redis] |
+ Redis support | Persistent apps with Redis |
pip install chuk-sessions[all] |
All optional features | Maximum compatibility |
pip install chuk-sessions[dev] |
Development tools | Contributing, testing |
from chuk_sessions import get_session
async with get_session() as session:
await session.set(key, value) # Store with default TTL
await session.setex(key, ttl, value) # Store with custom TTL (seconds)
value = await session.get(key) # Retrieve (None if expired)
deleted = await session.delete(key) # Delete (returns bool)from chuk_sessions import SessionManager
mgr = SessionManager(sandbox_id="my-app", default_ttl_hours=24)
# Session lifecycle
session_id = await mgr.allocate_session(user_id="alice", custom_metadata={})
is_valid = await mgr.validate_session(session_id)
info = await mgr.get_session_info(session_id)
success = await mgr.update_session_metadata(session_id, {"key": "value"})
success = await mgr.extend_session_ttl(session_id, additional_hours=2)
success = await mgr.delete_session(session_id)
# Admin helpers
stats = mgr.get_cache_stats()
cleaned = await mgr.cleanup_expired_sessions()All examples are tested and working! Run them to see CHUK Sessions in action:
# Simple 3-line example - perfect first step
python examples/simple_example.py
# Interactive tutorial with explanations
python examples/quickstart.pyOutput:
User: Alice
Token: secret123
Missing: None
# Complete feature demonstration
python examples/chuk_session_example.pyShows:
- β Low-level provider usage (memory/redis)
- β High-level SessionManager API
- β Multi-sandbox isolation (multi-tenant)
- β Real-world scenarios (web app, MCP server, API gateway)
- β Error handling & admin helpers
# Benchmark your system
python examples/performance_test.pyOutput includes:
- Throughput measurements (1.3M+ ops/sec)
- Latency percentiles (P50, P95, P99)
- Memory usage analysis
- Concurrent access tests
- README-ready performance tables
# CSRF protection examples
python examples/csrf_demo.py
# Secure session ID generation
python examples/session_id_demo.pyFeatures demonstrated:
- HMAC-based CSRF tokens
- Double-submit cookie pattern
- Encrypted stateless tokens
- Cryptographic session IDs with entropy analysis
- Protocol-specific formats (MCP, HTTP, WebSocket, JWT)
- Simple: One import, one line to start storing sessions
- Fast: 1.8M ops/sec in memory, 20K ops/sec with Redis
- Reliable: Automatic TTL, proper error handling, battle-tested
- Flexible: Works for simple key-value storage or complex session management
- Isolated: Multi-tenant by design with sandbox separation
- Optional Dependencies: Install only what you need
Perfect for web frameworks, API servers, MCP implementations, or any Python app needing sessions.
# Clone and install dependencies
git clone https://github.com/chrishayuk/chuk-sessions.git
cd chuk-sessions
make dev-install
# Run tests
make test
# Run tests with coverage (90%+ coverage)
make test-cov
# Run all checks (lint, typecheck, security, tests)
make check
# Format code
make format
# Build package
make build# Bump version
make bump-patch # 0.5 β 0.6
make bump-minor # 0.5 β 1.0
make bump-major # 0.5 β 1.0.0
# Create release (triggers GitHub Actions β PyPI)
make publishmake test- Run testsmake test-cov- Run tests with coverage reportmake lint- Run code linters (ruff)make format- Auto-format codemake typecheck- Run type checking (mypy)make security- Run security checks (bandit)make check- Run all checksmake clean- Clean build artifactsmake build- Build distribution packagesmake publish- Create tag and trigger automated release
See make help for all available commands.
Apache 2.0