CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-stamina

Production-grade retries made easy.

Overview
Eval results
Files

configuration.mddocs/

Configuration and Testing

Global configuration functions for controlling retry behavior system-wide and enabling test mode with modified retry parameters for faster test execution and predictable behavior.

Capabilities

Global Activation Control

Control whether retry behavior is active across the entire application. When deactivated, all retry decorators and contexts will execute only once without retries.

def is_active() -> bool:
    """
    Check whether retrying is globally active.
    
    Returns:
    bool: True if retrying is active, False otherwise
    """

def set_active(active: bool) -> None:
    """
    Globally activate or deactivate retrying.
    
    When deactivated, all retry decorators and retry contexts will 
    execute only once regardless of configured attempts.
    
    Parameters:
    - active: bool - Whether to activate retrying
    
    This function is idempotent and can be called repeatedly.
    """

Usage Examples:

import stamina

# Check current state
print(f"Retries active: {stamina.is_active()}")  # True by default

# Temporarily disable retries
stamina.set_active(False)

@stamina.retry(on=Exception, attempts=5)  
def unreliable_function():
    raise ValueError("This will not retry")

try:
    unreliable_function()  # Fails immediately, no retries
except ValueError:
    print("Function failed without retries")

# Re-enable retries
stamina.set_active(True)

# Emergency circuit breaker pattern
def emergency_disable_retries():
    """Disable retries when system is under stress."""
    stamina.set_active(False)
    logger.warning("Retries disabled due to system overload")

def restore_retries():
    """Re-enable retries when system recovers."""
    stamina.set_active(True)
    logger.info("Retries re-enabled")

Test Mode Configuration

Test mode provides predictable retry behavior for testing scenarios by disabling backoff delays and controlling attempt counts.

def is_testing() -> bool:
    """
    Check whether test mode is enabled.
    
    Returns:
    bool: True if test mode is active, False otherwise
    """

def set_testing(
    testing: bool, 
    *, 
    attempts: int = 1, 
    cap: bool = False
) -> _RestoreTestingCM:
    """
    Activate or deactivate test mode.
    
    In test mode:
    - All backoff delays are set to 0 (no waiting)
    - Attempt counts are controlled by the attempts parameter
    
    Parameters:
    - testing: bool - Whether to enable test mode
    - attempts: int - Number of attempts in test mode (default: 1)
    - cap: bool - If True, cap attempts rather than override (default: False)
    
    Returns:
    Context manager that restores previous testing state when exited
    
    When cap=True:
    - If attempts=3 and user specifies 5 attempts, use 3 (capped)
    - If attempts=3 and user specifies 2 attempts, use 2 (user's value)
    
    This function can be used as a context manager and is idempotent.
    """

Usage Examples:

import stamina

# Basic test mode usage
def test_retry_behavior():
    # Enable test mode with single attempt
    stamina.set_testing(True, attempts=1)
    
    @stamina.retry(on=ValueError, attempts=5)  # Will only try once
    def failing_function():
        raise ValueError("Test failure")
    
    try:
        failing_function()
        assert False, "Should have failed"
    except ValueError:
        pass  # Expected
    
    # Disable test mode
    stamina.set_testing(False)

# Test mode with multiple attempts
def test_eventual_success():
    stamina.set_testing(True, attempts=3)  # Allow 3 attempts in test
    
    call_count = 0
    
    @stamina.retry(on=ValueError, attempts=10)  # Normally 10, but test caps at 3
    def eventually_succeeds():
        nonlocal call_count
        call_count += 1
        if call_count < 3:
            raise ValueError("Not yet")
        return "success"
    
    result = eventually_succeeds()
    assert result == "success"
    assert call_count == 3
    
    stamina.set_testing(False)

# Context manager usage
def test_with_context_manager():
    with stamina.set_testing(True, attempts=2):
        # Test code here runs with test mode
        @stamina.retry(on=Exception, attempts=5)
        def test_function():
            raise Exception("Test")
        
        try:
            test_function()  # Will try 2 times, not 5
        except Exception:
            pass
    
    # Test mode automatically restored after context

# Capped attempts mode
def test_capped_attempts():
    with stamina.set_testing(True, attempts=3, cap=True):
        # User specifies 2 attempts, test allows up to 3, so use 2
        @stamina.retry(on=ValueError, attempts=2)
        def function_a():
            raise ValueError("Test")
        
        # User specifies 5 attempts, test caps at 3, so use 3  
        @stamina.retry(on=ValueError, attempts=5)
        def function_b():
            raise ValueError("Test")

