Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 409 – 예외 컨텍스트 억제

Author:
Ethan Furman <ethan at stoneleaf.us>
Status:
Final
Type:
Standards Track
Created:
26-Jan-2012
Python-Version:
3.3
Post-History:
30-Aug-2002, 01-Feb-2012, 03-Feb-2012
Superseded-By:
415
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

PEP 3134에 남아 있는 미해결 문제 중 하나는 컨텍스트를 억제하는 것입니다. 현재는 이를 수행할 방법이 없습니다. 이 PEP는 그 방법을 하나 제안합니다.

근거

예외를 발생시키는 기본적인 방법은 두 가지입니다:

  1. Python이 발생시키는 경우(버그가 있는 코드, 누락된 리소스, 루프 종료 등)
  2. 수동으로 발생시키는 경우(raise 문 사용)

라이브러리를 작성할 때나, 심지어 사용자 정의 클래스만 작성할 때에도 예외를 발생시켜야 할 수 있습니다. 더 나아가 한 예외를 다른 예외로 바꾸는 것이 유용하거나 필요할 수도 있습니다. 제 dbf 모듈에서 예를 들어 보겠습니다:

try:
    value = int(value)
except Exception:
    raise DbfError(...)

원래 예외가 무엇이었는지(ValueError, TypeError 또는 다른 무엇이든)는 중요하지 않습니다. 이 시점부터의 예외는 DbfError이며, 원래 예외는 아무런 가치가 없습니다. 그러나 현재는 이 예외를 출력하면 두 예외가 모두 표시됩니다.

대안

몇 가지 방안이 제시되었습니다:

  • raise as NewException()

    as 키워드를 재사용합니다. 실제로 원래 예외를 다시 발생시키는 것이 아니므로 혼동을 줄 수 있습니다

  • raise NewException() from None

    원래 예외를 명시적으로 선언하는 기존 문법을 따릅니다

  • exc = NewException(); exc.__context__ = None; raise exc

    앞의 방법을 매우 장황하게 쓴 방식입니다

  • raise NewException.no_context(...)

    컨텍스트 억제를 클래스 메서드로 만듭니다.

위의 모든 방안은 코어를 변경해야 합니다.

제안

두 번째 방안을 채택할 것을 제안합니다:

raise NewException from None

이 방안은 원인을 명시적으로 설정하는 기존 패턴을 사용한다는 장점이 있습니다:

raise KeyError() from NameError()

하지만 원인이 None이므로 기본 예외 출력 루틴은 이전 컨텍스트를 표시하지 않습니다.

구현 논의

참고: 이 PEP가 수락된 후, 더 깔끔한 구현 메커니즘이 PEP 415에서 제안되어 수락되었습니다. Python 3.3에서 실제로 사용된 구현에 대한 자세한 내용은 해당 PEP를 참조하십시오.

현재 __context__와 __cause__ 모두 기본값이 None입니다. raise ... from None(__cause__를 None으로 설정합니다)을 지원하려면 __cause__에 다른 기본값이 필요합니다. 이를 언어 수준에서 구현하는 방법에 대해 여러 아이디어가 제시되었습니다.

  • 이전 예외 정보를 덮어씁니다(문제를 회피하고 __cause__를 None으로 둡니다).

    poor error messages 때문에 디버깅을 심각하게 방해할 수 있으므로 기각되었습니다.

  • __cause__에 불리언 값 중 하나를 사용합니다. False가 기본값이 되고, from ...이 사용되면 명시적으로 연쇄된 예외나 None으로 대체됩니다.

    __cause__에 서로 다른 두 가지 객체 타입을 사용하도록 부추기고, 그중 하나(불리언)는 가능한 값의 전체 범위를 가질 수 없으므로(True는 절대 사용되지 않습니다) 기각되었습니다.

  • 특별한 예외 클래스 __NoException__을 만듭니다.

    혼란스러울 수 있고, 사용자가 실수로 발생시킬 수 있으며, None, True, False처럼 진정으로 유일한 값이 아니므로 기각되었습니다.

  • Ellipsis(... 싱글턴)를 기본값으로 사용합니다.

    채택되었습니다.

    영어에서 말줄임표는 단어가 생략되었을 때 자리 표시자로 흔히 사용됩니다. 이 점은 __cause__가 생략되었으니 자세한 내용은 __context__를 보라는 신호로서 여기서 유리하게 작용합니다.

    Ellipsis는 예외가 아니므로 발생시킬 수 없습니다.

    Ellipsis는 하나뿐이므로 사용되지 않는 값이 없습니다.

    오류 정보가 버려지지 않으므로, 기본 코드가 추적하지 않더라도 사용자 정의 코드는 전체 예외 연쇄를 추적할 수 있습니다.

언어 세부 사항

raise Exception from None을 지원하기 위해 __context__는 그대로 유지되지만, __cause__는 Ellipsis로 시작하며 raise Exception from None 방식이 사용되면 None으로 바뀝니다.

형식 __context__ __cause__
raise None Ellipsis
다시 raise 이전 예외 Ellipsis
None | ChainedException으로부터 다시 raise 이전 예외 None | 명시적으로 연쇄된 예외

그러면 기본 예외 출력 루틴은 다음과 같이 동작합니다.

  • __cause__가 Ellipsis이면 __context__(있는 경우)가 출력됩니다.
  • __cause__가 None이면 __context__는 출력되지 않습니다.
  • __cause__가 그 밖의 값이면 __cause__가 출력됩니다.

뒤의 두 경우 모두 예외 체인을 더 이상 따라가지 않습니다.

이제 __cause__의 기본값이 Ellipsis이고 raise Exception from Cause는 단순히 다음에 대한 문법적 설탕이므로:

_exc = NewException()
_exc.__cause__ = Cause()
raise _exc

이제 None뿐만 아니라 Ellipsis도 원인으로 허용됩니다:

raise Exception from Ellipsis

패치

이를 구현하는 CPython 패치가 Issue 6210에 첨부되어 있습니다.

참고 문헌

이 thread on python-dev에서 이루어진 논의와 개선 사항입니다.