{"library":"pybreaker","title":"PyBreaker","description":"PyBreaker is a Python implementation of the Circuit Breaker pattern, a resilience mechanism described in Michael T. Nygard's book 'Release It!'. It prevents cascading failures in distributed systems by detecting unhealthy services and temporarily stopping requests to them. The library provides configurable failure thresholds, reset timeouts, and optional Redis backing for state management. It is actively maintained, with the current version being 1.4.1, and receives regular updates.","status":"active","version":"1.4.1","language":"en","source_language":"en","source_url":"http://github.com/danielfm/pybreaker","tags":["circuit breaker","resilience","fault tolerance","microservices","distributed systems"],"install":[{"cmd":"pip install pybreaker","lang":"bash","label":"Install stable version"},{"cmd":"pip install pybreaker[redis]","lang":"bash","label":"Install with Redis support"}],"dependencies":[{"reason":"Requires Python 3.9 or newer.","package":"python","optional":false},{"reason":"Optional dependency for `CircuitRedisStorage` to enable shared state across multiple application instances.","package":"redis","optional":true}],"imports":[{"note":"The CircuitBreaker class is the primary entry point for creating and managing circuit breakers.","wrong":"import pybreaker; breaker = pybreaker.CircuitBreaker() without explicitly importing the class","symbol":"CircuitBreaker","correct":"from pybreaker import CircuitBreaker"},{"note":"Used for externalizing circuit breaker state to Redis, enabling distributed circuit breakers. Requires 'redis' package.","wrong":"from pybreaker.storage import CircuitRedisStorage (older paths)","symbol":"CircuitRedisStorage","correct":"from pybreaker import CircuitRedisStorage"},{"note":"Exception raised when a circuit is open and prevents an operation from executing. Essential for handling fallback logic.","symbol":"CircuitBreakerError","correct":"from pybreaker import CircuitBreakerError"}],"quickstart":{"code":"import pybreaker\nimport requests\n\n# Simulate a flaky external service\n_service_up = True\n\ndef external_service_call():\n    global _service_up\n    if _service_up:\n        print(\"Calling external service...\")\n        # Simulate a network error or service unavailability\n        if requests.get(\"http://localhost:9999/health\", timeout=0.1).status_code != 200:\n            raise requests.exceptions.ConnectionError(\"Service not reachable\")\n        return \"Service data\"\n    else:\n        print(\"Service is intentionally down (simulated).\")\n        raise requests.exceptions.ConnectionError(\"Service is down\")\n\n# Create a circuit breaker instance\n# Opens after 3 failures within 60 seconds\n# Stays open for 10 seconds before trying again (half-open)\nmy_breaker = pybreaker.CircuitBreaker(\n    fail_max=3,\n    reset_timeout=10,\n    exclude=[requests.exceptions.HTTPError] # Exclude HTTP errors that are not system failures\n)\n\n@my_breaker\ndef protected_call():\n    return external_service_call()\n\nprint(\"--- Starting Circuit Breaker Demo ---\")\n\nfor i in range(10):\n    print(f\"\\nAttempt {i+1}:\")\n    try:\n        result = protected_call()\n        print(f\"Success: {result}\")\n        _service_up = True # Reset service state on success to show circuit closing\n    except pybreaker.CircuitBreakerError:\n        print(\"Circuit OPEN! Falling back to cached data or default.\")\n        _service_up = False # Keep service down if breaker opened\n    except requests.exceptions.ConnectionError as e:\n        print(f\"Connection error: {e}. Circuit state: {my_breaker.current_state}\")\n        # Simulate service coming back up after a few failures\n        if i == 5: # Make service available after 5 attempts\n            _service_up = True\n    except Exception as e:\n        print(f\"Unexpected error: {e}\")\n\n# A real application would not typically control _service_up like this in quickstart,\n# but it illustrates the breaker's behavior.","lang":"python","description":"This quickstart demonstrates how to apply a circuit breaker to protect calls to an unreliable external service using `pybreaker.CircuitBreaker` as a decorator. It simulates service failures and shows how the circuit transitions between `CLOSED`, `OPEN`, and `HALF_OPEN` states, with appropriate error handling and a fallback mechanism."},"warnings":[{"fix":"Upgrade your Python environment to 3.9 or newer.","message":"Python 3.8 support was dropped in PyBreaker v1.3.0, and Python 3.7 support was dropped in v1.2.0. Users on these older Python versions must upgrade to Python 3.9+ to use newer PyBreaker versions.","severity":"breaking","affected_versions":">=1.2.0 (for Python 3.7), >=1.3.0 (for Python 3.8)"},{"fix":"Ensure all datetime operations use timezone-aware objects, preferably `datetime.datetime.now(datetime.timezone.utc)` or `datetime.datetime.now(UTC)` for current UTC time.","message":"PyBreaker v1.3.0 migrated from `datetime.datetime.utcnow()` to `datetime.datetime.now(UTC)` due to `utcnow()` being deprecated in Python 3.12. While `pybreaker` itself handles this, users with custom listeners or state management using `utcnow()` might encounter `DeprecationWarning`s or unexpected behavior with naive datetimes in newer Python versions.","severity":"deprecated","affected_versions":">=1.3.0"},{"fix":"Remove `decode_responses=True` from your Redis client initialization when passing it to `CircuitRedisStorage`.","message":"When using `CircuitRedisStorage`, do NOT initialize the `redis.StrictRedis` (or `redis.Redis`) connection with `decode_responses=True`. This will cause `AttributeError: 'str' object has no attribute 'decode'` in Python 3+ when `pybreaker` attempts to decode state values.","severity":"gotcha","affected_versions":"All versions using `CircuitRedisStorage`"},{"fix":"Use `CircuitRedisStorage` (or another shared storage implementation) and ensure a unique `namespace` is provided for each distinct circuit breaker if multiple are used with the same Redis instance. Example: `state_storage=pybreaker.CircuitRedisStorage(pybreaker.STATE_CLOSED, redis_client, namespace='my_unique_breaker_name')`.","message":"For distributed applications with multiple instances of your service, a `CircuitBreaker` instance must use shared state storage (e.g., `CircuitRedisStorage`) to ensure all instances observe the same circuit state. Without shared storage, each instance will manage its own circuit independently, defeating the purpose of a distributed circuit breaker.","severity":"gotcha","affected_versions":"All versions in distributed environments"},{"fix":"Carefully tune circuit breaker parameters based on the expected reliability of the external service and your application's tolerance for failures. Monitor circuit state and logs to refine these thresholds. Implement meaningful fallbacks when the circuit is open.","message":"Incorrectly configuring `fail_max`, `reset_timeout`, or `success_threshold` can lead to circuits tripping too easily (false positives) or failing to open quickly enough, exacerbating cascading failures.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-06T00:00:00.000Z","next_check":"2026-07-05T00:00:00.000Z"}