Encapsulating retries into `with` block Encapsulating retries into `with` block python python

Encapsulating retries into `with` block


Is it possible to repeat the code within a with statement?

No.

As pointed out earlier in that mailing list thread, you can reduce a bit of duplication by making the decorator call the passed function:

def do_work():    ...    # This is not ideal!    @transaction(retries=3)    def _perform_in_transaction():        # Atomic DB statements        ...    # called implicitly    ...


The way that occurs to me to do this is just to implement a standard database transaction context manager, but allow it to take a retries argument in the constructor. Then I'd just wrap that up in your method implementations. Something like this:

class transaction(object):    def __init__(self, retries=0):        self.retries = retries    def __enter__(self):        return self    def __exit__(self, exc_type, exc_val, traceback):        pass    # Implementation...    def execute(self, query):        err = None        for _ in range(self.retries):            try:                return self._cursor.execute(query)            except Exception as e:                err = e # probably ought to save all errors, but hey        raise errwith transaction(retries=3) as cursor:    cursor.execute('BLAH')


As decorators are just functions themselves, you could do the following:

with transaction(_perform_in_transaction, retries=3) as _perf:    _perf()

For the details, you'd need to implement transaction() as a factory method that returns an object with __callable__() set to call the original method and repeat it up to retries number of times on failure; __enter__() and __exit__() would be defined as normal for database transaction context managers.

You could alternatively set up transaction() such that it itself executes the passed method up to retries number of times, which would probably require about the same amount of work as implementing the context manager but would mean actual usage would be reduced to just transaction(_perform_in_transaction, retries=3) (which is, in fact, equivalent to the decorator example delnan provided).