Как я могу поймать исключение в декораторе, но позволить вызывающему также поймать его? - PullRequest
15 голосов
/ 22 ноября 2010

У меня есть функция Python, которая может вызвать исключение. Звонящий перехватывает исключение и обрабатывает его. Теперь я хотел бы добавить к этой функции декоратор, который также перехватывает исключение, выполняет некоторую обработку, но затем повторно вызывает исключение, чтобы оригинальный обработчик мог его обработать. Это работает, за исключением того, что, когда исходный вызывающий абонент отображает стек вызовов из исключения, он показывает строку в декораторе, где он был повторно вызван, а не там, где он первоначально произошел. Пример кода:

import sys,traceback

def mydec(func):
    def dec():
        try:
            func()
        except Exception,e:
            print 'Decorator handled exception %s' % e
            raise e
    return dec

@mydec
def myfunc():
    x = 1/0

try:
    myfunc()
except Exception,e:
    print 'Exception: %s' % e
    type,value,tb = sys.exc_info()
    traceback.print_tb(tb)

Вывод:

Decorator handled exception integer division or modulo by zero
Exception: integer division or modulo by zero
  File "allbug.py", line 20, in <module>
    myfunc()
  File "allbug.py", line 9, in dec
    raise e

Я бы хотел, чтобы декоратор мог обрабатывать исключение, но трассировка должна указывать строку x = 1/0, а не raise. Как я могу это сделать?

Ответы [ 2 ]

18 голосов
/ 22 ноября 2010

Просто используйте raise; (т.е. не вызывайте ничего конкретного, просто raise;) в блоке catch, чтобы повторно вызвать исключение без «сброса» обратной трассировки.

8 голосов
/ 01 апреля 2011

Я только что написал класс, похожий на то, что вы делаете, но с немного большим количеством доступных опций. Вот оно:

class ErrorIgnore(object):
   def __init__(self, errors, errorreturn = None, errorcall = None):
      self.errors = errors
      self.errorreturn = errorreturn
      self.errorcall = errorcall

   def __call__(self, function):
      def returnfunction(*args, **kwargs):
         try:
            return function(*args, **kwargs)
         except Exception as E:
            if type(E) not in self.errors:
               raise E
            if self.errorcall is not None:
               self.errorcall(E, *args, **kwargs)
            return self.errorreturn
      return returnfunction

Обычное использование будет примерно таким:

def errorcall(E, *args):
   print 'exception skipped', E

@ErrorIgnore(errors = [ZeroDivisionError, ValueError], errorreturn = None, errorcall = errorcall)
def myfunction(stuff):
   # do stuff
   # return stuff
   # the errors shown are skipped
...