Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a generic repeat, try, except and raise in python that can wrap arbitrary code blocks

Tags:

python

I have a code that tries a block of code, if error happens, retry up to a max count.

The structure is re-used many times.

Something looks like this:


a = 10
b = 20
c = 30

result = None

max_loop = 3
interval = 1

for i in range(max_loop):
    try:
        # these are generic statements
        statement_1(a)
        statement_2(b)
        # ...
        result = statement_3(c)        
        break
    except Exception as e:
        logging.error(f'retry {i}')
        logging.error(e)
        time.sleep(interval)
raise Exception(f'Failed after {max_loop} retries')

Is there a way to create a wrap/decorator/contextmanager, etc of the for: try: ... except:raise so I can reuse the structure? Something similar to in-line block, or anonymous function in other languages?

I cannot create a function because the try: block can contain any statements, and use any variables. Ideally, the structure should be able to take arbitrary code block.

example:


repeat_try_and_raise:
    statement_1(a)
    statement_2(b)
    # ...
    result = statement_3(c) 

# ...

repeat_try_and_raise:
    statement_4(a)
    statement_5(b)
    # ...
    statement_6(c)

EDIT

Reasons I don't want to use a function to wrap the statements

  1. I am lazy, don't want to create a function for a single use every time I reuse the work flow.
  2. The function will have its own scope which will make accessing variables in my code not straightforward
like image 377
Jingshao Chen Avatar asked Sep 05 '26 13:09

Jingshao Chen


1 Answers

Here's an idea with a context manager:

a = 10
result = None
max_loop = 2
interval = 0.1

for attempt in repeat_try_and_raise(max_loop, interval):
    with attempt:
        a += 1
        result = 0/0 if a < 13 else 42

print(f'Success with {result=}')

Output for max_loop = 2:

ERROR:root:retry 0
ERROR:root:division by zero
ERROR:root:retry 1
ERROR:root:division by zero
Traceback (most recent call last):
  File ".code.tio", line 25, in <module>
    for attempt in repeat_try_and_raise(max_loop, interval):
  File ".code.tio", line 18, in repeat_try_and_raise
    raise Exception(f'Failed after {max_loop} retries')
Exception: Failed after 2 retries

Output for max_loop = 3:

ERROR:root:retry 0
ERROR:root:division by zero
ERROR:root:retry 1
ERROR:root:division by zero
Success with result=42

Full code (Try it online!):

import logging, time

def repeat_try_and_raise(max_loop, interval):
    class Attempt:
        def __enter__(self):
            pass
        def __exit__(self, exc_type, exc_value, traceback):
            self.e = exc_value
            return True
    for i in range(max_loop):
        attempt = Attempt()
        yield attempt
        if attempt.e is None:
            return
        logging.error(f'retry {i}')
        logging.error(attempt.e)
        time.sleep(interval)
    raise Exception(f'Failed after {max_loop} retries')

a = 10
result = None
max_loop = 3
interval = 0.1

for attempt in repeat_try_and_raise(max_loop, interval):
    with attempt:
        a += 1
        result = 0/0 if a < 13 else 42

print(f'Success with {result=}')
like image 107
Kelly Bundy Avatar answered Sep 07 '26 04:09

Kelly Bundy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!