PEP 490 – C 수준에서 예외 연결
- Author:
- Victor Stinner <vstinner at python.org>
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 25-Mar-2015
- Python-Version:
- 3.6
번역·라이선스 안내
이 비공식 한국어 번역은 원문 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
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 이슈
예외 연쇄:
- 이슈 #23763: C에서 예외 연쇄
- 이슈 #23696: zipimport: ImportError를 OSError에 연쇄
- 이슈 #21715: C 레벨에서 예외 연쇄:
_PyErr_ChainExceptions()가 추가됨 - 이슈 #18488: sqlite: step() 메서드 호출이 실패하면 사용자 함수의 finalize() 메서드가 예외가 설정된 상태로 호출될 수 있음
- 이슈 #23781: 2.7에 비공개 _PyErr_ReplaceException() 추가
- Issue #23782: Leak in _PyTraceback_Add
예외를 잃어버리지 않도록 방지하는 변경 사항:
거부
이 PEP는 2017년 9월 12일 Victor Stinner에 의해 거부되었습니다. python-dev 논의에서 C 예외를 기본적으로 연쇄시키지 않고, 의미가 있는 경우에만 명시적으로 연쇄시키기로 결정되었습니다.
Copyright
This document has been placed in the public domain.