Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danielfm/pybreaker/llms.txt

Use this file to discover all available pages before exploring further.

PyBreaker includes optional support for Tornado coroutines, allowing you to protect async integration points with the same circuit breaker semantics as synchronous code. When Tornado is installed, CircuitBreaker gains the ability to wrap @gen.coroutine functions and propagate circuit state correctly through the coroutine lifecycle.

Installation

pip install pybreaker tornado

Decorator with Async Support

To protect a Tornado coroutine with the decorator pattern, pass __pybreaker_call_async=True as a keyword argument when applying the breaker. This signals CircuitBreaker.__call__ to route the call through call_async() instead of the synchronous call() path.
from tornado import gen
import pybreaker

db_breaker = pybreaker.CircuitBreaker(fail_max=5, reset_timeout=60)

@db_breaker(__pybreaker_call_async=True)
@gen.coroutine
def async_update(cust):
    # Do async stuff here...
    pass
The __pybreaker_call_async=True flag is consumed by CircuitBreaker.__call__ and is not forwarded to the wrapped function. The decorator order matters: @db_breaker(...) must be applied above @gen.coroutine so that the circuit breaker wraps the coroutine factory, not the raw function.

Direct Async Call

For cases where you cannot or do not want to use the decorator, call cb.call_async() directly. Pass the coroutine function as the first argument, followed by any positional and keyword arguments to forward to it.
@gen.coroutine
def async_update(cust):
    # Do async stuff here...
    pass

# Call through the circuit breaker
updated_customer = db_breaker.call_async(async_update, my_customer)
Signature: CircuitBreaker.call_async(func, *args, **kwargs) call_async returns a Tornado Future. yield it inside another @gen.coroutine or use it with IOLoop.run_sync to get the result.

Error Handling

Error handling behaviour is identical to the synchronous case. When the circuit is open, call_async raises CircuitBreakerError immediately without attempting the call. When the circuit is closed or half-open, a failure inside the coroutine is counted against the failure threshold as usual.
from pybreaker import CircuitBreakerError
from tornado import gen

@gen.coroutine
def safe_update(cust):
    try:
        result = yield db_breaker.call_async(async_update, cust)
        raise gen.Return(result)
    except CircuitBreakerError:
        # Circuit is open — return a fallback or re-raise
        raise gen.Return(None)
Tornado support is optional. If Tornado is not installed, calling call_async() or decorating with __pybreaker_call_async=True will raise ImportError: No module named tornado. PyBreaker checks for Tornado at import time and exposes the HAS_TORNADO_SUPPORT boolean if you need to detect availability at runtime.
This Tornado integration uses the legacy @gen.coroutine pattern introduced in Tornado 3.x. For modern Python async/await (asyncio), you do not need this special integration — the standard call() method and the @db_breaker decorator work correctly with async def functions, because Python’s native coroutines raise exceptions normally when awaited.

Build docs developers (and LLMs) love