예외에 정보를 추가 하시겠습니까?


142

나는 이와 같은 것을 성취하고 싶다 :

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')

이것을 달성하는 방법에 대한 단서가 있습니까? 파이썬 2와 3에서 어떻게해야합니까?


Exception message속성에 대한 문서를 찾는 동안 이 SO 질문 인 BaseException.message는 Python 2.6에서 더 이상 사용되지 않으므로 사용이 권장되지 않는 것으로 보입니다 (그리고 왜 문서에 없는지).
martineau

슬프게도 그 링크는 더 이상 작동하지 않는 것 같습니다.
Michael Scott Cuthbert

1
@MichaelScottCuthbert 여기에 좋은 대안이 있습니다 : itmaybeahack.com/book/python-2.6/html/p02/…
Niels Keurentjes

다음 은 메시지 속성의 상태와 args 속성 및 PEP 352 와의 관계에 대한 좋은 설명 입니다 . Steven F. Lott 의 무료 책 Building Skills in Python에서 발췌 한 것입니다.
martineau

답변:


118

이렇게하면 유형을 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')

Traceback (most recent call last):
  File "test.py", line 13, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    raise type(e)(e.message + ' happens at %s' % arg1)
IOError: Stuff happens at 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')

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    bar('arg1')
  File "test.py", line 11, in bar
    foo()
  File "test.py", line 5, in foo
    raise IOError('Stuff')
IOError: Stuff happens at arg1

업데이트 2

Python 3.x의 경우 첫 번째 업데이트의 코드가 구문 상 올바르지 않으며 2012-05-16의 PEP 352로 변경되어message 속성 이 있다는 아이디어 BaseException취소되었습니다 (첫 번째 업데이트는 2012-03-12에 게시 됨) . 따라서 현재 Python 3.5.2에서는 트레이스 백을 유지하고 function의 예외 유형을 하드 코딩하지 않기 위해 이러한 행을 따라 무언가를 수행해야합니다 bar(). 또한 다음 줄이 있습니다.

During handling of the above exception, another exception occurred:

표시된 추적 메시지에

# 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

답 인해 구문의 차이에 "아니오"로 보일 수도 있지만 파이썬 2와 3 모두에서 작동 할 수있는 방법이 있었다 경우 주석이 물었다가 있다 같은 도우미 함수를 사용하여 그 주위에 방법 reraise()six애드온은 모듈에. 따라서 어떤 이유로 라이브러리를 사용하지 않으려면 아래는 단순화 된 독립형 버전입니다.

또한 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')

3
그것은 기존의 예외에 정보를 추가하는 지점을 물리 치는 일종의 역 추적을 잃습니다. 또한> 1 인수를 사용하는 ctor에서는 예외가 작동하지 않습니다 (유형은 예외를 잡는 위치에서 제어 할 수없는 것입니다).
바츨라프 슬 라비크

1
@ Václav : 내가 추가 한 업데이트에서 볼 수 있듯이 백 트레이스가 손실되는 것을 방지하는 것은 매우 쉽습니다. 이것은 여전히 ​​모든 가능한 예외를 처리하지는 않지만 OP의 질문에 표시된 것과 유사한 경우에는 작동합니다.
마르티노

