PEP 741 – Python 구성 C API
- Author:
- Victor Stinner <vstinner at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Final
- Type:
- Standards Track
- Created:
- 18-Jan-2024
- Python-Version:
- 3.14
- Post-History:
- 19-Jan-2024, 08-Feb-2024
- Resolution:
- Discourse message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
C 구조체에 의존하지 않고 Python 초기화를 구성하며 향후 ABI 호환 변경을 수행할 수 있는 C API를 추가합니다.
내장 확장 모듈을 추가하는 데 사용할 수 있는 PyInitConfig_AddModule()를 추가하여 PEP 587 API를 완성합니다. 이 기능은 이전에 “inittab”이라고 불렸습니다.
현재 런타임 구성을 가져오고 설정하는 PyConfig_Get() 및 PyConfig_Set() 함수를 추가합니다.
PEP 587 “Python 초기화 구성”은 Python 초기화를 구성하는 모든 방식을 통합했습니다. 이 PEP는 Python 사전 초기화와 Python 초기화의 구성도 단일 API로 통합합니다. 또한 이 PEP는 API를 더욱 단순화하기 위해 “Python”과 “Isolated”라는 두 가지 선택지(PEP 587) 대신 Python을 임베드하는 선택지를 하나만 제공합니다.
하위 수준의 PEP 587 PyConfig API는 CPython 구현 세부 사항과 의도적으로 더 높은 수준으로 결합해야 하는 사용 사례(구성 메커니즘을 포함하여 CPython CLI의 전체 기능을 에뮬레이션하는 경우 등)에 사용할 수 있도록 계속 제공됩니다.
근거
런타임 구성 가져오기
PEP 587에는 현재 런타임 구성을 가져오는 API는 없고, Python 초기화를 구성하는 API만 있습니다.
예를 들어 전역 구성 변수 Py_UnbufferedStdioFlag는 Python 3.12에서 더 이상 사용되지 않으며, 대신 PyConfig.buffered_stdio를 사용하는 것이 권장됩니다. 이는 Python을 구성하는 데만 사용할 수 있으며, PyConfig.buffered_stdio를 가져오는 공개 API는 없습니다.
제한된 C API 사용자는 현재 런타임 구성을 가져오는 공개 API를 요청하고 있습니다.
Cython은 optimization_level 구성 옵션을 가져와야 합니다: issue.
2022년에 전역 구성 변수가 더 이상 사용되지 않게 되었을 때, Marc-André Lemburg requested가 Python 초기화 중뿐만 아니라 런타임에도 이러한 구성 변수에 액세스할 수 있는 C API를 요청했습니다.
보안 수정
매우 큰 문자열을 정수(10진수 기준)로 변환할 때 발생하는 서비스 거부 공격인 CVE-2020-10735를 수정하기 위해 ABI에 영향을 주는 새 PyConfig 멤버를 안정화 브랜치에 추가하는 방안이 논의되었습니다.
Gregory P. Smith는 PyConfig 멤버에 제한되지 않도록 텍스트 기반 구성 파일을 사용하는 다른 API를 제안했습니다: FR: Allow private runtime config to enable extending without breaking the PyConfig ABI (2022년 8월).
결국 안정화 브랜치에는 새로운 PyConfig 멤버를 추가하지 않고, 개발 브랜치(Python 3.12가 됨)에만 새로운 PyConfig.int_max_str_digits 멤버를 추가하기로 결정했습니다. 안정화 브랜치에서는 전용 비공개 전역 변수(PyConfig와 무관함)를 사용합니다.
PyPreConfig와 PyConfig 간 중복
Python 사전 초기화에서는 PyPreConfig 구조체를 사용하고, Python 초기화에서는 PyConfig 구조체를 사용합니다. 두 구조체에는 중복되는 멤버가 네 개 있습니다: dev_mode, parse_argv, isolated 및 use_environment.
이러한 중복은 두 구조체가 분리되어 있기 때문에 발생하며, 일부 PyConfig 멤버는 사전 초기화에 필요합니다.
Python 임베딩
Python을 임베드하는 애플리케이션
예:
- Blender 3D graphics.
- fontforge 글꼴 편집기.
- Gimp입니다.
- LibreOffice입니다.
- OBS Studio입니다.
- Tiled입니다.
- vim 텍스트 편집기입니다.
Linux, FreeBSD 및 macOS에서 애플리케이션은 일반적으로 libpython에 정적으로 링크되거나, libpython을 동적으로 로드합니다. libpython 공유 라이브러리는 버전이 지정되며, 예를 들어 Linux의 Python 3.12에서는 libpython3.12.so입니다.
vim 프로젝트는 안정적인 ABI를 대상으로 할 수 있습니다. 일반적으로 “시스템 Python” 버전이 사용됩니다. 현재는 사용할 Python 버전을 선택할 수 없습니다. 사용자는 필요할 때 더 최신 버전의 Python을 선택할 수 있기를 원합니다.
Linux에서 GIMP와 같이 Python을 임베드하는 애플리케이션을 배포하는 또 다른 방법은 Flatpack, AppImage 또는 Snap “컨테이너”에 Python을 포함하는 것입니다. 이 경우 애플리케이션은 컨테이너와 함께 자체 Python 버전 사본을 포함합니다.
Python을 임베드하는 라이브러리
예:
- Apache mod_wsgi (source).
- nimpy: Nim - Python 브리지입니다.
- PyO3: Python 인터프리터를 위한 Rust 바인딩입니다.
독립 실행형 애플리케이션을 생성하는 유틸리티
- macOS용 py2app입니다.
- Windows용 py2exe입니다.
- pyinstaller입니다.
- PyOxidizer: PEP 587 PyConfig API를 사용합니다.
이러한 유틸리티는 독립 실행형 애플리케이션을 생성하며, libpython에 링크되지 않습니다.
런타임 구성 설정
Marc-André Lemburg requested는 런타임에 일부 구성 옵션의 값을 설정할 수 있는 C API를 요청했습니다:
optimization_levelverboseparser_debuginspectwrite_bytecode
이전에는 전역 구성 변수를 직접 설정할 수 있었습니다:
Py_OptimizeFlagPy_VerboseFlagPy_DebugFlagPy_InspectFlagPy_DontWriteBytecodeFlag
하지만 이러한 구성 플래그는 Python 3.12에서 더 이상 사용되지 않으며 Python 3.14에서 제거될 예정입니다.
사양
Python 초기화를 구성하기 위한 C API 함수와 구조체를 추가합니다:
- 구성 생성:
PyInitConfig는 불투명 구조체입니다.PyInitConfig_Create().PyInitConfig_Free(config).
- 옵션 가져오기:
PyInitConfig_HasOption(config, name).PyInitConfig_GetInt(config, name, &value).PyInitConfig_GetStr(config, name, &value).PyInitConfig_GetStrList(config, name, &length, &items).PyInitConfig_FreeStrList().
- 옵션 설정:
PyInitConfig_SetInt(config, name, value).PyInitConfig_SetStr(config, name, value).PyInitConfig_SetStrList(config, name, length, items).PyInitConfig_AddModule(config, name, initfunc)
- 초기화:
Py_InitializeFromInitConfig(config).
- 오류 처리:
PyInitConfig_GetError(config, &err_msg).PyInitConfig_GetExitcode(config, &exitcode).
현재 런타임 구성을 가져오고 설정하는 C API 함수를 추가합니다:
PyConfig_Get(name).PyConfig_GetInt(name, &value).PyConfig_Set(name).PyConfig_Names()입니다.
C API는 구성 옵션 이름을 참조하기 위해 null로 끝나는 UTF-8 인코딩 문자열을 사용합니다.
이러한 C API 함수는 제한된 C API에서 제외됩니다.
PyInitConfig 구조
PyInitConfig구조는 PyConfig API의 세 구조체를 결합하여 구현되며, inittab 멤버도 포함합니다.
PyPreConfig preconfigPyConfig configPyStatus statusPyInitConfig_AddModule()용struct _inittab *inittab
PyStatus상태는 더 이상 분리되지 않고 통합된 PyInitConfig구조의 일부가 되므로 API를 더 쉽게 사용할 수 있습니다.
구성 옵션
구성 옵션의 이름은 PyPreConfig 및 PyConfig 구조 멤버를 따라 지정합니다. PyPreConfig 문서와 PyConfig 문서를 참조하십시오.
구성 옵션의 사용 중단 및 제거는 PEP의 범위에 포함되지 않으며, 사안별로 논의해야 합니다.
공개 구성 옵션
다음 옵션들은 PyConfig_Get()으로 가져오고 PyConfig_Set()으로 설정할 수 있습니다.
| 옵션 | 타입 | 설명 |
|---|---|---|
argv |
list[str] |
API: sys.argv. |
base_exec_prefix |
str |
API: sys.base_exec_prefix. |
base_executable |
str |
API: sys._base_executable. |
base_prefix |
str |
API: sys.base_prefix. |
bytes_warning |
int |
API: sys.flags.bytes_warning. |
exec_prefix |
str |
API: sys.exec_prefix. |
executable |
str |
API: sys.executable. |
inspect |
bool |
API: sys.flags.inspect (int). |
int_max_str_digits |
int |
API: sys.flags.int_max_str_digits, sys.get_int_max_str_digits() 및 sys.set_int_max_str_digits(). |
interactive |
bool |
API: sys.flags.interactive. |
module_search_paths |
list[str] |
API: sys.path. |
optimization_level |
int |
API: sys.flags.optimize. |
parser_debug |
bool |
API: sys.flags.debug (int). |
platlibdir |
str |
API: sys.platlibdir. |
prefix |
str |
API: sys.base_prefix. |
pycache_prefix |
str |
API: sys.pycache_prefix. |
quiet |
bool |
API: sys.flags.quiet (int). |
stdlib_dir |
str |
API: sys._stdlib_dir. |
use_environment |
bool |
API: sys.flags.ignore_environment (int). |
verbose |
int |
API: sys.flags.verbose. |
warnoptions |
list[str] |
API: sys.warnoptions입니다. |
write_bytecode |
bool |
API: sys.flags.dont_write_bytecode (int)와 sys.dont_write_bytecode (bool)입니다. |
xoptions |
dict[str, str] |
API: sys._xoptions입니다. |
일부 옵션 이름은 optimization_level 옵션과 sys.flags.optimize 속성처럼 sys 속성과 다릅니다. PyConfig_Set()은 해당 sys 속성을 설정합니다.
xoptions는 PyInitConfig에 있는 문자열 목록으로, 각 문자열의 형식은 key (value)은 암묵적으로 True임 또는 key=value입니다. 현재 런타임 구성에서는 이것이 딕셔너리(key: str → value: str | True)가 됩니다.
읽기 전용 구성 옵션
다음 옵션들은 PyConfig_Get()으로 가져올 수 있지만 PyConfig_Set()으로 설정할 수는 없습니다.
| 옵션 | 형식 | 설명 |
|---|---|---|
allocator |
int |
|
buffered_stdio |
bool |
|
check_hash_pycs_mode |
str |
|
code_debug_ranges |
bool |
|
coerce_c_locale |
bool |
|
coerce_c_locale_warn |
bool |
|
configure_c_stdio |
bool |
|
configure_locale |
bool |
|
cpu_count |
int |
API: os.cpu_count() (int | None). |
dev_mode |
bool |
API: sys.flags.dev_mode. |
dump_refs |
bool |
|
dump_refs_file |
str |
|
faulthandler |
bool |
API: faulthandler.is_enabled(). |
filesystem_encoding |
str |
API: sys.getfilesystemencoding(). |
filesystem_errors |
str |
API: sys.getfilesystemencodeerrors(). |
hash_seed |
int |
|
home |
str |
|
import_time |
bool |
|
install_signal_handlers |
bool |
|
isolated |
bool |
API: sys.flags.isolated (int). |
legacy_windows_fs_encoding |
bool |
Windows에서만 사용됩니다. |
legacy_windows_stdio |
bool |
Windows 전용입니다. |
malloc_stats |
bool |
|
orig_argv |
list[str] |
API: sys.orig_argv. |
parse_argv |
bool |
|
pathconfig_warnings |
bool |
|
perf_profiling |
bool |
API: sys.is_stack_trampoline_active(). |
program_name |
str |
|
run_command |
str |
|
run_filename |
str |
|
run_module |
str |
|
run_presite |
str |
디버그 빌드가 필요합니다. |
safe_path |
bool |
|
show_ref_count |
bool |
|
site_import |
bool |
API: sys.flags.no_site(int). |
skip_source_first_line |
bool |
|
stdio_encoding |
str |
API: sys.stdin.encoding, sys.stdout.encoding 및 sys.stderr.encoding입니다. |
stdio_errors |
str |
API: sys.stdin.errors, sys.stdout.errors 및 sys.stderr.errors입니다. |
tracemalloc |
int |
API: tracemalloc.is_tracing() (bool)입니다. |
use_frozen_modules |
bool |
|
use_hash_seed |
bool |
|
user_site_directory |
bool |
API: sys.flags.no_user_site (int)입니다. |
utf8_mode |
bool |
|
warn_default_encoding |
bool |
|
_pystats |
bool |
API: sys._stats_on(), sys._stats_off()입니다. Py_STATS 빌드가 필요합니다. |
Config 생성
PyInitConfig구조체:- Python 사전 초기화와 Python 초기화를 구성하는 불투명 구조체입니다.
PyInitConfig* PyInitConfig_Create(void):- Isolated Configuration의 기본값을 사용하여 새 초기화 구성을 생성합니다.
PyInitConfig_Free()로 해제해야 합니다.메모리 할당에 실패하면
NULL을 반환합니다. void PyInitConfig_Free(PyInitConfig *config):- 초기화 구성의 메모리를 해제합니다.
옵션 가져오기
구성 옵션 name 매개변수는 NULL이 아니며 null로 끝나는 UTF-8 인코딩 문자열이어야 합니다.
int PyInitConfig_HasOption(PyInitConfig *config, const char *name):- 구성에 name이라는 옵션이 있는지 테스트합니다.
옵션이 있으면
1을 반환하고, 그렇지 않으면0을 반환합니다. int PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value):- 정수 구성 옵션을 가져옵니다.
- *value를 설정하고, 성공하면
0을 반환합니다. - 오류 발생 시 config에 오류를 설정하고
-1을 반환합니다.
- *value를 설정하고, 성공하면
int PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value):- 구성 옵션을 널 종료 UTF-8 인코딩 문자열로 가져옵니다.
- *value를 설정하고, 성공하면
0을 반환합니다. - 오류 발생 시 config에 오류를 설정하고
-1을 반환합니다.
성공하면
free(value)로 문자열을 해제해야 합니다. - *value를 설정하고, 성공하면
int PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items):- 문자열 목록 구성 옵션을 널 종료 UTF-8 인코딩 문자열 배열로 가져옵니다.
- *length와 *value를 설정하고, 성공하면
0을 반환합니다. - 오류 발생 시 config에 오류를 설정하고
-1을 반환합니다.
성공하면
PyInitConfig_FreeStrList(length, items)를 사용하여 문자열 목록을 해제해야 합니다. - *length와 *value를 설정하고, 성공하면
void PyInitConfig_FreeStrList(size_t length, char **items):PyInitConfig_GetStrList()로 생성된 문자열 목록의 메모리를 해제합니다.
옵션 설정
구성 옵션 name 매개변수는 NULL이 아닌 널 종료 UTF-8 인코딩 문자열이어야 합니다.
일부 구성 옵션은 다른 옵션에 부수 효과를 줍니다. 이 로직은 Py_InitializeFromInitConfig()가 호출될 때만 구현되며, 아래의 “Set” 함수에서는 구현되지 않습니다. 예를 들어 dev_mode를 1로 설정해도 faulthandler는 1로 설정되지 않습니다.
int PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value):- 정수 구성 옵션을 설정합니다.
- 성공하면
0을 반환합니다. - 오류 발생 시 config에 오류를 설정하고
-1을 반환합니다.
- 성공하면
int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value):- 널 종료 UTF-8 인코딩 문자열에서 문자열 구성 옵션을 설정합니다. 문자열이 복사됩니다.
- 성공하면
0을 반환합니다. - config에 오류를 설정하고 오류 발생 시
-1을 반환합니다.
- 성공하면
int PyInitConfig_SetStrList(PyInitConfig *config, const char *name, size_t length, char * const *items):- 널 문자로 끝나는 UTF-8 인코딩 문자열 배열에서 문자열 목록 구성 옵션을 설정합니다. 문자열 목록을 복사합니다.
- 성공하면
0을 반환합니다. - config에 오류를 설정하고 오류 발생 시
-1을 반환합니다.
- 성공하면
int PyInitConfig_AddModule(PyInitConfig *config, const char *name, PyObject* (*initfunc)(void)):- 내장 확장 모듈을 내장 모듈 테이블에 추가합니다.
새 모듈은 name으로 가져올 수 있으며, 첫 번째 가져오기 시도 시 호출되는 초기화 함수로 initfunc를 사용합니다.
- 성공하면
0을 반환합니다. - config에 오류를 설정하고 오류 발생 시
-1을 반환합니다.
Python이 여러 번 초기화되는 경우 각 Python 초기화 시
PyInitConfig_AddModule()를 호출해야 합니다.PyImport_AppendInittab()함수와 유사합니다. - 성공하면
Python을 초기화합니다.
int Py_InitializeFromInitConfig(PyInitConfig *config):- 초기화 구성에서 Python을 초기화합니다.
- 성공하면
0을 반환합니다. - config에 오류를 설정하고 오류 발생 시
-1을 반환합니다. - Python이 종료하려는 경우 config에 종료 코드를 설정하고
-1을 반환합니다.
종료 코드의 경우
PyInitConfig_GetExitcode()를 참조하십시오. - 성공하면
오류 처리
int PyInitConfig_GetError(PyInitConfig* config, const char **err_msg):- config의 오류 메시지를 가져옵니다.
- 오류가 설정되어 있으면 *err_msg를 설정하고
1을 반환합니다. - 그렇지 않으면 *err_msg를
NULL로 설정하고0을 반환합니다.
오류 메시지는 UTF-8로 인코딩된 문자열입니다.
config에 종료 코드가 있으면 종료 코드를 오류 메시지로 형식화합니다.
오류 메시지는 다른
PyInitConfig함수가 config와 함께 호출될 때까지 유효합니다. 호출자는 오류 메시지를 해제할 필요가 없습니다. - 오류가 설정되어 있으면 *err_msg를 설정하고
int PyInitConfig_GetExitcode(PyInitConfig* config, int *exitcode):- config의 종료 코드를 가져옵니다.
- Python이 종료하려는 경우 *exitcode를 설정하고
1을 반환합니다. - config에 종료 코드가 설정되어 있지 않으면
0을 반환합니다.
parse_argv옵션이 0이 아닌 경우Py_InitializeFromInitConfig()함수만 종료 코드를 설정할 수 있습니다.명령줄 구문 분석에 실패한 경우(종료 코드 2) 또는 명령줄 옵션에서 명령줄 도움말을 표시하도록 요청한 경우(종료 코드 0) 종료 코드를 설정할 수 있습니다.
- Python이 종료하려는 경우 *exitcode를 설정하고
런타임 구성 가져오기 및 설정
구성 옵션의 name 매개변수는 NULL이 아니며 null로 끝나는 UTF-8 인코딩 문자열이어야 합니다.
PyObject* PyConfig_Get(const char *name):- 구성 옵션의 현재 런타임 값을 Python 객체로 가져옵니다.
- 성공하면 새 참조를 반환합니다.
- 오류가 발생하면 예외를 설정하고
NULL을 반환합니다.
객체 타입은 옵션에 따라 달라집니다. Configuration Options 표를 참조하십시오.
다른 옵션은 내부
PyPreConfig및PyConfig구조체에서 가져옵니다.호출자는 GIL을 보유해야 합니다. Python 초기화 전이나 Python 종료 후에는 이 함수를 호출할 수 없습니다.
int PyConfig_GetInt(const char *name, int *value):PyConfig_Get()와 유사하지만 값을 정수로 가져옵니다.*value를 설정하고 성공 시0을 반환합니다.- 오류가 발생하면 예외를 설정하고
-1을 반환합니다.
PyObject* PyConfig_Names(void):- 모든 구성 옵션 이름을
frozenset으로 가져옵니다.오류가 발생하면 예외를 설정하고
NULL을 반환합니다.호출자는 GIL을 보유해야 합니다.
PyObject* PyConfig_Set(const char *name, PyObject *value):- 구성 옵션의 현재 런타임 값을 설정합니다.
- 옵션 name이 없으면
ValueError를 발생시킵니다. - value가 유효하지 않은 값이면
ValueError를 발생시킵니다. - 옵션이 읽기 전용이어서 설정할 수 없으면
ValueError를 발생시킵니다. - value가 올바른 타입이 아니면
TypeError를 발생시킵니다.
Read-only configuration options은 설정할 수 없습니다.
호출자는 GIL을 보유해야 합니다. Python 초기화 전이나 Python 종료 후에는 이 함수를 호출할 수 없습니다.
- 옵션 name이 없으면
안정성
옵션의 동작, 기본 옵션 값 및 Python 동작은 Python 버전마다 변경될 수 있으므로 “안정적”이지 않습니다.
또한 일반적인 PEP 387 폐기 절차에 따라 구성 옵션을 추가하거나 폐기하거나 제거할 수 있습니다.
PyPreConfig 및 PyConfig API와의 상호 작용
하위 수준의 PEP 587 PyPreConfig 및 PyConfig API는 계속 사용할 수 있으며 완전히 지원됩니다. 초록에서 언급한 바와 같이, 대규모 애플리케이션의 일부로 Python 런타임을 제공하는 데 그치기보다는 전체 CPython CLI의 동작을 면밀히 에뮬레이트하려는 임베딩 사용 사례에는 여전히 이러한 API가 선호되는 접근 방식입니다.
PyPreConfig API는 이 PEP의 초기화 API와 함께 사용할 수 있습니다. 이러한 경우 인터프리터가 사전 구성된 후에는 사전 구성 설정에 대한 읽기 전용 및 읽기/쓰기 제한이 PyInitConfig_SetInt에도 PyConfig_Set와 함께 적용됩니다(구체적으로 use_environment 변수만 업데이트할 수 있으며, 다른 사전 구성 변수 중 어느 것이라도 업데이트하려고 하면 오류가 보고됩니다).
예제
Python 초기화
다양한 유형의 구성 옵션을 설정하여 Python을 초기화하고, 오류가 발생하면 -1을 반환하는 예제입니다.
int init_python(void)
{
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("PYTHON INIT ERROR: memory allocation failed\n");
return -1;
}
// Set an integer (dev mode)
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
goto error;
}
// Set a list of UTF-8 strings (argv)
char *argv[] = {"my_program", "-c", "pass"};
if (PyInitConfig_SetStrList(config, "argv",
Py_ARRAY_LENGTH(argv), argv) < 0) {
goto error;
}
// Set a UTF-8 string (program name)
if (PyInitConfig_SetStr(config, "program_name", L"my_program") < 0) {
goto error;
}
// Initialize Python with the configuration
if (Py_InitializeFromInitConfig(config) < 0) {
goto error;
}
PyInitConfig_Free(config);
return 0;
error:
// Display the error message
const char *err_msg;
(void)PyInitConfig_GetError(config, &err_msg);
printf("PYTHON INIT ERROR: %s\n", err_msg);
PyInitConfig_Free(config);
return -1;
}
초기화 bytes_warning 옵션 증가
초기화 구성의 bytes_warning 옵션을 증가시키는 예제입니다.
int config_bytes_warning(PyInitConfig *config)
{
int64_t bytes_warning;
if (PyInitConfig_GetInt(config, "bytes_warning", &bytes_warning)) {
return -1;
}
bytes_warning += 1;
if (PyInitConfig_SetInt(config, "bytes_warning", bytes_warning)) {
return -1;
}
return 0;
}
런타임 verbose 옵션 가져오기
구성 옵션 verbose의 현재 런타임 값을 가져오는 예제입니다.
int get_verbose(void)
{
int verbose;
if (PyConfig_GetInt("verbose", &verbose) < 0) {
// Silently ignore the error
PyErr_Clear();
return -1;
}
return verbose;
}
오류가 발생하면 함수는 오류를 조용히 무시하고 -1을 반환합니다. 실제로는 향후 Python 버전에서 verbose 옵션을 제거하지 않는 한 이 옵션을 가져오는 과정에서 실패할 수 없습니다.
구현
하위 호환성
변경 사항은 완전히 하위 호환됩니다. 새로운 API만 추가됩니다.
PyConfig C API(PEP 587)와 같은 기존 API는 변경되지 않습니다.
거부된 아이디어
텍스트로서의 구성
안정 ABI와 API를 호환시키고 사용자 지정 옵션을 허용하기 위해 구성을 텍스트로 제공하자는 제안이 있었습니다.
예제:
# integer
bytes_warning = 2
# string
filesystem_encoding = "utf8" # comment
# list of strings
argv = ['python', '-c', 'code']
API는 구성 파일이 아니라 문자열로 구성을 받게 됩니다. 가상의 PyInit_SetConfig() 함수를 사용한 예제입니다.
void stable_abi_init_demo(int set_path)
{
PyInit_SetConfig(
"isolated = 1\n"
"argv = ['python', '-c', 'code']\n"
"filesystem_encoding = 'utf-8'\n"
);
if (set_path) {
PyInit_SetConfig("pythonpath = '/my/path'");
}
}
이 예제에서는 읽기 쉽게 만들기 위해 오류 처리를 생략합니다.
문제는 이러한 구성 텍스트를 생성하려면 문자열에 따옴표를 추가하고 문자열 내의 따옴표를 이스케이프해야 한다는 점입니다. 문자열 배열의 형식을 지정하는 일은 간단하지 않게 됩니다.
문자열 또는 문자열 배열을 형식화하는 API를 제공하는 것은 그다지 가치가 없지만, Python은 값을 문자열 또는 문자열 배열로 직접 전달하여 구성 옵션을 설정하는 API를 직접 제공할 수 있습니다. 이렇게 하면 이스케이프해야 하는 개행 문자와 같은 일부 문자에 특별한 의미를 부여하지 않아도 됩니다.
정수로 옵션 참조하기
구성 옵션을 참조하는 데 문자열을 사용하면 문자열을 비교해야 하므로, 정수를 비교하는 것보다 느릴 수 있습니다.
Py_tp_doc와 같은 타입 “슬롯”과 유사하게 정수를 사용하여 구성 옵션을 참조하십시오. const char *name 매개변수는 int option으로 대체됩니다.
정수를 사용할 때 사용자 정의 옵션을 허용하면 충돌이 발생할 가능성이 더 높습니다. 정수 옵션의 “네임스페이스”(범위)를 유지 관리하기가 더 어렵기 때문입니다. 문자열을 사용하면 콜론을 구분자로 하는 간단한 접두사를 사용할 수 있습니다.
정수는 정수 상수 목록도 유지 관리해야 하므로 C API와 Python API를 더 커지게 합니다.
Python 3.13에는 구성 옵션이 약 62개뿐이므로 성능은 실제로 차단 요인이 아닙니다. 나중에 더 나은 성능이 필요해지면 해시 테이블을 사용하여 이름으로 옵션을 가져올 수 있습니다.
구성 옵션 가져오기를 핫 코드에서 사용하는 경우 값을 한 번 읽어 캐시할 수 있습니다. 참고로 대부분의 구성 옵션은 런타임에 변경할 수 없습니다.
다단계 초기화(PEP 432와 유사)
Eric Snow expressed concerns는 이 제안이 임베더에게 초기화가 단일한 거대한 단계라는 생각을 강화할 수 있다는 우려를 표명했습니다. 그는 초기화가 서로 구별되는 5개의 단계를 포함한다고 주장했으며, API가 이를 명시적으로 반영해야 한다고 제안하기까지 했습니다. Eric은 최소한 초기화 구현이 코드 건전성 향상을 위해 부분적으로라도 해당 단계들을 반영해야 한다고 제안했습니다. 전반적으로 그의 설명은 PEP 432 및 PEP 587과 일부 유사점이 있습니다.
이 PEP와 관련된 Eric의 또 다른 핵심 요점은 이상적으로는 Py_InitializeFromConfig()에 전달되는 구성이 해당 함수가 호출되기 전에 완성되어야 하지만, 현재는 초기화 과정에서 실제로 구성이 수정된다는 것이었습니다.
Eric이 반드시 PEP 741의 대안을 제안한 것은 아니지만, 단계를 중심으로 세분화된 초기화 API를 추가하자는 제안은 사실상 이 PEP가 달성하려는 것과 정반대입니다. 이러한 API는 더 복잡하며, 새로운 공개 구조체와 새로운 공개 함수를 추가해야 합니다. 이는 기존 API를 통합하고 더 단순하게 만들려는 이 PEP의 취지와 반대로 Python 초기화를 더 복잡하게 만듭니다. 유사한 목적을 위해 여러 구조체를 사용하면 중복 멤버가 생길 수 있으며, 이는 기존 PyPreConfig와 PyConfig 구조체 간에 중복 멤버가 있는 것과 유사한 문제입니다.
로캘 인코딩 및 와이드 문자열
로캘 인코딩으로 인코딩된 문자열과 와이드 문자열(wchar_t*)을 PyInitConfig API에서 허용하는 것은 PyInitConfig API를 단순하게 유지하고 Python 사전 초기화의 복잡성을 피하기 위해 연기되었습니다. 이러한 기능은 전체 CPython CLI 동작을 에뮬레이트할 때 주로 필요하므로, 하위 수준의 PEP 587 API를 사용하는 것이 더 적합합니다.
논의
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.