"outsourcing" exception-handling to a decorator

PythonException HandlingDecoratorPython 2.6

Python Problem Overview


Many try/except/finally-clauses not only "uglify" my code, but i find myself often using identical exception-handling for similar tasks. So i was considering reducing redundancy by "outsourcing" them to a ... decorator.

Because i was sure not to be the 1st one to come to this conclusion, I googled and found this - imho - ingenious recipe which added the possibility to handle more than one exception.

But i was surprised why this doesn't seem to be a wide known and used practice per se, so i was wondering if there is maybe an aspect i wasn't considering?

  1. Is it bogus to use the decorator pattern for exception-handling or did i just miss it the whole time? Please enlighten me! What are the pitfalls?

  2. Is there maybe even a package/module out there which supports the creation of such exception-handling in a reasonable way?

Python Solutions


Solution 1 - Python

The biggest reason to keep the try/except/finally blocks in the code itself is that error recovery is usually an integral part of the function.

For example, if we had our own int() function:

def MyInt(text):
    return int(text)

What should we do if text cannot be converted? Return 0? Return None?

If you have many simple cases then I can see a simple decorator being useful, but I think the recipe you linked to tries to do too much: it allows a different function to be activated for each possible exception--in cases such as those (several different exceptions, several different code paths) I would recommend a dedicated wrapper function.

Here's my take on a simple decorator approach:

class ConvertExceptions(object):

    func = None

    def __init__(self, exceptions, replacement=None):
        self.exceptions = exceptions
        self.replacement = replacement

    def __call__(self, *args, **kwargs):
        if self.func is None:
            self.func = args[0]
            return self
        try:
            return self.func(*args, **kwargs)
        except self.exceptions:
            return self.replacement

and sample usage:

@ConvertExceptions(ValueError, 0)
def my_int(value):
    return int(value)

print my_int('34')      # prints 34
print my_int('one')     # prints 0

Solution 2 - Python

Basically, the drawback is that you no longer get to decide how to handle the exception in the calling context (by just letting the exception propagate). In some cases this may result in a lack of separation of responsibility.

Solution 3 - Python

  1. Decorator in Python is not the same as the Decorator pattern, thought there is some similarity. It is not completely clear waht you mean here, but I think you mean the one from Python (thus, it is better not to use the word pattern)

  2. Decorators from Python are not that useful for exception handling, because you would need to pass some context to the decorator. That is, you would either pass a global context, or hide function definitions within some outer context, which requires, I would say, LISP-like way of thinking.

  3. Instead of decorators you can use contextmanagers. And I do use them for that purpose.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDon QuestionView Question on Stackoverflow
Solution 1 - PythonEthan FurmanView Answer on Stackoverflow
Solution 2 - PythonKarl KnechtelView Answer on Stackoverflow
Solution 3 - PythonnewtoverView Answer on Stackoverflow