Определить, были ли исключения уже обработаны во вложенных операторах with в Python 2.7

#python #python-2.7 #exception-handling #with-statement #contextmanager

#python #python-2.7 #исключение #with-statement #contextmanager

Вопрос:

Рассмотрим следующий фрагмент кода:

 class Test(object):
    def __enter__(self):
        pass
    def __exit__(self,type,value,trace):
        if type:
            print "Error occured: "   str(value.args)
            #if I changed the next line to 'return True', the
            #'print "should not happen"' statements are executed, but the
            #error information would be only printed once (what I want)
            return False
        return True

with Test():
    with Test():
        with Test():
            raise Exception('Foo','Bar')
        print "should not happen"
    print "should not happen"
  

Вывод примера:

Произошла ошибка: (‘Foo’, ‘Bar’)

Произошла ошибка: (‘Foo’, ‘Bar’)

Произошла ошибка: (‘Foo’, ‘Bar’)

У меня есть несколько вложенных with операторов, и я хочу обработать случай, когда исключение возникает где-то в коде. Чего я хочу добиться, так это того, чтобы выполнение было остановлено (в приведенном выше примере вывода «не должно произойти» нет), но информация об ошибке выводится только один раз. Поэтому мне нужно каким-то образом узнать, была ли уже обработана та же ошибка.

У вас есть какие-либо идеи, как этого можно достичь?

Комментарии:

1. mywiki. wooledge.org/XyProblem

Ответ №1:

На самом деле вы не можете сделать это чисто — либо диспетчер контекста проглатывает исключение, либо нет.

Если у вас все в порядке с исключением, распространяющимся из диспетчера (что должно быть так, если вы обрабатываете здесь произвольные исключения), вы можете вручную исправить экземпляр исключения:

 def __exit__(self, type, value, trace):
    if type:
        if not getattr(value, '_printed_it_already', False):
           print "error occured: "   str(value.args)
           value._printed_it_already = True
           return False
    return True
  

Обратите внимание, что такого рода исправление ошибок в Python не одобряется … Я думаю, стоит спросить, что вы на самом деле пытаетесь сделать. Обычно, когда необработанное исключение выводит свою трассировку стека, вы получаете довольно хорошее представление о том, с чего args должно было начинаться исключение…

Комментарии:

1. именно то, что я искал, спасибо! Я знаю, что это не очень хорошая практика. Я создал небольшой task runner, где каждый with представляет некоторый вид транзакции. with Синтаксис прост в использовании для этого, но моей единственной проблемой было вывести ошибку (и сделать это только один раз)

Ответ №2:

Вы можете добавить error_handled атрибут к исключению и протестировать его:

 class Test(object):
    def __enter__(self):
        pass
    def __exit__(self,type,value,trace):
        if type:
            if not getattr(value,'error_handled', False):
                value.error_handled = True
                print "Error occured: "   str(value.args)

with Test():
    with Test():
        with Test():
            raise Exception('Foo','Bar')
        print "should not happen"
    print "should not happen"