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 Initialization Configuration”은 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 구성 옵션을 가져와야 합니다: 이슈.
2022년에 전역 구성 변수가 폐지 예정으로 지정되었을 때, Marc-André Lemburg는 (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 그래픽.
- 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 (소스).
- nimpy: Nim - Python 브리지.
- PyO3: Python 인터프리터를 위한 Rust 바인딩.
독립 실행형 애플리케이션을 만드는 유틸리티
- macOS용 py2app.
- Windows용 py2exe.
- pyinstaller.
- PyOxidizer: PEP 587 PyConfig API를 사용합니다.
이 유틸리티들은 독립 실행형 애플리케이션을 만들며, libpython에 링크되지 않습니다.
런타임 구성 설정
Marc-André Lemburg는 런타임에 일부 구성 옵션의 값을 설정하는 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는 구성 옵션 이름을 가리킬 때 널 종료 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. |
일부 옵션 이름은 sys 속성과 다릅니다. optimization_level 옵션과 sys.flags.optimize 속성이 그 예입니다. 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 빌드가 필요합니다. |
구성 만들기
PyInitConfig구조체:- Python 사전 초기화와 Python 초기화를 설정하기 위한 불투명 구조체입니다.
PyInitConfig* PyInitConfig_Create(void):- 격리된 구성의 기본값을 사용하여 새 초기화 구성을 만듭니다.
PyInitConfig_Free()로 해제해야 합니다.메모리 할당에 실패하면
NULL을 반환합니다. void PyInitConfig_Free(PyInitConfig *config):- 초기화 구성의 메모리를 해제합니다.
옵션 가져오기
구성 옵션 name 매개변수는 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 인코딩 문자열이어야 합니다.
일부 구성 옵션은 다른 옵션에 부수 효과를 일으킵니다. 이 로직은 아래의 “Set” 함수들이 아니라 Py_InitializeFromInitConfig()가 호출될 때만 적용됩니다. 예를 들어, 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에 종료 코드가 있으면 종료 코드를 오류 메시지 형식으로 만듭니다.
오류 메시지는 config로 다른
PyInitConfig함수가 호출될 때까지 유효합니다. 호출자는 오류 메시지를 해제할 필요가 없습니다. - 오류가 설정되어 있으면 *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이 아니며 널로 끝나는 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와 함께 사용할 수 있습니다. 이 경우 인터프리터가 사전 구성된 후에는 사전 구성 설정에 대한 읽기 전용 대 읽기/쓰기 제한이 PyConfig_Set뿐 아니라 PyInitConfig_SetInt에도 적용됩니다(구체적으로, 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는 변경되지 않습니다.
기각된 아이디어
텍스트로 된 구성
API를 안정 ABI와 호환되게 하고 사용자 정의 옵션을 허용하기 위해 구성을 텍스트로 제공하자는 제안이 있었습니다.
예시:
# 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개뿐이므로 성능은 실제로 큰 걸림돌이 아닙니다. 나중에 더 나은 성능이 필요해지면 해시 테이블을 사용하여 이름으로 옵션을 가져올 수 있습니다.
구성 옵션을 가져오는 작업이 자주 실행되는 코드(hot code)에서 사용된다면, 값을 한 번 읽어 캐시할 수 있습니다. 참고로, 대부분의 구성 옵션은 런타임에 변경할 수 없습니다.
다단계 초기화(PEP 432와 유사)
이 제안이 임베더들에게 초기화가 하나의 일체형 단계라는 생각을 굳힐 수 있다고 Eric Snow가 우려를 표명했습니다. 그는 초기화가 서로 구별되는 5개의 단계로 이루어져 있다고 주장했으며, API가 이를 명시적으로 반영해야 한다고 제안하기까지 했습니다. Eric은 최소한 초기화 구현만이라도 이러한 단계를 반영해야 한다고 제안했으며, 그 이유 중 하나는 코드 건전성 향상이었습니다. 전반적으로 그의 설명은 PEP 432 및 PEP 587과 몇 가지 유사점이 있습니다.
이 PEP와 관련된 Eric의 또 다른 핵심 요점은, 이상적으로는 Py_InitializeFromConfig()에 전달되는 구성이 해당 함수가 호출되기 전에 완성되어 있어야 하지만, 현재는 초기화 과정에서 실제로 구성이 수정된다는 것이었습니다.
Eric이 반드시 PEP 741의 대안을 제안한 것은 아니었지만, 단계별로 세분화된 초기화 API를 추가하자는 제안은 사실상 이 PEP가 달성하려는 목표와 정반대입니다. 그러한 API는 더 복잡하며, 새로운 공개 구조체와 새로운 공개 함수를 추가해야 합니다. 이 PEP는 기존 API를 통합하고 더 단순하게 만들려는 반면, 그러한 API는 오히려 Python 초기화를 더 복잡하게 만듭니다. 비슷한 목적을 가진 구조체가 여러 개 있으면 멤버가 중복될 수 있으며, 이는 기존 PyPreConfig와 PyConfig 구조체 사이의 멤버 중복과 비슷한 문제입니다.
로케일 인코딩과 와이드 문자열
PyInitConfig API를 단순하게 유지하고 Python 사전 초기화의 복잡성을 피하기 위해, PyInitConfig API에서 로케일 인코딩으로 인코딩된 문자열과 와이드 문자열(wchar_t*)을 받는 기능은 보류되었습니다. 또한 이러한 기능은 대부분 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.