CPython 初始化配置 C API 实战指南:PyInitConfig、PyConfig 与 PyPreConfig 全解
本文系统梳理 CPython 官方文档 Doc/c-api/init_config.rst 中定义的三套初始化配置接口:面向嵌入场景的新版 PyInitConfig 不透明结构(PEP 741,Python 3.14 引入)、经典的结构体直填式 PyConfig/PyPreConfig(PEP 587,Python 3.8 引入),以及运行期的 PyConfig_Get/PyConfig_Set 查询接口。读完本文,你将掌握从 C 代码创建、修改、初始化 Python 解释器的完整流程,理解每个配置选项的默认值与来源,并能结合源码验证配置解析的底层实现。
三套 API 的定位与版本
CPython 初始化配置 API 经历了三代演进,Doc/c-api/init_config.rst 把它们组织在同一个文档中:
| API | 引入版本 | 核心结构 | 典型用途 |
|---|---|---|---|
PyInitConfig C API |
3.14(PEP 741) | 不透明指针,键值式读写 | 嵌入式应用,只关心个别选项 |
PyConfig C API |
3.8(PEP 587) | 完整结构体,字段直填 | 定制化 python 解释器 |
运行期 PyConfig_Get/PyConfig_Set |
3.14 | 无结构体,按名查询 | 解释器运行中动态调整 |
PyInitConfig 与 PyConfig 的关键差异在于:前者是不透明结构(typedef struct PyInitConfig PyInitConfig;,见 initconfig.h 第 289 行),调用方通过字符串选项名读写,无法直接访问字段;后者是完整的 struct PyConfig,所有字段可直填。PyInitConfig 创建后默认使用 Isolated Configuration(隔离配置),适合把 Python 嵌入宿主应用且不希望受环境变量、命令行干扰的场景。
PyInitConfig 完整工作流
创建与释放
PyInitConfig_Create() 通过 calloc 分配结构并用 PyPreConfig_InitIsolatedConfig 和 PyConfig_InitIsolatedConfig 填充默认值(见 initconfig.c 第 3999-4010 行)。分配失败返回 NULL,调用方必须检查。PyInitConfig_Free() 接收 NULL 时不做任何操作,因此可以在多个错误分支中无条件调用。
带错误处理的初始化示例
文档给出了一个"始终启用 Python 开发模式"的完整示例,这是理解错误处理模式的最佳入口:
int init_python(void)
{
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("PYTHON INIT ERROR: memory allocation failed\n");
return -1;
}
// 启用 Python Development Mode
if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {
goto error;
}
// 用配置初始化 Python
if (Py_InitializeFromInitConfig(config) < 0) {
goto error;
}
PyInitConfig_Free(config);
return 0;
error:
{
// 注意:goto 不能跳转到变量声明,所以用大括号包一层
const char *err_msg;
(void)PyInitConfig_GetError(config, &err_msg);
printf("PYTHON INIT ERROR: %s\n", err_msg);
PyInitConfig_Free(config);
return -1;
}
}
这个示例展示了三条错误处理路径:创建失败(内存分配错误)、选项设置失败、初始化失败。goto error 后必须用大括号包裹变量声明,因为 C 语言不允许 goto 目标指向变量声明。
错误与退出码
PyInitConfig_GetError(config, &err_msg) 返回 1 表示有错误,*err_msg 指向一个 UTF-8 字符串;返回 0 表示无错误,*err_msg 被设为 NULL。错误消息在下次对同一 config 调用任何 PyInitConfig 函数之前保持有效,调用方无需释放。
PyInitConfig_GetExitCode(config, &exitcode) 用于区分"错误"与"解释器请求退出"两种状态。退出码只会在 parse_argv 选项非零时由 Py_InitializeFromInitConfig() 设置,典型场景:命令行解析失败(退出码 2)或命令行选项要求显示帮助(退出码 0)。从源码实现看(initconfig.c 第 4028-4068 行),PyInitConfig_GetError 在状态为 EXIT 时会动态生成 "exit code N" 格式的错误消息。
选项读写
所有选项名参数必须是"非 NULL 的空终止 UTF-8 编码字符串"。可用的选项名见下方"配置选项参考"章节。
PyInitConfig_HasOption(config, name):返回 1 表示选项存在,0 表示不存在。PyInitConfig_GetInt(config, name, &value):读取整型选项,成功返回 0,失败返回 -1 并在 config 中设置错误。PyInitConfig_GetStr(config, name, &value):读取字符串选项。*value可能为NULL(当选项是可选字符串且未设置时)。成功获取的字符串必须用free(value)释放(因为内部用malloc复制)。PyInitConfig_GetStrList(config, name, &length, &items):读取字符串列表。成功后必须用PyInitConfig_FreeStrList(length, items)释放。PyInitConfig_SetInt(config, name, value):设置整型选项,值范围会做边界检查(如INT_MIN/INT_MAX)。PyInitConfig_SetStr(config, name, value):设置字符串选项,内部会复制字符串,调用方可以安全地释放原字符串。PyInitConfig_SetStrList(config, name, length, items):设置字符串列表,内部会复制整个列表。
一个重要的实现细节:设置 hash_seed 选项时,实现会自动把 use_hash_seed 设为 1(见 initconfig.c 第 4323-4325 行);设置 module_search_paths 时会自动把 module_search_paths_set 设为 1(第 4475-4477 行)。这些"副作用"在 Py_InitializeFromInitConfig() 调用时才会完整体现,Set 函数本身不处理所有跨选项联动——例如设置 dev_mode=1 不会自动设置 faulthandler=1,文档明确说明这一逻辑只在初始化阶段执行。
注册内建模块
PyInitConfig_AddModule(config, name, initfunc) 把一个内建扩展模块添加到内建模块表中,类似于 PyImport_AppendInittab。如果 Python 被多次初始化,必须在每次初始化前调用该函数。从源码实现看(initconfig.c 第 4483-4505 行),内部维护一个 _inittab 数组,每次添加都重新分配并追加一个终止项(name=NULL, initfunc=NULL)。
Py_InitializeFromInitConfig 内部流程
Py_InitializeFromInitConfig() 的内部调用链(initconfig.c 第 4509-4534 行):
- 若
inittab_size >= 1,调用PyImport_ExtendInittab()注册内建模块; - 调用
_PyPreConfig_GetConfig()把PyPreConfig字段同步到PyConfig; - 调用
Py_PreInitializeFromArgs()完成预初始化(设置内存分配器、locale、UTF-8 模式); - 调用
Py_InitializeFromConfig()完成正式初始化。
任一步骤返回异常(error 或 exit)时,函数返回 -1 并在 config 中记录状态。
配置选项参考(PyInitConfig 选项表)
PyInitConfig 的所有可用选项在文档中用一张表列出,每个选项映射到 PyConfig 或 PyPreConfig 的一个结构体成员,并标注了类型和可见性。以下是完整继承的选项表:
| 选项名 | 对应成员 | 类型 | 可见性 |
|---|---|---|---|
"allocator" |
PyPreConfig.allocator |
int |
Read-only |
"argv" |
PyConfig.argv |
list[str] |
Public |
"base_exec_prefix" |
PyConfig.base_exec_prefix |
str |
Public |
"base_executable" |
PyConfig.base_executable |
str |
Public |
"base_prefix" |
PyConfig.base_prefix |
str |
Public |
"buffered_stdio" |
PyConfig.buffered_stdio |
bool |
Read-only |
"bytes_warning" |
PyConfig.bytes_warning |
int |
Public |
"check_hash_pycs_mode" |
PyConfig.check_hash_pycs_mode |
str |
Read-only |
"code_debug_ranges" |
PyConfig.code_debug_ranges |
bool |
Read-only |
"coerce_c_locale" |
PyPreConfig.coerce_c_locale |
bool |
Read-only |
"coerce_c_locale_warn" |
PyPreConfig.coerce_c_locale_warn |
bool |
Read-only |
"configure_c_stdio" |
PyConfig.configure_c_stdio |
bool |
Read-only |
"configure_locale" |
PyPreConfig.configure_locale |
bool |
Read-only |
"cpu_count" |
PyConfig.cpu_count |
int |
Public |
"dev_mode" |
PyConfig.dev_mode |
bool |
Read-only |
"dump_refs" |
PyConfig.dump_refs |
bool |
Read-only |
"dump_refs_file" |
PyConfig.dump_refs_file |
str |
Read-only |
"exec_prefix" |
PyConfig.exec_prefix |
str |
Public |
"executable" |
PyConfig.executable |
str |
Public |
"faulthandler" |
PyConfig.faulthandler |
bool |
Read-only |
"filesystem_encoding" |
PyConfig.filesystem_encoding |
str |
Read-only |
"filesystem_errors" |
PyConfig.filesystem_errors |
str |
Read-only |
"hash_seed" |
PyConfig.hash_seed |
int |
Read-only |
"home" |
PyConfig.home |
str |
Read-only |
"import_time" |
PyConfig.import_time |
int |
Read-only |
"inspect" |
PyConfig.inspect |
bool |
Public |
"install_signal_handlers" |
PyConfig.install_signal_handlers |
bool |
Read-only |
"int_max_str_digits" |
PyConfig.int_max_str_digits |
int |
Public |
"interactive" |
PyConfig.interactive |
bool |
Public |
"isolated" |
PyConfig.isolated |
bool |
Read-only |
"legacy_windows_fs_encoding" |
PyPreConfig.legacy_windows_fs_encoding |
bool |
Read-only |
"legacy_windows_stdio" |
PyPreConfig.legacy_windows_stdio |
bool |
Read-only |
"malloc_stats" |
PyConfig.malloc_stats |
bool |
Read-only |
"module_search_paths" |
PyConfig.module_search_paths |
list[str] |
Public |
"optimization_level" |
PyConfig.optimization_level |
int |
Public |
"orig_argv" |
PyConfig.orig_argv |
list[str] |
Read-only |
"parse_argv" |
PyConfig.parse_argv |
bool |
Read-only |
"parser_debug" |
PyConfig.parser_debug |
bool |
Public |
"pathconfig_warnings" |
PyConfig.pathconfig_warnings |
bool |
Read-only |
"perf_profiling" |
PyConfig.perf_profiling |
bool |
Read-only |
"platlibdir" |
PyConfig.platlibdir |
str |
Public |
"prefix" |
PyConfig.prefix |
str |
Public |
"program_name" |
PyConfig.program_name |
str |
Read-only |
"pycache_prefix" |
PyConfig.pycache_prefix |
str |
Public |
"quiet" |
PyConfig.quiet |
bool |
Public |
"run_command" |
PyConfig.run_command |
str |
Read-only |
"run_filename" |
PyConfig.run_filename |
str |
Read-only |
"run_module" |
PyConfig.run_module |
str |
Read-only |
"run_presite" |
PyConfig.run_presite |
str |
Read-only |
"safe_path" |
PyConfig.safe_path |
bool |
Read-only |
"show_ref_count" |
PyConfig.show_ref_count |
bool |
Read-only |
"site_import" |
PyConfig.site_import |
bool |
Read-only |
"skip_source_first_line" |
PyConfig.skip_source_first_line |
bool |
Read-only |
"stdio_encoding" |
PyConfig.stdio_encoding |
str |
Read-only |
"stdio_errors" |
PyConfig.stdio_errors |
str |
Read-only |
"stdlib_dir" |
PyConfig.stdlib_dir |
str |
Public |
"tracemalloc" |
PyConfig.tracemalloc |
int |
Read-only |
"use_environment" |
PyConfig.use_environment |
bool |
Public |
"use_frozen_modules" |
PyConfig.use_frozen_modules |
bool |
Read-only |
"use_hash_seed" |
PyConfig.use_hash_seed |
bool |
Read-only |
"use_system_logger" |
PyConfig.use_system_logger |
bool |
Read-only |
"user_site_directory" |
PyConfig.user_site_directory |
bool |
Read-only |
"utf8_mode" |
PyPreConfig.utf8_mode |
bool |
Read-only |
"verbose" |
PyConfig.verbose |
int |
Public |
"warn_default_encoding" |
PyConfig.warn_default_encoding |
bool |
Read-only |
"warnoptions" |
PyConfig.warnoptions |
list[str] |
Public |
"write_bytecode" |
PyConfig.write_bytecode |
bool |
Public |
"xoptions" |
PyConfig.xoptions |
dict[str, str] |
Public |
"_pystats" |
PyConfig._pystats |
bool |
Read-only |
可见性含义:
- Public:可通过
PyConfig_Get读取,也可通过PyConfig_Set设置; - Read-only:可通过
PyConfig_Get读取,但不能通过PyConfig_Set设置。
选项名与结构体字段的映射关系由 PYCONFIG_SPEC[] 和 PYPRECONFIG_SPEC[] 两张静态表驱动(initconfig.c 第 118-253 行),使用 offsetof 宏定位字段地址。
运行期配置 API
Python 初始化完成后,可以用 PyConfig_Get/PyConfig_Set 在运行期读写配置选项:
PyConfig_Get(name):返回配置选项的当前值作为 Python 对象(bool/int/str/list[str]/dict[str, str]),成功返回新引用,失败设置异常并返回NULL。PyConfig_GetInt(name, &value):以 Cint形式读取,成功返回 0,失败返回 -1 并设置异常。PyConfig_Names():返回所有配置选项名的frozenset。PyConfig_Set(name, value):设置配置选项,对未知名称、非法值、只读选项分别抛出ValueError,类型不匹配时抛出TypeError。
使用前提:调用线程必须处于"已附加的线程状态"(attached thread state),且在 Python 初始化之后、终结之前。部分选项直接从 sys 模块属性读取(如 "argv" 读自 sys.argv)。PyConfig_Set 还会触发审计事件 cpython.PyConfig_Set。
一个值得注意的版本变化:PyConfig_Set 现在会替换 sys.flags(创建新对象),而不是原地修改。
PyConfig 结构体 API(PEP 587)
两种配置模式
PyConfig 支持两种初始化预设:
-
Python Configuration(
PyConfig_InitPythonConfig):构建一个行为等同于常规python解释器的定制化解释器。环境变量和命令行参数用于配置 Python,但全局配置变量(如Py_IgnoreEnvironmentFlag)被忽略。此模式启用 C locale 强制转换(PEP 538)和 UTF-8 模式(PEP 540),取决于LC_CTYPElocale、PYTHONUTF8和PYTHONCOERCECLOCALE环境变量。 -
Isolated Configuration(
PyConfig_InitIsolatedConfig):把 Python 从系统中隔离,适合嵌入应用。忽略全局配置变量、环境变量、命令行参数(argv不被解析)和用户 site 目录;C 标准流(如stdout)和LC_CTYPElocale 保持不变;不注册信号处理器。
隔离模式示例
文档给出了一个始终运行在隔离模式的完整 main 函数:
int main(int argc, char **argv)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.isolated = 1;
/* 解码命令行参数,隐式预初始化 Python(隔离模式) */
status = PyConfig_SetBytesArgv(&config, argc, argv);
if (PyStatus_Exception(status)) {
goto exception;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
return Py_RunMain();
exception:
PyConfig_Clear(&config);
if (PyStatus_IsExit(status)) {
return status.exitcode;
}
/* 显示错误消息并以非零退出码退出 */
Py_ExitStatusException(status);
}
注意这里先调用 PyConfig_InitPythonConfig 再手动设置 isolated = 1,而不是直接用 PyConfig_InitIsolatedConfig——这是因为需要先解析命令行参数。
PyWideStringList
PyWideStringList 是一个 wchar_t* 字符串列表结构,包含 length(列表长度)和 items(字符串指针数组)两个字段。当 length 非零时,items 必须非 NULL 且所有字符串必须非 NULL。
提供两个操作函数(都需要 Python 已预初始化):
PyWideStringList_Append(list, item):在末尾追加;PyWideStringList_Insert(list, index, item):在index位置插入;若index大于等于列表长度则退化为追加;index必须 >= 0。
PyStatus 状态结构
PyStatus 是初始化函数统一返回的状态类型,可表示三种状态:成功、错误、退出。
typedef struct {
enum {
_PyStatus_TYPE_OK=0,
_PyStatus_TYPE_ERROR=1,
_PyStatus_TYPE_EXIT=2
} _type;
const char *func; // 产生错误的函数名(可为 NULL)
const char *err_msg; // 错误消息
int exitcode; // 退出码
} PyStatus;
创建函数:
PyStatus_Ok():成功;PyStatus_Error(err_msg):带消息的错误(err_msg不能为 NULL);PyStatus_NoMemory():内存分配失败;PyStatus_Exit(exitcode):以指定退出码退出。
处理函数:
PyStatus_Exception(status):判断是否为错误或退出(为真时必须处理);PyStatus_IsError(status):判断是否为错误;PyStatus_IsExit(status):判断是否为退出;Py_ExitStatusException(status):若为退出则调用exit(exitcode);若为错误则打印消息并以非零码退出。只能在上一个函数返回非零时调用。
文档附带一个实用的内存分配示例:
PyStatus alloc(void **ptr, size_t size)
{
*ptr = PyMem_RawMalloc(size);
if (*ptr == NULL) {
return PyStatus_NoMemory();
}
return PyStatus_Ok();
}
int main(int argc, char **argv)
{
void *ptr;
PyStatus status = alloc(&ptr, 16);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
PyMem_Free(ptr);
return 0;
}
注意:Python 内部使用宏设置
PyStatus.func(记录产生错误的函数名),而公开创建函数把func设为NULL。
PyPreConfig 结构体
PyPreConfig 用于预初始化阶段,包含以下字段及默认值:
allocator(int):Python 内存分配器选择。
| 常量 | 值 | 说明 |
|---|---|---|
PYMEM_ALLOCATOR_NOT_SET |
0 | 不更改(使用默认值) |
PYMEM_ALLOCATOR_DEFAULT |
1 | 默认内存分配器 |
PYMEM_ALLOCATOR_DEBUG |
2 | 默认分配器 + 调试钩子 |
PYMEM_ALLOCATOR_MALLOC |
3 | 使用 C 库 malloc() |
PYMEM_ALLOCATOR_MALLOC_DEBUG |
4 | 强制 malloc() + 调试钩子 |
PYMEM_ALLOCATOR_PYMALLOC |
5 | Python pymalloc 分配器 |
PYMEM_ALLOCATOR_PYMALLOC_DEBUG |
6 | pymalloc + 调试钩子 |
PYMEM_ALLOCATOR_MIMALLOC |
6 | mimalloc(文档中编号与 PYMALLOC_DEBUG 重复) |
PYMEM_ALLOCATOR_MIMALLOC_DEBUG |
7 | mimalloc + 调试钩子 |
PYMEM_ALLOCATOR_PYMALLOC* 在 --without-pymalloc 配置下不可用;PYMEM_ALLOCATOR_MIMALLOC* 在 --without-mimalloc 配置或底层原子支持不可用时不可用。默认值:PYMEM_ALLOCATOR_NOT_SET。
configure_locale(int):是否把 LC_CTYPE locale 设为用户首选 locale。设为 0 时同时把 coerce_c_locale 和 coerce_c_locale_warn 设为 0。默认:Python 配置为 1,隔离配置为 0。
coerce_c_locale(int):值为 2 时强制转换 C locale;值为 1 时读取 LC_CTYPE locale 决定是否转换。默认:Python 配置为 -1,隔离配置为 0。
coerce_c_locale_warn(int):非零时,若 C locale 被转换则发出警告。默认:Python 配置为 -1,隔离配置为 0。
dev_mode(int):启用 Python 开发模式。默认:Python 模式为 -1,隔离模式为 0。
isolated(int):隔离模式开关。默认:Python 模式为 0,隔离模式为 1。
legacy_windows_fs_encoding(int):仅 Windows 可用(#ifdef MS_WINDOWS)。非零时:设 utf8_mode=0、filesystem_encoding="mbcs"、filesystem_errors="replace"。由 PYTHONLEGACYWINDOWSFSENCODING 环境变量初始化。默认:0。
parse_argv(int):非零时,Py_PreInitializeFromArgs/Py_PreInitializeFromBytesArgs 会像常规 Python 一样解析 argv。默认:Python 配置为 1,隔离配置为 0。
use_environment(int):是否使用环境变量。默认:Python 配置为 1,隔离配置为 0。
utf8_mode(int):非零时启用 Python UTF-8 模式。由 -X utf8 命令行选项和 PYTHONUTF8 环境变量设为 0 或 1。默认:1。
预初始化流程
预初始化完成三件事:
- 设置 Python 内存分配器(
allocator); - 配置
LC_CTYPElocale(locale encoding); - 设置 Python UTF-8 模式(
utf8_mode)。
当前预配置存储在 _PyRuntime.preconfig 中。提供三个预初始化函数:
Py_PreInitialize(&preconfig);Py_PreInitializeFromBytesArgs(&preconfig, argc, argv):若parse_argv非零则解析字节字符串argv;Py_PreInitializeFromArgs(&preconfig, argc, argv):若parse_argv非零则解析宽字符串argv。
对于 Python Configuration(PyPreConfig_InitPythonConfig),若 Python 带命令行参数初始化,必须把同样的参数传给预初始化,因为它们影响预配置(如编码)。例如 -X utf8 会启用 UTF-8 模式。
预初始化完成后、Py_InitializeFromConfig 之前,可以调用 PyMem_SetAllocator() 安装自定义分配器。若 allocator 设为 PYMEM_ALLOCATOR_NOT_SET,则可以在 Py_PreInitialize 之前调用。
重要约束:PyMem_RawMalloc 等 Python 内存分配函数不得在预初始化之前调用(直接调用 C 库 malloc()/free() 始终安全)。Py_DecodeLocale 也不得在预初始化之前调用。
预初始化启用 UTF-8 模式的示例:
PyStatus status;
PyPreConfig preconfig;
PyPreConfig_InitPythonConfig(&preconfig);
preconfig.utf8_mode = 1;
status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
/* 此时 Python 使用 UTF-8 */
Py_Initialize();
/* ... 使用 Python API ... */
Py_Finalize();
PyConfig 结构体字段详解
PyConfig 是包含大部分 Python 配置参数的结构体,完整定义见 initconfig.h 第 133-245 行。使用完毕后必须调用 PyConfig_Clear() 释放内存。
结构体方法:
PyConfig_InitPythonConfig(&config)/PyConfig_InitIsolatedConfig(&config):用对应预设初始化;PyConfig_SetString(config, &config_str, str):把宽字符字符串复制到目标字段,必要时隐式预初始化;PyConfig_SetBytesString(config, &config_str, str):用Py_DecodeLocale解码字节字符串后设置;PyConfig_SetArgv(config, argc, argv)/PyConfig_SetBytesArgv(config, argc, argv):设置命令行参数;PyConfig_SetWideStringList(config, &list, length, items):设置宽字符串列表;PyConfig_Read(&config):一次性读取所有 Python 配置。已初始化的字段保持不变。自 Python 3.11 起,路径配置字段不再在此函数中计算或修改。argv只被解析一次:解析后parse_argv被设为 2,防止二次解析把应用选项误当作 Python 选项;PyConfig_Clear(&config):释放配置内存。
关键约束:大多数 PyConfig 方法会隐式预初始化。预初始化配置基于 PyConfig,因此与 PyPreConfig 共有的字段(dev_mode、isolated、parse_argv、use_environment)必须在调用任何 PyConfig 方法之前设置。若使用 PyConfig_SetArgv/PyConfig_SetBytesArgv,该调用必须先于其他方法,因为预初始化配置依赖于命令行参数。
核心字段(按类别组织,完整字段列表见源码注释):
-
命令行相关:
argv(sys.argv来源,解析后第一个元素应是脚本文件而非宿主程序)、orig_argv(sys.orig_argv)、parse_argv、run_command(-c值)、run_filename(命令行脚本路径)、run_module(-m值)、run_presite(调试构建专用,-X presite)、skip_source_first_line(-x选项)。 -
路径配置输入:
home(PYTHONHOME)、platlibdir(PYTHONPLATLIBDIR,默认 "lib" 或 Windows 的 "DLLs")、pythonpath_env(PYTHONPATH)、program_name、pathconfig_warnings。 -
路径配置输出:
module_search_paths/module_search_paths_set(sys.path)、stdlib_dir、executable(sys.executable)、base_executable(sys._base_executable,由__PYVENV_LAUNCHER__设置)、prefix/base_prefix、exec_prefix/base_exec_prefix。 -
行为开关:
isolated(隔离模式)、use_environment(是否读环境变量,-E设为 0)、safe_path(-P选项,不向sys.path前置不安全路径)、site_import(-S设为 0,禁用site模块导入)、user_site_directory(-s/-I设为 0)、dev_mode(-X dev/PYTHONDEVMODE)、faulthandler(-X faulthandler/PYTHONFAULTHANDLER)、tracemalloc(-X tracemalloc=N)、perf_profiling(-X perf,值为 1 或 2)、install_signal_handlers、write_bytecode(-B/PYTHONDONTWRITEBYTECODE)、verbose(-v/PYTHONVERBOSE)、quiet(-q)、inspect(-i)、interactive(-i)、optimization_level(-O/PYTHONOPTIMIZE,0/1/2 三级)、parser_debug(-d,需调试构建)、int_max_str_digits(-X int_max_str_digits,默认 -1 表示 4300)、cpu_count(-X cpu_count=N,覆盖os.cpu_count)、buffered_stdio(-u/PYTHONUNBUFFERED)、import_time(-X importtime,值为 1 或 2)、code_debug_ranges(PYTHONNODEBUGRANGES/-X no_debug_ranges)、show_ref_count(-X showrefcount,需Py_REF_DEBUG)、dump_refs(PYTHONDUMPREFS,需--with-trace-refs构建)、malloc_stats(PYTHONMALLOCSTATS)、hash_seed/use_hash_seed(PYTHONHASHSEED)、bytes_warning(-b,计划 3.17 移除)、warn_default_encoding、legacy_windows_stdio(仅 Windows)、use_frozen_modules(PYTHON_FROZEN_MODULES,发布构建默认 1)、use_system_logger(仅 macOS/iOS,iOS 默认 1)、pycache_prefix(-X pycache_prefix=PATH/PYTHONPYCACHEPREFIX)、check_hash_pycs_mode(--check-hash-based-pycs,值 "always"/"never"/"default")、warnoptions(-W/PYTHONWARNINGS)、xoptions(-X选项值)、stdio_encoding/stdio_errors(PYTHONIOENCODING)、filesystem_encoding/filesystem_errors、_pystats(需Py_STATS宏)。
用 PyConfig 初始化解释器
Py_InitializeFromConfig() 接受一个已填充的 PyConfig 结构体。调用方必须用 PyStatus_Exception/Py_ExitStatusException 处理异常。若使用 PyImport_FrozenModules/PyImport_AppendInittab/PyImport_ExtendInittab,必须在预初始化之后、正式初始化之前设置/调用;若 Python 被多次初始化,PyImport_AppendInittab/PyImport_ExtendInittab 必须在每次初始化前调用。
当前配置存储在 PyInterpreterState.config 中。
设置程序名的简洁示例:
void init_python(void)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* 设置程序名,隐式预初始化 Python */
status = PyConfig_SetString(&config, &config.program_name,
L"/path/to/my_program");
if (PyStatus_Exception(status)) {
goto exception;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
return;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
更完整的示例——修改默认配置、读取配置、覆盖部分参数。注意自 3.11 起,许多参数直到初始化时才计算,因此不能从配置结构体中读取:
PyStatus init_python(const char *program_name)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* 在读取配置前设置程序名(从 locale 编码解码字节串),
隐式预初始化 Python */
status = PyConfig_SetBytesString(&config, &config.program_name,
program_name);
if (PyStatus_Exception(status)) {
goto done;
}
/* 一次性读取所有配置 */
status = PyConfig_Read(&config);
if (PyStatus_Exception(status)) {
goto done;
}
/* 显式指定 sys.path */
/* 若要修改默认路径集,先完成初始化,
再用 PySys_GetAttrString("path") */
config.module_search_paths_set = 1;
status = PyWideStringList_Append(&config.module_search_paths,
L"/path/to/stdlib");
if (PyStatus_Exception(status)) {
goto done;
}
status = PyWideStringList_Append(&config.module_search_paths,
L"/path/to/more/modules");
if (PyStatus_Exception(status)) {
goto done;
}
/* 覆盖 PyConfig_Read() 计算的 executable */
status = PyConfig_SetString(&config, &config.executable,
L"/path/to/my_executable");
if (PyStatus_Exception(status)) {
goto done;
}
status = Py_InitializeFromConfig(&config);
done:
PyConfig_Clear(&config);
return status;
}
路径配置机制
PyConfig 包含多组路径配置字段:
输入:home、platlibdir、pathconfig_warnings、program_name、pythonpath_env、当前工作目录(用于取绝对路径)、PATH 环境变量(从 program_name 解析完整路径)、__PYVENV_LAUNCHER__ 环境变量、(仅 Windows)注册表 HKEY_CURRENT_USER/HKEY_LOCAL_MACHINE 下 Software\Python\PythonCore\X.Y\PythonPath 中的应用路径。
输出:base_exec_prefix、base_executable、base_prefix、exec_prefix、executable、module_search_paths_set/module_search_paths、prefix。
若至少一个"输出字段"未设置,Python 会计算路径配置以填充未设置字段。若 module_search_paths_set 为 0,module_search_paths 被覆盖并设 module_search_paths_set 为 1。可以显式设置所有输出字段来完全绕过默认路径计算(字符串非空即视为已设置;module_search_paths 在 module_search_paths_set=1 时视为已设置且不做修改)。把 pathconfig_warnings 设为 0 可抑制路径计算警告(Unix 专用,Windows 本身不产生警告)。base_prefix/base_exec_prefix 未设置时分别继承 prefix/exec_prefix 的值。
配置文件:路径配置使用 pyvenv.cfg、._pth 文件(如 python._pth)、pybuilddir.txt(仅 Unix)。若存在 ._pth 文件:设 isolated=1、use_environment=0、site_import=0、user_site_directory=0(3.15 起)、safe_path=1。若 home 未设置且 pyvenv.cfg 存在于 executable 同目录或其父目录,则 prefix/exec_prefix 被设为该位置,base_prefix/base_exec_prefix 保持指向基础安装。3.14 起 prefix/exec_prefix 直接设为 pyvenv.cfg 所在目录(此前由 site 模块完成,因此受 -S 影响)。
Py_RunMain/Py_Main 会修改 sys.path:若 run_filename 是包含 __main__.py 的目录,前置该目录;若 isolated 为 0:run_module 已设置则前置当前目录;run_filename 已设置则前置脚本所在目录;否则前置空字符串(当前工作目录)。site_import 非零时 site 模块可修改 sys.path;user_site_directory 非零且用户 site 包目录存在时,site 模块追加该目录。
Py_GetArgcArgv()
Py_GetArgcArgv(int *argc, wchar_t ***argv) 获取 Python 修改之前的原始命令行参数,与 PyConfig.orig_argv 对应。
多阶段初始化私有临时 API
这一节介绍 PEP 432 的核心功能——多阶段初始化的私有临时 API:
- "Core" 阶段(最小 Python):内建类型、内建异常、内建与冻结模块、
sys模块仅部分初始化(如sys.path尚不存在); - "Main" 阶段(完全初始化):安装并配置
importlib、应用路径配置、安装信号处理器、完成sys模块初始化(创建sys.stdout、sys.path)、启用faulthandler/tracemalloc等可选功能、导入site模块。
私有临时 API:
PyConfig._init_main:设为 0 时Py_InitializeFromConfig在 "Core" 阶段停止;_Py_InitializeMain():进入 "Main" 阶段,完成 Python 初始化。
"Core" 阶段不导入任何模块,importlib 未配置,路径配置仅在 "Main" 阶段应用。这允许在 Python 中自定义路径配置、安装自定义 sys.meta_path 导入器或导入钩子。此 API 被标记为私有且临时,可能在正式公开 API 设计完成前随时修改或移除。
在 "Core" 与 "Main" 之间运行 Python 代码的示例:
void init_python(void)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
config._init_main = 0;
/* ... 自定义 config 配置 ... */
status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
/* 用 sys.stderr,因为 sys.stdout 只在
_Py_InitializeMain() 中创建 */
int res = PyRun_SimpleString(
"import sys; "
"print('Run Python code before _Py_InitializeMain', "
"file=sys.stderr)");
if (res < 0) {
exit(1);
}
/* ... 此处放更多配置代码 ... */
status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
}
测试用例验证
仓库中的嵌入测试程序 Programs/_testembed.c 包含 test_initconfig_api() 函数(第 1776-1835 行),演示了 PyInitConfig 的完整使用:创建配置、设置 configure_locale/dev_mode/hash_seed/perf_profiling/program_name/pycache_prefix/xoptions 等选项、调用 Py_InitializeFromInitConfig、释放配置,以及在错误分支中获取错误消息。test_initconfig_get_api()(第 1838 行起)进一步验证了 PyInitConfig_HasOption/GetInt/GetStr/GetStrList/SetInt/SetStr/SetStrList 的读写一致性,并验证了 hash_seed 设置后 use_hash_seed 自动变为 1 的联动行为。
源码索引
- 文档原文:Doc/c-api/init_config.rst
PyInitConfig/PyConfig/PyPreConfig结构定义与函数声明:Include/cpython/initconfig.h- 实现与选项规格表:Python/initconfig.c
- 嵌入测试:Programs/_testembed.c
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00