Я хочу добиться чего-то вроде этого:
def foo():
try:
raise IOError('Stuff ')
except:
raise
def bar(arg1):
try:
foo()
except Exception as e:
e.message = e.message + 'happens at %s' % arg1
raise
bar('arg1')
Traceback...
IOError('Stuff Happens at arg1')
Но вот что я получаю:
Traceback..
IOError('Stuff')
Есть какие-нибудь подсказки о том, как этого добиться? Как это сделать в Python 2 и 3?
message
атрибута Exception я обнаружил этот вопрос SO, BaseException.message устарел в Python 2.6 , что, похоже, указывает на то, что его использование теперь не рекомендуется (и почему его нет в документации).Ответы:
Я бы сделал это так, чтобы изменение его типа
foo()
не потребовало также его измененияbar()
.def foo(): try: raise IOError('Stuff') except: raise def bar(arg1): try: foo() except Exception as e: raise type(e)(e.message + ' happens at %s' % arg1) bar('arg1')
Обновление 1
Вот небольшая модификация, которая сохраняет исходную трассировку:
... def bar(arg1): try: foo() except Exception as e: import sys raise type(e), type(e)(e.message + ' happens at %s' % arg1), sys.exc_info()[2] bar('arg1')
Обновление 2
Для Python 3.x код в моем первом обновлении синтаксически неверен, плюс идея наличия
message
атрибутаBaseException
была отозвана при изменении PEP 352 16 мая 2012 г. (мое первое обновление было опубликовано 12 марта 2012 г.) . Итак, в настоящее время в Python 3.5.2 вам все равно нужно будет что-то сделать в этих строках, чтобы сохранить трассировку, а не жестко кодировать тип исключения в функцииbar()
. Также обратите внимание, что там будет строка:в отображаемых сообщениях трассировки.
# for Python 3.x ... def bar(arg1): try: foo() except Exception as e: import sys raise type(e)(str(e) + ' happens at %s' % arg1).with_traceback(sys.exc_info()[2]) bar('arg1')
Обновление 3
Комментатор спросил, есть ли способ , который будет работать как в Python 2 и 3. Несмотря на то, что ответ может показаться, что «Нет» из - за различия синтаксиса, то есть способ обойти это, используя вспомогательную функцию как
reraise()
вsix
Add- на модуле. Итак, если вы по какой-то причине не хотите использовать библиотеку, ниже представлена упрощенная автономная версия.Также обратите внимание на то, что, поскольку исключение повторно возникает внутри
reraise()
функции, оно будет появляться при любом возбуждении трассировки, но конечный результат - это то, что вы хотите.import sys if sys.version_info.major < 3: # Python 2? # Using exec avoids a SyntaxError in Python 3. exec("""def reraise(exc_type, exc_value, exc_traceback=None): raise exc_type, exc_value, exc_traceback""") else: def reraise(exc_type, exc_value, exc_traceback=None): if exc_value is None: exc_value = exc_type() if exc_value.__traceback__ is not exc_traceback: raise exc_value.with_traceback(exc_traceback) raise exc_value def foo(): try: raise IOError('Stuff') except: raise def bar(arg1): try: foo() except Exception as e: reraise(type(e), type(e)(str(e) + ' happens at %s' % arg1), sys.exc_info()[2]) bar('arg1')
источник
__str__
, вы можете получить нежелательные результаты. Также обратите внимание, что второй аргумент передается конструктору, заданному первым аргументом, что дает несколько бессмысленный результатtype(e)(type(e)(e.message)
. В-третьих, e.message устарел в пользу e.args [0].Если вы пришли сюда в поисках решения для Python 3, в руководстве говорится:
raise new_exc from original_exc
Пример:
try: return [permission() for permission in self.permission_classes] except TypeError as e: raise TypeError("Make sure your view's 'permission_classes' are iterable. " "If you use '()' to generate a set with a single element " "make sure that there is a comma behind the one (element,).") from e
Что в итоге выглядит так:
Превращение совершенно невзрачного сообщения
TypeError
в красивое сообщение с намеками на решение без нарушения исходного исключения.источник
e.args
, но это кортеж, поэтому его нельзя изменить. Итак, сначала скопируйтеargs
в список, затем измените его, а затем скопируйте обратно как кортеж:args = list(e.args)
args[0] = 'bar'
e.args = tuple(args)
Предполагая, что вы не хотите или не можете изменять foo (), вы можете сделать это:
try: raise IOError('stuff') except Exception as e: if len(e.args) >= 1: e.args = (e.args[0] + ' happens',) + e.args[1:] raise
Это действительно единственное решение, которое решает проблему в Python 3 без уродливого и запутанного сообщения «Во время обработки вышеуказанного исключения произошло другое исключение».
В случае, если необходимо добавить строку повторного повышения в трассировку стека, запись
raise e
вместоraise
этого сделает свое дело.источник
e.args = ('mynewstr' + e.args[0],) + e.args[1:]
Мне пока не нравятся все ответы. Они по-прежнему слишком многословны, имхо. В любом коде и выводе сообщения.
Все, что я хочу иметь, - это трассировка стека, указывающая на исходное исключение, без каких-либо исключений между ними, поэтому не нужно создавать новые исключения, просто повторно поднимая оригинал со всеми соответствующими состояниями фрейма стека в нем, что привело к этому.
Стив Ховард дал хороший ответ, который я хочу расширить, нет, уменьшить ... только до Python 3.
except Exception as e: e.args = ("Some failure state", *e.args) raise
Единственное новшество - это расширение / распаковка параметров, что делает его небольшим и достаточно простым в использовании.
Попытайся:
foo = None try: try: state = "bar" foo.append(state) except Exception as e: e.args = ("Appending '"+state+"' failed", *e.args) raise print(foo[0]) # would raise too except Exception as e: e.args = ("print(foo) failed: " + str(foo), *e.args) raise
Это даст вам:
Traceback (most recent call last): File "test.py", line 6, in <module> foo.append(state) AttributeError: ('print(foo) failed: None', "Appending 'bar' failed", "'NoneType' object has no attribute 'append'")
Простой симпатичный принт может быть чем-то вроде
print("\n".join( "-"*i+" "+j for i,j in enumerate(e.args)))
источник
Один удобный подход, который я использовал, - использовать атрибут класса в качестве хранилища деталей, поскольку атрибут класса доступен как из объекта класса, так и из экземпляра класса:
class CustomError(Exception): def __init__(self, details: Dict): self.details = details
Затем в вашем коде:
raise CustomError({'data': 5})
И при отлове ошибки:
except CustomError as e: # Do whatever you want with the exception instance print(e.details)
источник
Я предоставлю фрагмент кода, который я часто использую, когда хочу добавить дополнительную информацию к исключению. Я работаю на Python 2.7 и 3.6.
import sys import traceback try: a = 1 b = 1j # The line below raises an exception because # we cannot compare int to complex. m = max(a, b) except Exception as ex: # I create my informational message for debugging: msg = "a=%r, b=%r" % (a, b) # Gather the information from the original exception: exc_type, exc_value, exc_traceback = sys.exc_info() # Format the original exception for a nice printout: traceback_string = ''.join(traceback.format_exception( exc_type, exc_value, exc_traceback)) # Re-raise a new exception of the same class as the original one, # using my custom message and the original traceback: raise type(ex)("%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % (msg, traceback_string))
Приведенный выше код приводит к следующему результату:
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-6-09b74752c60d> in <module>() 14 raise type(ex)( 15 "%s\n\nORIGINAL TRACEBACK:\n\n%s\n" % ---> 16 (msg, traceback_string)) TypeError: a=1, b=1j ORIGINAL TRACEBACK: Traceback (most recent call last): File "<ipython-input-6-09b74752c60d>", line 7, in <module> m = max(a, b) # Cannot compare int to complex TypeError: no ordering relation is defined for complex numbers
Я знаю, что это немного отличается от примера, приведенного в вопросе, но тем не менее я надеюсь, что кто-то сочтет это полезным.
источник
В отличие от предыдущих ответов, это работает с очень плохими исключениями
__str__
. Однако он изменяет тип, чтобы исключить бесполезные__str__
реализации.Я все еще хотел бы найти дополнительное улучшение, которое не изменяет тип.
from contextlib import contextmanager @contextmanager def helpful_info(): try: yield except Exception as e: class CloneException(Exception): pass CloneException.__name__ = type(e).__name__ CloneException.__module___ = type(e).__module__ helpful_message = '%s\n\nhelpful info!' % e import sys raise CloneException, helpful_message, sys.exc_traceback class BadException(Exception): def __str__(self): return 'wat.' with helpful_info(): raise BadException('fooooo')
Исходная трассировка и тип (имя) сохраняются.
Traceback (most recent call last): File "re_raise.py", line 20, in <module> raise BadException('fooooo') File "/usr/lib64/python2.6/contextlib.py", line 34, in __exit__ self.gen.throw(type, value, traceback) File "re_raise.py", line 5, in helpful_info yield File "re_raise.py", line 20, in <module> raise BadException('fooooo') __main__.BadException: wat. helpful info!
источник
Вы можете определить собственное исключение, которое наследуется от другого, и создать собственный конструктор для установки значения.
Например:
class MyError(Exception): def __init__(self, value): self.value = value Exception.__init__(self) def __str__(self): return repr(self.value)
источник
message
исходному исключению (но, я думаю, это можно исправить).Может быть
except Exception as e: raise IOError(e.message + 'happens at %s'%arg1)
источник