파이썬에서 "내부 예외"(트레이스 백 포함)?


146

내 배경은 C #이며 최근에 Python으로 프로그래밍을 시작했습니다. 예외가 발생하면 일반적으로 전체 스택 추적을 표시하면서 더 많은 정보를 추가하는 다른 예외로 래핑하고 싶습니다. C #에서는 쉽지만 파이썬에서는 어떻게해야합니까?

예 : C #에서는 다음과 같은 작업을 수행합니다.

try
{
  ProcessFile(filePath);
}
catch (Exception ex)
{
  throw new ApplicationException("Failed to process file " + filePath, ex);
}

파이썬에서는 비슷한 것을 할 수 있습니다.

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file ' + filePath, e)

... 그러나 이것은 내부 예외의 역 추적을 잃습니다!

편집 : 예외 메시지와 스택 추적을 모두보고 싶습니다. 즉, 출력에서 ​​예외 X가 여기서 발생했다가 예외 Y가 발생했음을보고 싶습니다 .C #에서와 동일합니다. 파이썬 2.6에서 가능합니까? 내가 지금까지 할 수있는 최선의 모습 (Glenn Maynard의 답변을 기반으로 함)은 다음과 같습니다.

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]

여기에는 메시지와 역 추적이 모두 포함되지만 역 추적에서 발생한 예외는 표시되지 않습니다.


3
수락 된 답변이 오래되었습니다. 다른 답변을 수락하는 것이 좋습니다.
Aaron Hall

1
@AaronHall 불행히도 2015 년부터 OP가 보이지 않았습니다.
Antti Haapala

답변:


136

파이썬 2

간단 해; 세 번째 인수로 역 추적을 전달하십시오.

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

한 예외를 포착하고 다른 예외를 다시 제기 할 때 항상이 작업을 수행하십시오.


4
감사. 추적은 유지되지만 원래 예외의 오류 메시지는 손실됩니다. 메시지와 역 추적을 모두 보려면 어떻게합니까?
EMP

6
raise MyException(str(e)), ...
글렌 메이 나드

23
Python 3 추가 raise E() from tb.with_traceback(...)
Dima Tisnek

3
@GlennMaynard 그것은 꽤 오래된 질문이지만의 중간 인수 raise는 예외에 전달할 값입니다 (첫 번째 인수가 예외 클래스이고 인스턴스가 아닌 경우). 따라서을 수행하는 대신 예외를 바꾸려면 다음 raise MyException(str(e)), None, sys.exc_info()[2]을 사용하는 것이 좋습니다 raise MyException, e.args, sys.exc_info()[2]..
bgusach

8
향후 패키지 python-future.org/compatible_idioms.html#raising-exceptions Eg from future.utils import raise_및 패키지를 사용하여 Python2 및 3 호환 방식이 가능합니다 raise_(ValueError, None, sys.exc_info()[2]).
jtpereyda

239

파이썬 3

파이썬 3에서는 다음을 수행 할 수 있습니다.

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

이것은 다음과 같은 것을 생성합니다 :

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

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

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")

17
raise ... from ...실제로 파이썬 3 에서이 작업을 수행하는 올바른 방법입니다. 더 많은 공감 수가 필요합니다.
Nakedible

Nakedible불행히도 대부분의 사람들이 여전히 파이썬 3을 사용하지 않기 때문이라고 생각합니다.
Tim Ludwinski

이것은 파이썬 3에서 'from'을 사용하더라도 발생하는 것으로 보입니다.
Steve Vermeulen

파이썬 2로 백 포트 될 수 있습니다. 언젠가는 희망합니다.
Marcin Wojnarski

4
@ogrisel future패키지를 사용하여 이를 달성 할 수 있습니다 : python-future.org/compatible_idioms.html#raising-exceptions 예 : from future.utils import raise_raise_(ValueError, None, sys.exc_info()[2]).
jtpereyda

19

파이썬 3에는 예외를 연결 하는 raise... from절이 있습니다. 글렌의 답변 은 Python 2.7에 적합하지만 원래 예외의 역 추적 만 사용하고 오류 메시지 및 기타 세부 정보를 버립니다. 다음은 현재 범위의 컨텍스트 정보를 원래 예외의 오류 메시지에 추가하지만 다른 세부 정보는 그대로 유지하는 Python 2.7의 일부 예입니다.

