Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions src/pybreaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class CircuitBreaker(object):
"""

def __init__(self, fail_max=5, reset_timeout=60, exclude=None,
listeners=None, state_storage=None, name=None):
listeners=None, state_storage=None, name=None,
fallback_method=None):
"""
Creates a new circuit breaker with the given parameters.
"""
Expand All @@ -64,6 +65,22 @@ def __init__(self, fail_max=5, reset_timeout=60, exclude=None,
self._excluded_exceptions = list(exclude or [])
self._listeners = list(listeners or [])
self._name = name
self._fallback_method = fallback_method

@property
def fallback_method(self):
"""
Return the current fallback method
"""
return self._fallback_method

@fallback_method.setter
def fallback_method(self, func):
"""
Sets the fallback function that will be called when the circuit is open
:param func:
"""
self._fallback_method = func

@property
def fail_counter(self):
Expand Down Expand Up @@ -205,7 +222,15 @@ def call(self, func, *args, **kwargs):
implemented by the current state of this circuit breaker.
"""
with self._lock:
return self.state.call(func, *args, **kwargs)
try:
result = self.state.call(func, *args, **kwargs)
except CircuitBreakerError as e:
if self.fallback_method:
result = self.fallback_method(*args, **kwargs)
else:
raise e

return result

def call_async(self, func, *args, **kwargs):
"""
Expand All @@ -219,7 +244,21 @@ def wrapped():
with self._lock:
ret = yield self.state.call_async(func, *args, **kwargs)
raise gen.Return(ret)
return wrapped()

@gen.coroutine
def fallback_wrapped():
with self._lock:
ret = yield self.fallback_method(*args, **kwargs)
raise gen.Return(ret)
try:
result = wrapped()
except CircuitBreakerError as e:
if self.fallback_method:
result = fallback_wrapped()
else:
raise e

return result

def open(self):
"""
Expand Down Expand Up @@ -254,6 +293,7 @@ def __call__(self, *call_args, **call_kwargs):
which will will call `func` as a Tornado co-routine.
"""
call_async = call_kwargs.pop('__pybreaker_call_async', False)
self.fallback_method = call_kwargs.pop('__pybreaker_fallback', None)

if call_async and not HAS_TORNADO_SUPPORT:
raise ImportError('No module named tornado')
Expand Down