"outsourcing" exception-handling to a decorator [closed]
Asked Answered
P

3

53

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?

Premillennialism answered 10/3, 2012 at 14:1 Comment(5)
I'm looking at the answers, and I just thought...if you were to refactor many similar error handling tasks together as a function...wouldn't you just have created a starting point for a class method in a decorator anyway?Modish
I can't follow you, sorry. Do you mean, i should use the decorator approach and refactor existing code with a fresh created Exception-Decorator-Class, or do you mean i don't need a decorator, because i could use a per-class exception handling method?Premillennialism
What I meant was, if you've abstracted away the details of most error handling into a method, then you've done 80% of the work for abstracting that into a class, for use as a decorator. My opinion? It might botch up your work every once in a while, but for dealing with events outside general error handling tasks, you could always, you know, write in-line try:except blocks in the method.Modish
So you are in favor of a decorator-approach, as long as it addresses general exception-handling, like retrying to connect to a server several times before giving up?Premillennialism
Yeah. I really like how clear Ethan's answer made your approach seem. Pretty clever.Modish
M
28

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
Monophthong answered 10/3, 2012 at 15:11 Comment(0)
D
6

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.

Dearing answered 10/3, 2012 at 14:50 Comment(2)
Can't i address this coupling-issue of actor/reactor by using individual handler-mehtods? I could even define a classmethod in every affected class.Premillennialism
You'd need your decorator to be able to hook in a handler provided by the caller, and at that point you've just added complexity for probably no real gain (unless your program does an incredibly large amount of exception handling that usually boils down to a few common forms, and even then there's almost certainly a smarter refactoring).Dearing
P
-1
  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.

Plantagenet answered 10/3, 2012 at 15:0 Comment(3)
Context managers are definitely useful, but they don't handle the cases the OP is talking about: having the exception handling code bound with the functions without being in the function.Monophthong
Exactly! Ethan nailed it at once. I was also considering the context approach, but with decorators i have an even more general way to address redundancy.Premillennialism
I find decorators useful for exception translation - then you don't need so much contextAgonistic

© 2022 - 2024 — McMap. All rights reserved.