CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cement

Advanced Application Framework for Python with a focus on Command Line Interfaces

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

caching.mddocs/

Caching System

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.

Capabilities

Cache Handler Interface

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.
        """

Usage Examples

Basic Caching

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 cache

Redis Caching

from 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 stats

Memcached Caching

from 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:1642248000

Cache Wrapper Decorator

from 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 cache

Cache Configuration and Management

from 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 info

Install with Tessl CLI

npx tessl i tessl/pypi-cement

docs

arguments.md

caching.md

configuration.md

controllers.md

extensions.md

foundation.md

hooks.md

index.md

interface-handler.md

logging.md

mail.md

output.md

plugins.md

templates.md

utilities.md

tile.json