Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Creating a decorator for retries of a function

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading