Python "поднять от" использования

В чем разница между raise а также raise from в питоне?

try:
    raise ValueError
except Exception as e:
    raise IndexError

который дает

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

а также

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

который дает

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

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

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

4 ответа

Решение

Разница в том, что когда вы используете from, __cause__ атрибут установлен, и в сообщении указывается, что исключение было вызвано напрямую. Если вы опустите from Тогда нет __cause__ установлен, но __context__ Атрибут также может быть установлен, и трассировка затем показывает контекст, как во время обработки чего-то еще произошло.

Настройка __context__ произойдет, если вы использовали raise в обработчике исключений; если вы использовали raise нигде нет __context__ установлен либо.

Если __cause__ установлен, __suppress_context__ = True флаг также установлен на исключение; когда __suppress_context__ установлен в True, __context__ игнорируется при печати трассировки.

При вызове из обработчика исключений, когда вы не хотите показывать контекст (не хотите, чтобы во время обработки другого сообщения об исключении произошло), используйте raise ... from None установить __suppress_context__ в True,

Другими словами, Python устанавливает контекст для исключений, чтобы вы могли проанализировать, где возникло исключение, позволяя вам увидеть, было ли заменено другое исключение. Вы также можете добавить причину в исключение, сделав трассировку явной для другого исключения (используйте другую формулировку), и контекст игнорируется (но все же может подвергаться самоанализу при отладке). С помощью raise ... from None позволяет подавить контекст печати.

Увидеть raise документация заявления:

from Предложение используется для цепочки исключений: если дано, второе выражение должно быть другим классом или экземпляром исключения, который затем будет присоединен к возбужденному исключению как __cause__ атрибут (который доступен для записи). Если возникшее исключение не обработано, оба исключения будут напечатаны:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

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

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Подобный механизм работает неявно, если исключение вызывается внутри обработчика исключения: > предыдущее исключение затем присоединяется как новое исключение __context__ атрибут:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Также см. Документацию " Встроенные исключения" для получения подробной информации о контексте и сведениях о причинах, прикрепленных к исключениям.

PEP 3134, цепочка исключений и встроенная обратная трассировка представили цепочку исключений ( неявная цепочка с явным raise EXCEPTIONили неявный подъем, и явно связанный с явным raise EXCEPTION from CAUSE). Вот соответствующие параграфы, чтобы понять их использование:

Мотивация

Во время обработки одного исключения (исключения A) может возникнуть другое исключение (исключение B). В современном Python (версия 2.4), если это происходит, исключение B распространяется наружу, а исключение A теряется. Для отладки проблемы полезно знать об обоих исключениях. Атрибут сохраняет эту информацию автоматически.

Иногда обработчику исключений может быть полезно намеренно повторно вызвать исключение либо для предоставления дополнительной информации, либо для преобразования исключения в другой тип. Атрибут предоставляет явный способ записи непосредственной причины исключения.

[…]

Неявная цепочка исключений

Вот пример, иллюстрирующий атрибут:

       def compute(a, b):
    try:
        a/b
    except Exception, exc:
        log(exc)

def log(exc):
    file = open('logfile.txt')  # oops, forgot the 'w'
    print >>file, exc
    file.close()

Вызов compute(0, 0)вызывает а. Функция перехватывает это исключение и вызывает log(exc), но log()Функция также вызывает исключение при попытке записи в файл, который не был открыт для записи.

В современном Python вызывающая сторона compute()получает брошенный . Потерян. С предлагаемым изменением экземпляр имеет дополнительный атрибут, который сохраняет ZeroDivisionError.

[…]

Явная цепочка исключений

Атрибут объектов исключений всегда инициализируется None. Он устанавливается новой формой raiseутверждение:

       raise EXCEPTION from CAUSE

что эквивалентно:

       exc = EXCEPTION
exc.__cause__ = CAUSE
raise exc

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

       class DatabaseError(Exception):
    pass

class FileDatabase(Database):
    def __init__(self, filename):
        try:
            self.file = open(filename)
        except IOError, exc:
            raise DatabaseError('failed to open') from exc

Если вызов к open()вызывает исключение, о проблеме будет сообщено как DatabaseError, с атрибутом, раскрывающим IOErrorкак первопричина.

Расширенная отчетность

