Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"outsourcing" exception-handling to a decorator [closed]

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?

like image 633
Don Question Avatar asked Mar 10 '12 14:03

Don Question


2 Answers

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 
like image 153
Ethan Furman Avatar answered Oct 04 '22 23:10

Ethan Furman


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.

like image 43
Karl Knechtel Avatar answered Oct 04 '22 23:10

Karl Knechtel