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

Python 개선 제안 한국어 번역

PEP 490 – C 수준에서 예외 연결

Author:
Victor Stinner <vstinner at python.org>
Status:
Rejected
Type:
Standards Track
Created:
25-Mar-2015
Python-Version:
3.6

Table of Contents

번역·라이선스 안내

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

초록

Python 수준에서 이미 수행되는 것처럼 C 수준에서도 예외를 연결합니다.

근거

Python 3에서는 새로운 획기적인 기능을 도입했습니다. 예외가 기본적으로 연결됩니다. PEP 3134.

예제:

try:
    raise TypeError("err1")
except TypeError:
    raise ValueError("err2")

출력:

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    raise TypeError("err1")
TypeError: err1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise ValueError("err2")
ValueError: err2

Python 코드에서는 예외가 기본적으로 연결되지만, C로 작성된 확장 기능에서는 연결되지 않습니다.

예외를 연결하기 위한 새로운 비공개 _PyErr_ChainExceptions() 함수가 Python 3.4.3 및 3.5에 도입되었습니다. 현재는 예외를 연결하려면 이 함수를 명시적으로 호출해야 하며, 사용법도 간단하지 않습니다.

이전 OSError를 새로운 ZipImportError 예외에 연결하기 위해 zipimport 모듈에서 _PyErr_ChainExceptions()를 사용하는 예:

PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
_PyErr_ChainExceptions(exc, val, tb);

이 PEP에서는 일관성을 유지하고 실패에 관한 더 많은 정보를 제공하여 디버깅에 도움을 주도록 C 수준에서도 예외를 자동으로 연결할 것을 제안합니다. 앞의 예제는 단순해집니다.:

PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);

제안

예외를 연결하도록 PyErr_*() 함수 수정

예외를 발생시키는 Python C API의 C 함수를 수정하여 예외를 자동으로 연결합니다. PyErr_SetString(), PyErr_Format(), PyErr_SetNone() 등을 수정합니다.

예외를 연결하지 않도록 함수 수정

새 예외에 이전 예외의 정보 또는 그보다 더 많은 정보가 포함되어 있는 경우, 특히 두 예외의 유형이 같은 경우에는 이전 예외를 유지하는 것이 항상 유용하지는 않습니다.

int(str)를 사용한 쓸모없는 예외 연결 예:

TypeError: a bytes-like object is required, not 'type'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a number, not 'type'

새로운 TypeError 예외에는 이전 예외보다 더 많은 정보가 포함되어 있습니다. 이전 예외는 숨겨야 합니다.

새 예외를 발생시키기 전에 현재 예외를 지우도록 PyErr_Clear() 함수를 호출하여 현재 예외가 새 예외와 연결되지 않도록 할 수 있습니다.

예외를 연결하도록 함수 수정

일부 함수는 현재 예외를 저장한 다음 복원합니다. 새 예외가 발생하면 함수에 따라 해당 예외가 현재 sys.stderr에 표시되거나 무시됩니다. 이러한 함수 중 일부는 대신 예외를 연결하도록 수정해야 합니다.

새 예외를 무시하는 함수의 예:

  • ptrace_enter_call(): 예외 무시
  • subprocess_fork_exec(): enable_gc()에서 발생한 예외 무시
  • _thread 모듈의 t_bootstrap(): 부트스트랩 함수를 sys.stderr에 표시하려고 시도하는 과정에서 발생한 예외를 무시합니다.
  • PyDict_GetItem(), _PyDict_GetItem_KnownHash(): 딕셔너리에서 키를 찾다가 발생한 예외 무시
  • _PyErr_TrySetFromCause(): 예외 무시
  • PyFrame_LocalsToFast(): dict_to_map()에서 발생한 예외를 무시합니다.
  • _PyObject_Dump(): 예외를 무시합니다. _PyObject_Dump()은 디버깅하고 실행 중인 프로세스를 검사하는 데 사용되므로 Python 상태를 수정해서는 안 됩니다.
  • Py_ReprLeave(): 예외를 무시합니다. “예외를 보고할 방법이 없기 때문입니다.”
  • type_dealloc(): remove_all_subclasses()에서 발생한 예외를 무시합니다.
  • PyObject_ClearWeakRefs(): 예외를 무시합니까?
  • call_exc_trace(), call_trace_protected(): 예외를 무시합니다.
  • remove_importlib_frames(): 예외를 무시합니다.
  • do_mktuple(), 예를 들어 Py_BuildValue()에서 사용되는 도우미: 예외를 무시합니까?
  • flush_io(): 예외를 무시합니다.
  • sys_write(), sys_format(): 예외를 무시합니다.
  • _PyTraceback_Add(): 예외를 무시합니다.
  • PyTraceBack_Print(): 예외를 무시합니다.

