파이썬에서 사용자 정의 메시지로 동일한 예외를 발생시키는 방법은 무엇입니까?


145

try내 코드 에이 블록 이 있습니다.

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

엄밀히 말하면, 사실은 상승하고 다른 ValueError 하지 ValueError에 의해 슬로우 do_something...()로 언급되는, err이 경우에는. 사용자 정의 메시지를 err어떻게 첨부 합니까? 나는 다음과 같은 코드를 시도하지만 인해 실패 errA, ValueError , 호출되지 않는 :

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

13
@Hamish, 추가 정보를 첨부하고 예외를 다시 발생시키는 것은 디버깅 할 때 매우 유용 할 수 있습니다.
Johan Lundberg

@Johan 절대적으로-그것이 바로 스택 트레이스입니다. 새 오류를 발생시키는 대신 기존 오류 메시지를 편집 한 이유를 이해할 수 없습니다.
Hamish

@ 해미시 물론이지만 다른 것을 추가 할 수 있습니다. 귀하의 질문에 대한 내 대답과 UnicodeDecodeError의 예를 살펴보십시오. 당신이 그것에 대한 의견이 있다면 아마도 내 대답을 주석으로 처리하십시오.
Johan Lundberg


1
@ 키트는 2020이며 파이썬 3은 어디에나 있습니다. 왜 :-) 벤의 대답에 허용 대답 변경하지 마십시오
MIT

답변:


88

업데이트 : Python 3의 경우 Ben의 답변을 확인하십시오.


현재 예외에 메시지를 첨부하고 다시 올리려면 다음을 수행하십시오.

python 2.x의 경우 x> = 6 :

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

이 또한 옳은 일을 할 것입니다 경우 err입니다 파생 에서 ValueError. 예를 들면 UnicodeDecodeError.

원하는 것을 추가 할 수 있습니다 err. 예를 들면 err.problematic_array=[1,2,3].


편집 : @Ducan은 python 3 .message의 멤버가 아니기 때문에 위의 python 3에서는 작동하지 않습니다 ValueError. 대신 이것을 사용할 수 있습니다 (유효한 파이썬 2.6 이상 또는 3.x).

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

편집 2 :

목적이 무엇인지에 따라 변수 이름 아래에 추가 정보를 추가하도록 선택할 수도 있습니다. python2와 python3 모두 :

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

9
Python 3 스타일 예외 처리 및를 사용하려고 노력했기 때문에 예외 print에 대한 message속성 이 없으므로 Python 3.x에서 코드가 작동하지 않을 수 있습니다 . err.args = (err.args[0] + " hello",) + err.args[1:]더 안정적으로 작동 할 수 있습니다 (그리고 메시지를 얻기 위해 문자열로 변환하십시오).
던컨

1
불행히도 args [0]이 오류 메시지를 나타내는 문자열 유형이라는 보장은 없습니다.- "예외 생성자에 제공된 인수의 튜플. IOError와 같은 일부 내장 예외는 특정 수의 인수를 예상하고 특별한 의미를 지정합니다. 이 튜플의 요소는 다른 하나는 보통 오류 메시지를 제공하는 단일 문자열로만 호출됩니다. " 따라서 코드가 작동하지 않습니다. arg [0]은 오류 메시지가 아닙니다 (int 일 수도 있고 파일 이름을 나타내는 문자열 일 수도 있습니다).
Trent

1
@Taras, 재미있는. 그것에 대한 참조가 있습니까? 그런 다음 완전히 새로운 멤버 인 err.my_own_extra_info에 추가하겠습니다. 또는 새로운 정보와 원래 정보를 유지하면서 내 예외로 모든 것을 캡슐화하십시오.
요한 룬드 버그

