Advanced Application Framework for Python with a focus on Command Line Interfaces
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The caching system provides caching interface supporting multiple backends including Redis and Memcached. It offers key-value storage for improved application performance with configurable backends and TTL support.
Base interface for caching functionality that defines the contract for cache operations.
class CacheHandler:
"""
Cache handler interface for key-value caching operations.
Provides methods for storing, retrieving, and managing cached
data with support for expiration and cache invalidation.
"""
def get(self, key: str) -> Any:
"""
Get a value from cache by key.
Args:
key: Cache key to retrieve
Returns:
Cached value or None if key not found or expired
"""
def set(self, key: str, value: Any, time: int = None) -> None:
"""
Set a value in cache with optional expiration.
Args:
key: Cache key to store under
value: Value to cache (must be serializable)
time: Expiration time in seconds (None for no expiration)
"""
def delete(self, key: str) -> None:
"""
Delete a key from cache.
Args:
key: Cache key to delete
"""
def purge(self) -> None:
"""
Clear all cached data.
Removes all keys and values from the cache.
"""from cement import App, Controller, ex, init_defaults
import time
CONFIG = init_defaults('myapp')
CONFIG['cache.redis'] = {
'host': 'localhost',
'port': 6379,
'db': 0,
'password': None
}
class DataController(Controller):
class Meta:
label = 'data'
stacked_on = 'base'
stacked_type = 'nested'
@ex(help='get user data with caching')
def user_info(self):
"""Get user information with caching."""
user_id = 12345
cache_key = f'user:{user_id}'
# Try to get from cache first
user_data = self.app.cache.get(cache_key)
if user_data is None:
print('Cache miss - fetching from database')
# Simulate database query
time.sleep(1)
user_data = {
'id': user_id,
'name': 'John Doe',
'email': 'john@example.com',
'last_login': '2023-01-15T10:30:00Z'
}
# Cache for 5 minutes
self.app.cache.set(cache_key, user_data, time=300)
print('Data cached')
else:
print('Cache hit - returning cached data')
output = self.app.render(user_data)
print(output)
@ex(help='clear user cache')
def clear_cache(self):
"""Clear user cache."""
user_id = 12345
cache_key = f'user:{user_id}'
self.app.cache.delete(cache_key)
print(f'Cleared cache for {cache_key}')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
extensions = ['redis']
cache_handler = 'redis'
config_defaults = CONFIG
handlers = [BaseController, DataController]
with MyApp() as app:
app.run()
# Usage:
# myapp data user-info # First call - cache miss
# myapp data user-info # Second call - cache hit
# myapp data clear-cache # Clear cachefrom cement import App, Controller, ex, init_defaults
import json
import time
CONFIG = init_defaults('myapp')
CONFIG['cache.redis'] = {
'host': 'localhost',
'port': 6379,
'db': 1, # Use database 1 for this app
'password': 'your_redis_password',
'socket_timeout': 5,
'connection_pool_kwargs': {
'max_connections': 50
}
}
class ApiController(Controller):
class Meta:
label = 'api'
stacked_on = 'base'
stacked_type = 'nested'
def fetch_api_data(self, endpoint):
"""Simulate API data fetching."""
print(f'Fetching data from API: {endpoint}')
time.sleep(2) # Simulate API delay
return {
'endpoint': endpoint,
'data': ['item1', 'item2', 'item3'],
'timestamp': time.time()
}
@ex(
help='get API data with Redis caching',
arguments=[
(['endpoint'], {'help': 'API endpoint to fetch'})
]
)
def fetch(self):
"""Fetch API data with Redis caching."""
endpoint = self.app.pargs.endpoint
cache_key = f'api:data:{endpoint}'
# Check cache first
cached_data = self.app.cache.get(cache_key)
if cached_data:
print('Returning cached API data')
print(f'Cache key: {cache_key}')
data = json.loads(cached_data)
else:
print('Cache miss - fetching from API')
data = self.fetch_api_data(endpoint)
# Cache for 10 minutes
self.app.cache.set(cache_key, json.dumps(data), time=600)
print(f'Data cached with key: {cache_key}')
output = self.app.render(data)
print(output)
@ex(help='show cache statistics')
def stats(self):
"""Show Redis cache statistics."""
# This would typically use Redis commands
# For demo purposes, show cached keys
print('Cache Statistics:')
print('- Cache backend: Redis')
print('- Connection: Active')
print('- Sample operations demonstrated')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
extensions = ['redis']
cache_handler = 'redis'
config_defaults = CONFIG
handlers = [BaseController, ApiController]
with MyApp() as app:
app.run()
# Usage:
# myapp api fetch /users/active
# myapp api fetch /products/popular
# myapp api statsfrom cement import App, Controller, ex, init_defaults
import pickle
CONFIG = init_defaults('myapp')
CONFIG['cache.memcached'] = {
'hosts': ['127.0.0.1:11211'],
'binary': True,
'behaviors': {
'tcp_nodelay': True,
'ketama': True
}
}
class SessionController(Controller):
class Meta:
label = 'session'
stacked_on = 'base'
stacked_type = 'nested'
@ex(
help='create user session',
arguments=[
(['username'], {'help': 'username for session'})
]
)
def create(self):
"""Create user session with Memcached."""
username = self.app.pargs.username
session_id = f'session:{username}:{int(time.time())}'
session_data = {
'username': username,
'created_at': time.time(),
'permissions': ['read', 'write'],
'preferences': {
'theme': 'dark',
'language': 'en'
}
}
# Store session for 1 hour
self.app.cache.set(session_id, session_data, time=3600)
print(f'Session created: {session_id}')
print(f'Session data: {session_data}')
@ex(
help='get user session',
arguments=[
(['session_id'], {'help': 'session ID to retrieve'})
]
)
def get(self):
"""Get user session from Memcached."""
session_id = self.app.pargs.session_id
session_data = self.app.cache.get(session_id)
if session_data:
print(f'Session found: {session_id}')
output = self.app.render(session_data)
print(output)
else:
print(f'Session not found or expired: {session_id}')
@ex(
help='delete user session',
arguments=[
(['session_id'], {'help': 'session ID to delete'})
]
)
def delete(self):
"""Delete user session from Memcached."""
session_id = self.app.pargs.session_id
self.app.cache.delete(session_id)
print(f'Session deleted: {session_id}')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
extensions = ['memcached']
cache_handler = 'memcached'
config_defaults = CONFIG
handlers = [BaseController, SessionController]
with MyApp() as app:
app.run()
# Usage:
# myapp session create johndoe
# myapp session get session:johndoe:1642248000
# myapp session delete session:johndoe:1642248000from cement import App, Controller, ex
import functools
import hashlib
import json
import time
def cached(timeout=300, key_prefix=''):
"""Decorator to cache function results."""
def decorator(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
# Generate cache key from function name and arguments
key_data = {
'function': func.__name__,
'args': args,
'kwargs': kwargs
}
key_string = json.dumps(key_data, sort_keys=True)
key_hash = hashlib.md5(key_string.encode()).hexdigest()
cache_key = f'{key_prefix}{func.__name__}:{key_hash}'
# Try to get from cache
result = self.app.cache.get(cache_key)
if result is None:
print(f'Cache miss for {func.__name__}')
result = func(self, *args, **kwargs)
self.app.cache.set(cache_key, result, time=timeout)
print(f'Result cached with key: {cache_key}')
else:
print(f'Cache hit for {func.__name__}')
return result
return wrapper
return decorator
class ComputeController(Controller):
class Meta:
label = 'compute'
stacked_on = 'base'
stacked_type = 'nested'
@cached(timeout=600, key_prefix='compute:')
def expensive_calculation(self, n):
"""Expensive calculation that benefits from caching."""
print(f'Performing expensive calculation for n={n}')
# Simulate expensive computation
time.sleep(3)
result = sum(i * i for i in range(n))
return result
@ex(
help='perform cached calculation',
arguments=[
(['number'], {'type': int, 'help': 'number for calculation'})
]
)
def calculate(self):
"""Perform calculation with caching."""
n = self.app.pargs.number
start_time = time.time()
result = self.expensive_calculation(n)
end_time = time.time()
print(f'Result: {result}')
print(f'Execution time: {end_time - start_time:.2f} seconds')
@ex(help='clear computation cache')
def clear(self):
"""Clear all cached computations."""
self.app.cache.purge()
print('Computation cache cleared')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
extensions = ['redis']
cache_handler = 'redis'
handlers = [BaseController, ComputeController]
with MyApp() as app:
app.run()
# Usage:
# myapp compute calculate 1000 # First call - slow
# myapp compute calculate 1000 # Second call - fast (cached)
# myapp compute clear # Clear cachefrom cement import App, Controller, ex, init_defaults
CONFIG = init_defaults('myapp')
CONFIG['cache.redis'] = {
'host': 'localhost',
'port': 6379,
'db': 0,
'default_timeout': 300, # 5 minutes default
'key_prefix': 'myapp:'
}
class CacheController(Controller):
class Meta:
label = 'cache'
stacked_on = 'base'
stacked_type = 'nested'
@ex(
help='set cache value',
arguments=[
(['key'], {'help': 'cache key'}),
(['value'], {'help': 'cache value'}),
(['--ttl'], {'type': int, 'help': 'time to live in seconds'})
]
)
def set(self):
"""Set a cache value."""
key = self.app.pargs.key
value = self.app.pargs.value
ttl = self.app.pargs.ttl
self.app.cache.set(key, value, time=ttl)
ttl_msg = f' (TTL: {ttl}s)' if ttl else ' (no expiration)'
print(f'Set cache key "{key}" = "{value}"{ttl_msg}')
@ex(
help='get cache value',
arguments=[
(['key'], {'help': 'cache key to retrieve'})
]
)
def get(self):
"""Get a cache value."""
key = self.app.pargs.key
value = self.app.cache.get(key)
if value is not None:
print(f'Cache key "{key}" = "{value}"')
else:
print(f'Cache key "{key}" not found or expired')
@ex(
help='delete cache key',
arguments=[
(['key'], {'help': 'cache key to delete'})
]
)
def delete(self):
"""Delete a cache key."""
key = self.app.pargs.key
self.app.cache.delete(key)
print(f'Deleted cache key "{key}"')
@ex(help='clear all cache')
def clear(self):
"""Clear all cached data."""
self.app.cache.purge()
print('All cache data cleared')
@ex(help='show cache info')
def info(self):
"""Show cache configuration information."""
cache_config = self.app.config.get_section_dict('cache.redis')
print('Cache Configuration:')
print(f' Backend: Redis')
print(f' Host: {cache_config.get("host", "localhost")}')
print(f' Port: {cache_config.get("port", 6379)}')
print(f' Database: {cache_config.get("db", 0)}')
print(f' Default TTL: {cache_config.get("default_timeout", 300)}s')
print(f' Key Prefix: {cache_config.get("key_prefix", "")}')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
extensions = ['redis']
cache_handler = 'redis'
config_defaults = CONFIG
handlers = [BaseController, CacheController]
with MyApp() as app:
app.run()
# Usage:
# myapp cache set user:123 "John Doe" --ttl 60
# myapp cache get user:123
# myapp cache delete user:123
# myapp cache clear
# myapp cache infoInstall with Tessl CLI
npx tessl i tessl/pypi-cement