새 예외를 sys.stderr에 표시하는 함수의 예시는 다음과 같습니다.

  • atexit_callfuncs(): PyErr_Display()로 예외를 표시하고 가장 최근의 예외를 반환합니다. 이 함수는 여러 콜백을 호출하며 가장 최근의 예외만 반환합니다.
  • sock_dealloc(): PyErr_WriteUnraisable()ResourceWarning예외를 기록합니다.
  • slot_tp_del(): PyErr_WriteUnraisable()로 예외를 표시합니다.
  • _PyGen_Finalize(): PyErr_WriteUnraisable()gen_close()예외를 표시합니다.
  • slot_tp_finalize(): PyErr_WriteUnraisable()__del__()메서드에서 발생한 예외를 표시합니다.
  • PyErr_GivenExceptionMatches(): PyErr_WriteUnraisable()PyType_IsSubtype()에서 발생한 예외를 표시합니다.

하위 호환성

예외 연결의 부수 효과는 예외가 트레이스백 객체를 저장하고, 트레이스백 객체가 프레임 객체를 저장하며, 프레임 객체가 지역 변수를 저장한다는 점입니다. 지역 변수는 예외에 의해 계속 유지됩니다. 흔히 발생하는 문제는 지역 변수와 예외 사이의 참조 순환입니다. 예외가 지역 변수에 저장되고 프레임이 예외에 간접적으로 저장됩니다. 이 순환은 예외를 저장하는 애플리케이션에만 영향을 줍니다.

이제 Python 3.5에서 도입된 새로운 traceback.TracebackException객체로 참조 순환을 해결할 수 있습니다. 이 객체는 지역 변수를 저장하지 않고도 전체 텍스트 트레이스백을 형식화하는 데 필요한 정보를 저장합니다.

asyncio는 참조 순환 문제의 영향을 받습니다. 이 모듈은 Python 표준 라이브러리 외부에서도 유지 관리되어 Python 3.3용 버전을 릴리스합니다. 참조 순환 문제를 해결하기 위해 traceback.TracebackException이 비공개 asyncio 모듈로 백포트될 수도 있습니다.

대안

변경하지 않습니다.

새로운 비공개 _PyErr_ChainExceptions()함수로 예외를 수동으로 연결하기에 충분합니다.

타당한 경우에만 예외를 명시적으로 연결합니다.

예외를 연결하기 위한 새로운 헬퍼

PyErr_SetString()과 같은 함수는 예외를 자동으로 연결하지 않습니다. _PyErr_ChainExceptions()의 사용을 쉽게 하기 위해 새로운 비공개 함수가 추가되었습니다.

  • _PyErr_SetStringChain(exc_type, message)
  • _PyErr_FormatChain(exc_type, format, ...)
  • _PyErr_SetNoneChain(exc_type)
  • _PyErr_SetObjectChain(exc_type, exc_value)

_PyErr_SetKeyError(key) 또는 PyErr_SetImportError(message, name, path)와 같이 특정 예외를 발생시키는 헬퍼 함수는 예외를 연결하지 않습니다. 이러한 헬퍼 함수와 함께 일반적인 _PyErr_ChainExceptions(exc_type, exc_value, exc_tb)를 사용하여 예외를 연결해야 합니다.

부록

PEP

  • PEP 3134 – 예외 연결 및 내장 트레이스백 (Python 3.0): 예외에 새로운 __context____cause__ 속성을 추가합니다.
  • PEP 415 – 예외 속성을 사용한 컨텍스트 억제 구현 (Python 3.3): raise exc from None
  • PEP 409 – 예외 컨텍스트 억제 (PEP 415로 대체됨)

Python C API

헤더 파일 Include/pyerror.h는 예외와 관련된 함수를 선언합니다.

예외를 발생시키는 함수:

  • PyErr_SetNone(exc_type)
  • PyErr_SetObject(exc_type, exc_value)
  • PyErr_SetString(exc_type, message)
  • PyErr_Format(exc, format, ...)

특정 예외를 발생시키는 헬퍼:

  • PyErr_BadArgument()
  • PyErr_BadInternalCall()
  • PyErr_NoMemory()
  • PyErr_SetFromErrno(exc)
  • PyErr_SetFromWindowsErr(err)
  • PyErr_SetImportError(message, name, path)
  • _PyErr_SetKeyError(key)
  • _PyErr_TrySetFromCause(prefix_format, ...)

현재 예외 관리:

  • PyErr_Clear(): except: pass처럼 현재 예외를 지웁니다.
  • PyErr_Fetch(exc_type, exc_value, exc_tb)
  • PyErr_Restore(exc_type, exc_value, exc_tb)
  • PyErr_GetExcInfo(exc_type, exc_value, exc_tb)
  • PyErr_SetExcInfo(exc_type, exc_value, exc_tb)

예외를 처리하는 다른 함수들:

  • PyErr_ExceptionMatches(exc): except exc:  ...를 구현하기 위한 검사
  • PyErr_GivenExceptionMatches(exc1, exc2)
  • PyErr_NormalizeException(exc_type, exc_value, exc_tb)
  • _PyErr_ChainExceptions(exc_type, exc_value, exc_tb)

Python 이슈

예외 연쇄:

예외를 잃어버리지 않도록 방지하는 변경 사항:

거부

이 PEP는 2017년 9월 12일 Victor Stinner에 의해 거부되었습니다. python-dev 논의에서 C 예외를 기본적으로 연쇄시키지 않고, 의미가 있는 경우에만 명시적으로 연쇄시키기로 결정되었습니다.