알려진 예외 유형

try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback

맛의 raise 제 식 번째 식과 튜플의 예외 클래스 생성자 인수 및 제 표현식으로 추적 같은 예외 유형 걸린다. Python 2.2 이전 버전을 실행중인 경우의 경고를 참조하십시오 sys.exc_info().

모든 예외 유형

다음은 코드에서 어떤 종류의 예외를 포착해야하는지 모르는 경우보다 일반적인 목적의 또 다른 예입니다. 단점은 예외 유형이 손실되고 RuntimeError가 발생한다는 것입니다. traceback모듈 을 가져와야합니다 .

except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb

메시지 수정

예외 유형으로 컨텍스트를 추가 할 수있는 다른 옵션이 있습니다. 예외 메시지를 수정 한 다음 다시 가져올 수 있습니다.

import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise

다음과 같은 스택 추적이 생성됩니다.

Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']

check_output()호출 된 행을 표시 하지만 예외 메시지에 명령 행이 포함되어 있음을 알 수 있습니다.


1
어디에서 ex.strerror오는가? 파이썬 문서에서 관련 히트를 찾을 수 없습니다. 그렇지 str(ex)않습니까?
Henrik Heimbuerger

1
IOErrorEnvironmentError@hheimbuerger 에서 파생되며 errornostrerror속성 을 제공 합니다.
돈 커크비

catch Error를 통해 임의의 (예 : ValueError)를 어떻게 래핑 합니까? 이 경우에 대한 답변을 재현하면 스택 추적이 손실됩니다. RuntimeErrorException
Karl Richter

@karl, 당신이 무엇을 묻는 지 잘 모르겠습니다. 새 질문에 샘플을 게시 한 다음 여기에서 링크 할 수 있습니까?
Don Kirkby

나는 stackoverflow.com/questions/23157766/… 에서 OP의 질문 사본을 편집 하여 답변을 직접 고려하여 명확하게했습니다. 우리는 거기서 논의해야합니다 :)
Karl Richter

12

에서 파이썬 3.x를 :

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

또는 단순히

except Exception:
    raise MyException()

처리되지 않으면 예외를 전파 MyException하지만 인쇄 합니다 .

에서 파이썬 2.x에서 :

raise Exception, 'Failed to process file ' + filePath, e