Testing Patterns

Common patterns for testing retry behavior:

import pytest
import stamina

class TestRetryBehavior:
    def setup_method(self):
        """Reset retry state before each test."""
        stamina.set_active(True)
        stamina.set_testing(False)
    
    def test_successful_retry(self):
        """Test that retries eventually succeed."""
        with stamina.set_testing(True, attempts=3):
            call_count = 0
            
            @stamina.retry(on=ValueError, attempts=5)
            def flaky_function():
                nonlocal call_count
                call_count += 1
                if call_count < 3:
                    raise ValueError("Not ready")
                return "success"
            
            result = flaky_function()
            assert result == "success"
            assert call_count == 3
    
    def test_exhausted_retries(self):
        """Test behavior when all retries are exhausted."""
        with stamina.set_testing(True, attempts=2):
            call_count = 0
            
            @stamina.retry(on=ValueError, attempts=3)  # Will be capped at 2
            def always_fails():
                nonlocal call_count
                call_count += 1
                raise ValueError(f"Failure {call_count}")
            
            with pytest.raises(ValueError, match="Failure 2"):
                always_fails()
            
            assert call_count == 2
    
    def test_no_retries_when_inactive(self):
        """Test that inactive mode prevents retries.""" 
        stamina.set_active(False)
        
        call_count = 0
        
        @stamina.retry(on=ValueError, attempts=5)
        def should_not_retry():
            nonlocal call_count
            call_count += 1
            raise ValueError("Single failure")
        
        with pytest.raises(ValueError):
            should_not_retry()
        
        assert call_count == 1  # Called only once

    def test_context_manager_cleanup(self):
        """Test that context managers properly restore state."""
        original_testing = stamina.is_testing()
        
        try:
            with stamina.set_testing(True, attempts=2):
                assert stamina.is_testing() == True
                # Test code here
            
            # State should be restored
            assert stamina.is_testing() == original_testing
        finally:
            stamina.set_testing(original_testing)

# Pytest fixtures for common test setups
@pytest.fixture
def no_retries():
    """Fixture to disable retries for fast tests."""
    with stamina.set_testing(True, attempts=1):
        yield

@pytest.fixture  
def fast_retries():
    """Fixture for tests that need some retries but fast execution."""
    with stamina.set_testing(True, attempts=3):
        yield

def test_with_no_retries(no_retries):
    """Test using the no_retries fixture."""
    @stamina.retry(on=ValueError, attempts=10)
    def fails_once():
        raise ValueError("Test failure")
    
    with pytest.raises(ValueError):
        fails_once()  # Fails immediately

def test_with_fast_retries(fast_retries):
    """Test using the fast_retries fixture."""
    call_count = 0
    
    @stamina.retry(on=ValueError, attempts=10)
    def succeeds_on_third():
        nonlocal call_count
        call_count += 1
        if call_count < 3:
            raise ValueError("Not yet")
        return "success"
    
    result = succeeds_on_third()
    assert result == "success"

Configuration Best Practices

Application Startup

Configure retry behavior during application initialization:

import stamina
import os

def configure_retries():
    """Configure retry behavior based on environment."""
    env = os.getenv("ENVIRONMENT", "production")
    
    if env == "test":
        # Fast, predictable retries for tests
        stamina.set_testing(True, attempts=2)
    elif env == "development":
        # Shorter timeouts for development
        # Use default active behavior but configure individual retries
        pass
    elif env == "production":
        # Full retry behavior (default)
        stamina.set_active(True)
    else:
        raise ValueError(f"Unknown environment: {env}")

# Call during app startup
configure_retries()

Health Check Integration

Integrate with application health checks:

import stamina
from your_app import health_check

def monitor_system_health():
    """Disable retries when system is unhealthy."""
    if not health_check.is_healthy():
        stamina.set_active(False)
        logger.warning("Retries disabled due to system health issues")
    else:
        stamina.set_active(True)

# Run periodically
import threading
import time

def health_monitor_loop():
    while True:
        monitor_system_health()
        time.sleep(30)  # Check every 30 seconds

health_thread = threading.Thread(target=health_monitor_loop, daemon=True)
health_thread.start()

Types

class _RestoreTestingCM:
    """Context manager that restores previous testing state when exited."""
    def __enter__(self) -> None: ...
    def __exit__(self, exc_type, exc_val, exc_tb) -> None: ...

Install with Tessl CLI

npx tessl i tessl/pypi-stamina

docs

configuration.md

index.md

instrumentation.md

retry-callers.md

retry-core.md

tile.json