We have function example_2 which we would like to run. Sometimes it raises an exception, sometimes it doesn’t.
Depending on the exception raised, I would like to log it(controlled exception) or not (uncontrolled exception). I would also like to retry the function example_2 a few times, only if the exceptions are controlled.
Here’s my try (based on the book ‘clean code in python’):
from typing import Optional
from collections.abc import Sequence
import logging
from functools import wraps
class ControlledException(Exception):
"""My own customized exception"""
class UncontrolledException(Exception):
"""An exception I don't like/control """
logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG)
logger = logging.getLogger('tcpserver')
_default_retries_limit = 3
def with_retry( retries_limit =_default_retries_limit, list_allowed_exceptions = None):
list_allowed_exceptions = list_allowed_exceptions or (ControlledException,)
def retry (operation):
@wraps(operation)
def wrapping_function(*args, **kwargs):
last_raised = None
for num in range(retries_limit):
print("Try number: {}".format(num+1))
try:
return operation(*args, **kwargs)
except list_allowed_exceptions as raised_exception:
logger.warning("returning %s due to %s",operation.__qualname__,raised_exception)
logger.warning('Protocol problem: %s', 'connection reset', extra=d)
last_raised = raised_exception
raise last_raised
return wrapping_function
return retry
@with_retry(retries_limit = 5, list_allowed_exceptions = ControlledException)
def run_operation(task):
print("Task result: {}".format(task))
def example_2(threshould_1,threshould_2):
"""random.random() draws from a uniform distribution"""
print('Called example function')
value = random()
if value < threshould_1:
print("value = ", value)
raise ControlledException
elif value > threshould_1 and value < threshould_2:
print("value = ", value)
raise UncontrolledException
return value
When I call run_operation(example_2(0.9,0.0)), and when ControlledException is raised, nothing in the except branch of the try command is run… why?
Edit:
If I do
@with_retry(...)
run_operation(...)
then I don’t go to the except. But if I do
@with_retry(...)
example_2(...)
then I go to the except. Now I don’t get why this should be different?
>Solution :
As per the comments:
Running:
run_operation(example_2(0.9,0.0))
is equivalent to:
my_result = example_2(0.9,0.0)
run_operation(my_result)
If run_operation is "protected", but example_2 can throw an exception (or… does throw an exception, rather), the "protection" provided by the decorator will never happen… Because run_operation will never be reached.
When Python sees this: run_operation(example_2(0.9,0.0)) it will call example_2(0.9,0.0) first (fully and completely) and theeeEEEeen it will (try) to pick up the result and call run_operation with whatever value was returned by the call to example_2.
In this case, much before trying to call run_operation, the call example_2(0.9,0.0) has already thrown an Exception.