2013-05-07 2 views
57

Существует ли стандартный способ использования цепочек исключений в Python? Как исключение Java «вызвано»?Цепочки исключений Python

Вот некоторые фон.

У меня есть модуль с одним основным классом исключений DSError:

class DSError(Exception): 
    pass 

Где-то в этом модуле будет:

try: 
    v = my_dict[k] 
    something(v) 
except KeyError as e: 
    raise DSError("no key %s found for %s" % (k, self)) 
except ValueError as e: 
    raise DSError("Bad Value %s found for %s" % (v, self)) 
except DSError as e: 
    raise DSError("%s raised in %s" % (e, self)) 

В принципе этот фрагмент должен бросить только DSError и скажите мне, что случилось и Зачем. Дело в том, что блок может попытаться бросить много других исключений, так что я бы предпочел, если я могу сделать что-то вроде:

try: 
    v = my_dict[k] 
    something(v) 
except Exception as e: 
    raise DSError(self, v, e) # Exception chained... 

Это стандартный вещий путь? Я не видел цепочки исключений в других модулях, так как это делается на Python?

+2

Woah, Спасибо @jamylak! Вы исправили мои кавычки, прежде чем я их увидел :-) – Ayman

+0

Да, и мне пришлось добавить избыточное слово, потому что SO не позволяло мне делать эти * небольшие изменения * :( – jamylak

+0

Что вы хотите, чтобы результат был? t сказать, действительно ли вы хотите трассировку стека исходного исключения или хотите скрыть его и просто иметь собственное исключение с единственным сообщением, которое суммирует исходное исключение? – BrenBarn

ответ

81

Exception chaining доступна только в Python 3, где вы можете написать:

try: 
    v = {}['a'] 
except KeyError as e: 
    raise ValueError('failed') from e 

что дает выход как

Traceback (most recent call last): 
    File "t.py", line 2, in <module> 
    v = {}['a'] 
KeyError: 'a' 

The above exception was the direct cause of the following exception: 

Traceback (most recent call last): 
    File "t.py", line 4, in <module> 
    raise ValueError('failed') from e 
ValueError: failed 

В большинстве случаев вам даже не нужен from; Python 3 будет по умолчанию показать все исключения, произошедших во время обработки исключений, как это:

Traceback (most recent call last): 
    File "t.py", line 2, in <module> 
    v = {}['a'] 
KeyError: 'a' 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
    File "t.py", line 4, in <module> 
    raise ValueError('failed') 
ValueError: failed 

Что вы можете сделать в Python 2 является добавлением пользовательских атрибутов к классу исключений, как:

class MyError(Exception): 
    def __init__(self, message, cause): 
     super(MyError, self).__init__(message + u', caused by ' + repr(cause)) 
     self.cause = cause 

try: 
    v = {}['a'] 
except KeyError as e: 
    raise MyError('failed', e) 
+2

Для python 2, если вы хотите сохранить трассировку - какой-то _must_ хотите - ['raise MyError (сообщение + u ', вызванное' + repr (причина)), None, sys.exc_info() [2]'] (http://stackoverflow.com/a/1350981/281545) –

+1

«В большинстве случаев вам даже не нужно от« У вас есть пример, где это необходимо или полезно? – timgeb

+2

@timgeb [PEP 3134] (https://www.python.org/dev/peps/pep-3134/) имеет две ситуации для цепочки: одна, где код обработки ошибок приводит к тому, что возникает другое исключение, а другое, где исключение было преднамеренно переведено в другое исключение. «From e» предназначен для преднамеренного случая и изменяет сообщение на выходе, как показано в ответе выше. –

4

Это то, о чем вы просите?

class MyError(Exception): 
    def __init__(self, other): 
     super(MyError, self).__init__(other.message) 

>>> try: 
...  1/0 
... except Exception, e: 
...  raise MyError(e) 
Traceback (most recent call last): 
    File "<pyshell#27>", line 4, in <module> 
    raise MyError(e) 
MyError: division by zero 

Если вы хотите сохранить исходный объект исключения, можно, конечно, сделать это в своем собственном классе исключения __init__-х. Вы могли бы на самом деле хотите сохранить отслеживающую как сам объект исключения не дает много полезной информации о том, где произошло исключение:

class MyError(Exception): 
    def __init__(self, other): 
     self.traceback = sys.exc_info() 
     super(MyError, self).__init__(other.message) 

После этого вы можете получить доступ к атрибуту вашего исключения traceback, чтобы получить информацию о оригинале исключение. (Python 3 уже обеспечивает это как атрибут объекта исключения __traceback__.)

+0

Почти правильно, но я считал, что это будет «обманывать», поскольку оно принимает только сообщение о цепном исключении, а не о конкретном объекте исключения. Т.е. я не знаю, где произошло фактическое деление на ноль, просто, что он был пойман _somewhere_. – Ayman

+0

@Ayman: См. мой отредактированный ответ.Все, что вам нужно сделать, это захватить трассировку и сохранить ее. Однако, если вы действительно хотите, чтобы вся информация из оригинального исключения отображалась в трассировке как реальное исключение, тогда phihag прав, что это невозможно сделать на Python 2. Вам нужно просто вручную распечатать старую трассировку как часть сообщения вашего исключения. – BrenBarn

+0

Спасибо. Я не знал о sys.exc_info(). Я бы принял это как ответ тоже :-) – Ayman

Смежные вопросы