1
이것은 아니다 아주 좋아. type (e)이 재정의 __str__하면 원하지 않는 결과가 발생할 수 있습니다. 또한 두 번째 인수는 첫 번째 인수가 제공 한 생성자에 전달되므로 다소 의미가 없습니다 type(e)(type(e)(e.message). 셋째, e.message는 e.args [0]를 위해 사용되지 않습니다 .
bukzor

1
그렇다면 파이썬 2와 3 모두에서 작동하는 이식 가능한 방법이 없습니까?
Elias Dorneles

1
@martineau except 블록 안에서 가져 오기의 목적은 무엇입니까? 필요할 때만 가져 와서 메모리를 절약 할 수 있습니까?
AllTradesJack

115

Python 3에 대한 솔루션을 찾고 여기에 온 경우 설명서에 다음과 같이 나와 있습니다.

베어 raise를 사용하여 현재 처리중인 예외를 다시 발생시키는 대신 새 예외를 발생 시킬 때 raise에서 with를 사용하여 명시 적 원인으로 암시 적 예외 컨텍스트를 보완 할 수 있습니다.

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

결국 다음과 같이 보입니다.

2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/
Traceback (most recent call last):
File "venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions
    return [permission() for permission in self.permission_classes]
TypeError: 'type' object is not iterable 

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

Traceback (most recent call last):
    # Traceback removed...
TypeError: Make sure your view's Permission_classes are iterable. If 
     you use parens () to generate a set with a single element make 
     sure that there is a (comma,) behind the one element.

TypeError원래 예외를 엉망으로 만들지 않고 솔루션에 대한 힌트와 함께 완전히 설명이없는 멋진 메시지로 전환합니다.


14
결과 예외가 원래 원인으로 돌아 가기 때문에 더 자세한 정보를 제공하기 때문에 이것이 최상의 솔루션입니다.
JT

메시지를 추가 할 수는 있지만 여전히 새로운 예외를 제기하지 않는 솔루션이 있습니까? 예외 인스턴스의 메시지를 확장한다는 의미입니다.
edcSam

Yaa ~~ 그것은 효과가 있지만 나에게해서는 안되는 느낌입니다. 메시지는에 저장되어 e.args있지만 튜플이므로 변경할 수 없습니다. 따라서 먼저 args목록에 복사 한 다음 수정 한 다음 다시 Tuple로 복사하십시오.args = list(e.args) args[0] = 'bar' e.args = tuple(args)
Chris

27

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가 트릭을 수행합니다.


그러나이 경우 예외가 foo에서 변경되면 bar도 변경해야합니다.?
anijhaw

1
예외를 포착하면 (위에서 편집) 표준 라이브러리 예외 (예외에서 상속하고 Exception .__ init__를 호출하는 예외)를 포착 할 수 있습니다.
Steve Howard

6
보다 완전 / 협동적일 수 있도록, 원래 튜플의 다른 부분을 포함하십시오 :e.args = ('mynewstr' + e.args[0],) + e.args[1:]
Dubslow

1
@ nmz787 이것은 실제로 Python 3에 가장 적합한 솔루션입니다. 정확히 오류가 무엇입니까?
Christian

1
@Dubslow와 martineau 나는 당신의 제안을 편집에 통합했습니다.
기독교

9

나는 지금까지 주어진 답변을 모두 좋아하지 않습니다. 그들은 여전히 ​​너무 장황하다. 코드 및 메시지 출력

내가 갖고 싶은 것은 소스 예외를 가리키는 스택 트레이스, 사이에 예외가 없으므로 새로운 예외를 만들지 않고 모든 관련 스택 프레임 상태로 원본을 다시 올리는 것입니다.

Steve Howard 는 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)))

5

내가 사용한 편리한 방법 중 하나는 클래스 속성을 클래스 객체와 클래스 인스턴스 모두에서 액세스 할 수 있으므로 클래스 속성을 세부 정보 저장소로 사용하는 것입니다.

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)

OP가 원래 예외가 발생하고 잡히지 않을 때 스택 추적의 일부로 세부 정보를 인쇄하도록 요청하므로 실제로 유용하지 않습니다.
cowbert

나는 해결책이 좋다고 생각한다. 그러나 설명은 사실이 아닙니다. 인스턴스화 할 때 클래스 속성이 인스턴스에 복사됩니다. 따라서 인스턴스의 "details"속성을 수정해도 클래스 속성은 여전히 ​​None입니다. 어쨌든 우리는 여기서이 동작을 원합니다.
Adam Wallner

2

이전 답변과 달리 이것은 실제로 나쁜 예외로 인해 작동합니다 __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!

2

예외에 추가 정보를 추가 할 때마다 자주 사용하는 코드를 제공합니다. 파이썬 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


나는 이것이 질문에 제공된 예와 약간 다르다는 것을 알고 있지만 그럼에도 불구하고 누군가가 유용하다고 생각하기를 바랍니다.


1

다른 예외를 상속하는 자체 예외를 정의하고 자체 생성자를 만들어 값을 설정할 수 있습니다.

예를 들면 다음과 같습니다.

class MyError(Exception):
   def __init__(self, value):
     self.value = value
     Exception.__init__(self)

   def __str__(self):
     return repr(self.value)

2
message원래 예외의 내용을 변경 / 추가 할 필요는 없습니다 (그러나 고칠 수 있다고 생각합니다).
martineau

-6

아마도

except Exception as e:
    raise IOError(e.message + 'happens at %s'%arg1)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.