Обработчик исключений по умолчанию будет изменен, чтобы сообщать о связанных исключениях. Цепочка исключений проходится по атрибутам и с приоритетом. В соответствии с хронологическим порядком трассировки самое последнее возникшее исключение отображается последним; то есть отображение начинается с описания самого внутреннего исключения и повторяет цепочку до самого внешнего исключения. Трассировки отформатированы как обычно, с одной из строк:

Вышеупомянутое исключение было прямой причиной следующего исключения:

или же

Во время обработки вышеупомянутого исключения произошло другое исключение:

между обратными трассировками, в зависимости от того, связаны ли они __context__соответственно. Вот набросок процедуры:

       def print_chain(exc):
    if exc.__cause__:
        print_chain(exc.__cause__)
        print '\nThe above exception was the direct cause...'
    elif exc.__context__:
        print_chain(exc.__context__)
        print '\nDuring handling of the above exception, ...'
    print_exc(exc)

[…]

PEP 415, Реализовать подавление контекста с помощью атрибутов исключений, затем было введено подавление контекстов исключений (с явным raise EXCEPTION from None). Вот соответствующий абзац, чтобы понять его использование:

Предложение

Новый атрибут на BaseException, , будут введены. Всякий раз, когда установлено, будет установлено значение . В частности, синтаксис будет установлен на True. Код печати исключения проверит этот атрибут, чтобы определить, будут ли напечатаны контекст и причина. __cause__вернется к своей первоначальной цели и ценностям.

Существует приоритет для __suppress_context__с print_line_and_fileатрибут исключения.

Обобщить, raise exc from causeбудет эквивалентно:

       exc.__cause__ = cause
raise exc

куда exc.__cause__ = causeнеявно устанавливает exc.__suppress_context__.

Таким образом, в PEP 415 набросок процедуры, приведенный в PEP 3134, становится следующим:

      def print_chain(exc):
    if exc.__cause__:
        print_chain(exc.__cause__)
        print '\nThe above exception was the direct cause...'
    elif exc.__context__ and not exc.__suppress_context__:
        print_chain(exc.__context__)
        print '\nDuring handling of the above exception, ...'
    print_exc(exc)

Самый короткий ответ. PEP-3134 говорит сам за себя.raise Exception from eустанавливает поле нового исключения.

Более длинный ответ от того же PEP:

  • __context__поле будет неявно установлено исходной ошибкой внутриexcept:блокировать, если не сказано не с__suppress_context__ = True.
  • __cause__точно так же, как контекст, но должен быть установлен явно с помощьюfromсинтаксис
  • tracebackвсегда будет цепочка, когда вы звонитеraiseвнутриexceptблокировать. Вы можете избавиться от трассировки, а) проглотив исключениеexcept: passили возиться сsys.exc_info()напрямую.

Длинный ответ

      import traceback 
import sys

class CustomError(Exception):
    def __init__(self):
        super().__init__("custom")

def print_exception(func):
    print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
    try:
        func()
    except Exception as e:
        "Here is result of our actions:"
        print(f"\tException type:    '{type(e)}'")
        print(f"\tException message: '{e}'")
        print(f"\tException context: '{e.__context__}'")
        print(f"\tContext type:      '{type(e.__context__)}'")
        print(f"\tException cause:   '{e.__cause__}'")
        print(f"\tCause type:         '{type(e.__cause__)}'")
        print("\nTRACEBACKSTART>>>")
        traceback.print_exc()
        print("<<<TRACEBACKEND")


def original_error_emitter():
    x = {}
    print(x.does_not_exist)

def vanilla_catch_swallow():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        pass

def vanilla_catch_reraise():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise e

def catch_replace():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError()

def catch_replace_with_from():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError() from e

def catch_reset_trace():
    saw_an_error = False
    try:
        original_error_emitter()
    except Exception as e:
        saw_an_error = True
    if saw_an_error:
        raise CustomError()

print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)

Приведет к следующему результату:

      Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow' 




Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise' 

        Exception type:    '<class 'AttributeError'>'
        Exception message: ''dict' object has no attribute 'does_not_exist''
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
    raise e
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND



Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND



Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   ''dict' object has no attribute 'does_not_exist''
        Cause type:         '<class 'AttributeError'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

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

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
    raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND



Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace' 

        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'

TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND

У меня была аналогичная ошибка с использованием pylint. Если причины нет, чтобы подавить эту ошибку, просто выполните следующие действия.

      raise Exception from None 

или просто

      raise KeyError() from NameError()
Другие вопросы по тегам