2
args [0]이 오류 메시지가 아닌 경우의 실제 예 -docs.python.org/2/library/exceptions.html- "exception EnvironmentError Python 시스템 외부에서 발생할 수있는 예외의 기본 클래스 : IOError, OSError. 이 유형의 예외가 2- 튜플을 사용하여 생성 된 경우 첫 번째 항목은 인스턴스의 errno 속성에서 사용할 수 있으며 (오류 번호로 간주 됨) 두 번째 항목은 strerror 속성에서 사용할 수 있습니다 (보통 연관된 항목 임) 튜플 자체는 args 속성에서도 사용할 수 있습니다. "
Trent

2
나는 이것을 전혀 이해하지 못한다. 여기서 .message속성을 설정하는 유일한 이유 는이 속성이 명시 적으로 인쇄 되기 때문입니다 . 잡거나 인쇄하지 않고 예외를 발생 시키면 속성이 유용한 기능을 수행하는 것을 볼 수 없습니다.message .
DanielSank

171

파이썬 3.x 만 지원할만큼 운이 좋으면 이것이 정말 아름다움이됩니다 :)

에서 올리다

raise from을 사용하여 예외를 연결할 수 있습니다 .

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

이 경우, 발신자가 잡을 수있는 예외는 예외를 제기하는 장소의 줄 번호를 갖습니다.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

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

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

맨 아래 예외에는 예외를 발생시킨 지점의 스택 추적 만 있습니다. 발신자는 여전히 예외 예외의 __cause__속성 에 액세스하여 원래 예외를 얻을 수 있습니다 .

with_traceback

또는 with_traceback 을 사용할 수 있습니다 .

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

이 양식을 사용하면 발신자가 포착 한 예외에 원래 오류가 발생한 위치의 역 추적이 있습니다.

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

맨 아래 예외에는 잘못된 나누기를 수행 한 행과 예외를 다시 발생시키는 행이 있습니다.


1
추가 역 추적없이 예외에 사용자 정의 메시지를 추가 할 수 있습니까? 예를 들어, raise Exception('Smelly socks') from e"스 멜리 양말"을 새로운 역 추적을 도입하지 않고 원래 역 추적에 주석으로 추가하도록 수정할 수 있습니다.
joelostblom

이것이 Johan Lundberg의 답변에서 얻을 수있는 행동입니다
Ben

3
정말 사랑 스럽습니다. 감사합니다.
allanberry

3
새 메시지로 새 예외를 발생 시키거나 체인 예외를 발생 시키면 많은 경우에 필요한 것보다 더 많은 혼란이 발생합니다. 그 자체로는 예외를 처리하기가 복잡합니다. 더 나은 전략은 가능한 경우 err.args + = ( "message")에서와 같이 가능한 경우 원래 예외의 인수에 메시지를 추가하고 예외 메시지를 다시 발생시키는 것입니다. 역 추적은 예외가 발생한 행 번호로 이동하지 않지만 예외가 발생한 위치로 이동합니다.
사용자 별표

2
from 절에 None을 지정하여 예외 체인 표시 를 명시 적으로 억제 할 수도 있습니다 .raise RuntimeError("Something bad happened") from None
pfabri

10
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

인쇄물:

There is a problem: invalid literal for int() with base 10: 'a'

1
다른 인스턴스를 올리는 것 외에 내가하려고하는 것에 대한 파이썬 관용구가 있는지 궁금 합니다.
키트

@ 키트-나는 '예외를 다시 제기'라고합니다 : docs.python.org/reference/simple_stmts.html#raise
eumiro

1
@eumiro, 아니요 당신은 새로운 예외를 만들고 있습니다. 내 대답을 참조하십시오. 귀하의 링크에서 : "... 재조정 할 예외가 현재 범위에서 가장 최근에 활성화 된 예외 인 경우 표현식이없는 인상을 선호해야합니다."
Johan Lundberg 8

3
@JohanLundberg- raise매개 변수가 없으면 다시 올립니다 . OP가 메시지를 추가하려면 새 예외를 제기해야하며 원래 예외의 메시지 / 유형을 재사용 할 수 있습니다.
eumiro