__context__속성 을 강제 종료하여 두 예외를 모두 인쇄하지 못하게 할 수 있습니다 . 여기에 내가 그 캐치를 사용하여 상황에 맞는 매니저를 작성하고 즉석에서 예외를 변경합니다 (참조 http://docs.python.org/3.1/library/stdtypes.html를 작동 방식의 expanation에 대한)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise

4
형식 오류 : 인상 : ARG 3 역 추적 또는 없음이어야합니다
글렌 메이 나드

죄송합니다, 실수를 저질렀습니다. 어쨌든 예외를 허용하고 자동으로 추적 속성을 얻는다고 생각했습니다. docs.python.org/3.1/reference/…에 따라 e .__ traceback__
ilya n

1
@ilyan .: Python 2에는 e.__traceback__속성 이 없습니다 !
Jan Hudec

5

파이썬 2.x 에서이 작업을 수행 할 수는 없다고 생각하지만이 기능과 비슷한 것이 Python 3의 일부입니다. From PEP 3134 :

오늘날의 파이썬 구현에서 예외는 유형, 값 및 역 추적의 세 부분으로 구성됩니다. 'sys'모듈은 세 가지 병렬 변수 exc_type, exc_value 및 exc_traceback에 현재 예외를 표시하고 sys.exc_info () 함수는이 세 부분의 튜플을 반환하며 'raise'문은 세 인수 형식을 허용합니다. 이 세 부분. 예외를 조작하려면 종종이 세 가지를 병렬로 전달해야하므로 지루하고 오류가 발생하기 쉽습니다. 또한 'except'문은 역 추적이 아니라 값에 대한 액세스 만 제공 할 수 있습니다. ' traceback '속성을 예외 값에 추가하면 모든 예외 정보를 한 곳에서 액세스 할 수 있습니다.

C #과 비교 :

C #의 예외에는 다른 예외를 가리킬 수있는 읽기 전용 'InnerException'속성이 포함되어 있습니다. 그것의 문서 [10]는 "예외 X가 이전 예외 Y의 직접적인 결과로 발생 될 때, X의 InnerException 속성은 Y에 대한 참조를 포함해야한다"고 말합니다. 이 속성은 VM에 의해 자동으로 설정되지 않습니다. 오히려 모든 예외 생성자는 선택적인 'innerException'인수를 사용하여 명시 적으로 설정합니다. ' cause '속성은 InnerException과 동일한 목적을 수행하지만이 PEP는 모든 예외의 생성자를 확장하는 대신 새로운 형태의 '상승'을 제안합니다. C #은 또한 InnerException 체인의 끝으로 바로 이동하는 GetBaseException 메서드를 제공합니다.

Java, Ruby 및 Perl 5는 이러한 유형의 것도 지원하지 않습니다. 다시 인용 :

다른 언어와 마찬가지로 Java 및 Ruby는 'catch'/ 'rescue'또는 'finally'/ 'ensure'절에서 다른 예외가 발생하면 원래 예외를 버립니다. Perl 5에는 내장 된 구조적 예외 처리 기능이 없습니다. Perl 6의 경우 RFC 번호 88 [9]는 @@이라는 배열에 체인으로 연결된 예외를 암시 적으로 유지하는 예외 메커니즘을 제안합니다.


그러나 Perl5에서는 "qq {OH NOES! $ @} 고백"이라고 말하고 다른 예외의 스택 추적을 잃지 않습니다. 또는 예외를 유지하는 자체 유형을 구현할 수 있습니다.
jrockway

4

Python 2와 3 사이의 호환성을 극대화하기 raise_from위해 six라이브러리 에서 사용할 수 있습니다 . https://six.readthedocs.io/#six.raise_from . 다음은 예입니다 (명확하게하기 위해 약간 수정 됨).

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)

3

Python 2.x에서 예외를 체인화하기 위해 CausedException 클래스 를 사용할 수 있습니다 (또한 Python 3에서도 새로 발생한 예외의 원인으로 둘 이상의 포착 된 예외를 제공하려는 경우 유용 할 수 있습니다). 어쩌면 당신을 도울 수 있습니다.


2

관련 정보를 가져 와서 전달할 수 있습니까? 나는 다음과 같은 것을 생각하고있다 :

import traceback
import sys
import StringIO

class ApplicationError:
    def __init__(self, value, e):
        s = StringIO.StringIO()
        traceback.print_exc(file=s)
        self.value = (value, s.getvalue())

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

try:
    try:
        a = 1/0
    except Exception, e:
        raise ApplicationError("Failed to process file", e)
except Exception, e:
    print e

2

가정 :

  • Python 2에서 작동하는 솔루션이 필요합니다 (순수한 Python 3의 경우 raise ... from 솔루션 )
  • 오류 메시지를 풍부하게하고 싶습니다 (예 : 추가 컨텍스트 제공)
  • 전체 스택 추적이 필요합니다

문서 https://docs.python.org/3/tutorial/errors.html#raising-exceptions 에서 간단한 솔루션을 사용할 수 있습니다 .

try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!' # print or log, provide details about context
    raise # reraise the original exception, keeping full stack trace

출력 :

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

핵심 부분은 독립적 인 '상승'키워드입니다. except 블록에서 Exception을 다시 발생시킵니다.


이것은 Python 2 & 3 호환 솔루션입니다! 감사!
Andy Chase

아이디어는 다른 유형의 예외를 제기하는 것이라고 생각합니다.
Tim Ludwinski

2
이것은 하나의 예외를 다시 발생시키는 중첩 된 예외의 체인이 아닙니다
Karl Richter

예외 메시지를 보강하고 전체 스택 추적이 필요한 경우 이것이 최고의 Python 2 솔루션입니다!
geekQ

raise와 raise의 차이점
변수
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.