Цепочки исключений Python

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

Вот какой фон.

У меня есть модуль с одним основным классом исключений 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 может вызывать множество других исключений, поэтому я бы предпочел, чтобы сделать что-то вроде:

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

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

Ответ 1

Цепочка исключений доступна только в 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

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

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__ объекта исключения.)