2
메시지 를 추가 하려면 "ValueError"를 발생시켜 처음부터 새 메시지를 작성할 수 없습니다. 이렇게하면 어떤 종류의 ValueError인지에 대한 기본 정보가 삭제됩니다 (C ++에서 슬라이스하는 것과 유사 함). 인수없이 raise를 사용 하여 동일한 예외 를 다시 발생 시키면 올바른 특정 유형 (ValueError에서 파생 됨)으로 원래 객체를 전달합니다.
Johan Lundberg

9

모든 답변이 정보를 e.args [0]에 추가하여 기존 오류 메시지를 변경하는 것 같습니다. args 튜플을 확장하는 데 단점이 있습니까? 가능한 거꾸로 생각할 수 있습니다. 해당 문자열을 구문 분석 해야하는 경우 원래 오류 메시지를 그대로 둘 수 있습니다. 사용자 지정 오류 처리에서 여러 메시지 또는 오류 코드가 생성 된 경우 (예 : 시스템 모니터링 도구를 통해) 트레이스 백이 프로그래밍 방식으로 구문 분석되는 경우 여러 요소를 튜플에 추가 할 수 있습니다.

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

또는

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

이 접근법의 단점을 볼 수 있습니까?


내 이전 답변은 e.args [0]을 변경하지 않습니다.
Johan Lundberg

4

이 코드 템플릿을 사용하면 사용자 정의 메시지로 예외를 제기 할 수 있습니다.

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

3
스택 추적을 유지하지 않습니다.
plok

질문자는 스택 추적을 유지하도록 지정하지 않습니다.
shrewmouse 2016 년

4

다음을 사용하여 오류 메시지와 함께 새로운 예외를 제기하십시오.

raise Exception('your error message')

또는

raise ValueError('your error message')

'from'(Python 3.x 만 지원)을 사용하여 그것을 올리거나 오류 메시지를 현재 예외에 첨부 (교체)하려는 위치 내에서 :

except ValueError as e:
  raise ValueError('your message') from e

고맙습니다, @gberger는 '전자에서'접근 방식은 실제로 파이썬 2.x를 지원하지 않습니다
알렉세이 Antonenko을

3

이것은 원래의 역 추적을 유지하면서 Python 2.7 및 3.x의 예외 메시지를 수정하는 데 사용하는 함수입니다. 필요합니다six

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

3

Python 3 내장 예외에는 다음과 같은 strerror필드가 있습니다.

except ValueError as err:
  err.strerror = "New error message"
  raise err

작동하지 않는 것 같습니다. somethng가 누락 되었습니까?
MasayoMusic

2

예외가 다시 잡히지 않으면 추가 된 메시지가 표시되지 않으면 현재 답변이 나에게 효과적이지 않았습니다.

그러나 아래와 같이하면 예외가 다시 발생하는지 여부에 관계없이 추적이 유지되고 추가 된 메시지가 표시됩니다.

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(나는 Python 2.7을 사용했지만 Python 3에서는 시도하지 않았습니다)


1

위의 솔루션 중 어느 것도 내가 원하는 것을 정확하게 수행하지 못했습니다. 이는 오류 메시지의 첫 부분에 정보를 추가하는 것이 었습니다. 즉, 사용자가 내 사용자 정의 메시지를 먼저 볼 수 있기를 원했습니다.

이것은 나를 위해 일했다 :

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

0

이것은 Python 3에서만 작동합니다 . 예외의 원래 인수를 수정하고 자신의 인수를 추가 할 수 있습니다.

예외는 작성된 인수를 기억합니다. 나는 이것이 예외를 수정할 수 있다고 가정합니다.

이 함수 reraise에서 예외의 원래 인수 앞에 원하는 새 인수 (메시지 등)를 추가합니다. 마지막으로 역 추적 이력을 유지하면서 예외를 다시 발생시킵니다.

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

산출

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

-3

오류 유형을 사용자 정의하려면 ValueError를 기반으로 오류 클래스를 정의하면됩니다.


이 경우 어떻게 도움이 될까요?
Johan Lundberg
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.