PEP 587 – Python 初始化配置
- 作者:
- Victor Stinner <vstinner at python.org>, Alyssa Coghlan <ncoghlan at gmail.com>
- BDFL 委托:
- Thomas Wouters <thomas at python.org>
- 讨论至:
- Python-Dev 列表
- 状态:
- 最终版
- 类型:
- 标准跟踪
- 创建日期:
- 2019 年 3 月 27 日
- Python 版本:
- 3.8
摘要
添加一个新的 C API 来配置 Python 初始化,提供对整个配置更精细的控制和更好的错误报告。
现在可以读取配置,然后在应用之前覆盖一些计算出的参数。还可以完全覆盖 Python 计算模块搜索路径(sys.path
)的方式。
新的隔离配置提供了合理的默认值,用于将 Python 与系统隔离。例如,将 Python 嵌入到应用程序中。使用环境现在是选择加入选项,而不是选择退出选项。例如,环境变量、命令行参数和全局配置变量默认被忽略。
使用新的Py_RunMain()
函数构建一个行为与常规 Python 相同的定制 Python 变得更容易。此外,使用Python 配置,PyConfig.argv
参数现在以常规 Python 解析命令行参数的相同方式进行解析,PyConfig.xoptions
被视为-X opt
命令行选项进行处理。
这从PEP 432开发和重构工作中提取了 API 设计的一个子集,该子集现在被认为足够稳定,可以公开(允许第三方嵌入应用程序访问与原生 CPython CLI 正在使用的相同配置 API)。
基本原理
Python 高度可配置,但其配置是自然演进的。初始化配置分散在代码中,使用不同的方式进行设置:全局配置变量(例如:Py_IsolatedFlag
)、环境变量(例如:PYTHONPATH
)、命令行参数(例如:-b
)、配置文件(例如:pyvenv.cfg
)、函数调用(例如:Py_SetProgramName()
)。需要一种直接可靠的方式来配置 Python。
一些配置参数无法从 C API 访问,或者不容易访问。例如,没有 API 可以覆盖 sys.executable
的默认值。
像 PYTHONPATH
这样的某些选项只能通过环境变量设置,如果不正确地取消设置,会对 Python 子进程产生副作用。
一些选项也依赖于其他选项:请参阅优先级和规则。Python 3.7 API 没有提供整体配置的一致视图。
Python 3.7 初始化 C API 将 wchar_t*
字符串作为输入,而 Python 文件系统编码在初始化期间设置,这可能导致乱码。
Python 3.7 的 API(如 Py_Initialize()
)在内存分配失败时会中止进程,这在 Python 嵌入时很不方便。此外,Py_Main()
可能直接退出进程而不是返回退出代码。提议的新 API 将错误或退出代码报告给调用者,调用者可以决定如何处理它。
在 Python 3.6 中正确实现 PEP 540 (UTF-8 模式) 和新的 -X dev
几乎是不可能的。Python 3.7 和 Python 3.8 对代码库进行了深入的重构,以无副作用的方式将配置读取到结构中。现在可以清除配置(释放内存)并在编码更改时重新读取配置。这需要正确实现 UTF-8 模式,该模式使用 -X utf8
命令行选项更改编码。在内部,字节 argv
字符串从文件系统编码解码。-X dev
更改了内存分配器(行为类似于 PYTHONMALLOC=debug
),而在解析命令行参数时不可能更改内存分配。内部实现的新设计不仅允许正确实现 -X utf8
和 -X dev
,还允许更容易地更改 Python 行为,特别是对于像这样的边缘情况,并确保配置保持一致:请参阅 优先级和规则。
本 PEP 是 PEP 432(整体设计)的部分实现。稍后可以向 PyConfig
结构添加新字段,以完成 PEP 432 的实现(例如,通过添加新的部分初始化 API,允许使用 Python 对象配置 Python 以完成完整的初始化)。但是,这些功能在本 PEP 中被省略,因为即使是原生 CPython CLI 也不是以这种方式工作的——本 PEP 中的公共 API 提案仅限于已实现并作为原生 CPython CLI 中使用的私有 API 采用的功能。
Python 初始化 C API
本 PEP 提议添加以下新结构和函数。
新结构
PyConfig
PyPreConfig
PyStatus
PyWideStringList
新函数
PyConfig_Clear(config)
PyConfig_InitIsolatedConfig()
PyConfig_InitPythonConfig()
PyConfig_Read(config)
PyConfig_SetArgv(config, argc, argv)
PyConfig_SetBytesArgv(config, argc, argv)
PyConfig_SetBytesString(config, config_str, str)
PyConfig_SetString(config, config_str, str)
PyPreConfig_InitIsolatedConfig(preconfig)
PyPreConfig_InitPythonConfig(preconfig)
PyStatus_Error(err_msg)
PyStatus_Exception(status)
PyStatus_Exit(exitcode)
PyStatus_IsError(status)
PyStatus_IsExit(status)
PyStatus_NoMemory()
PyStatus_Ok()
PyWideStringList_Append(list, item)
PyWideStringList_Insert(list, index, item)
Py_BytesMain(argc, argv)
Py_ExitStatusException(status)
Py_InitializeFromConfig(config)
Py_PreInitialize(preconfig)
Py_PreInitializeFromArgs(preconfig, argc, argv)
Py_PreInitializeFromBytesArgs(preconfig, argc, argv)
Py_RunMain()
本 PEP 还将 _PyRuntimeState.preconfig
(PyPreConfig
类型) 和 PyInterpreterState.config
(PyConfig
类型) 字段添加到这些内部结构中。PyInterpreterState.config
成为新的参考配置,取代了全局配置变量和其他私有变量。
PyWideStringList
PyWideStringList
是一个 wchar_t*
字符串列表。
PyWideStringList
结构字段
length
(Py_ssize_t
)items
(wchar_t**
)
方法
PyStatus PyWideStringList_Append(PyWideStringList *list, const wchar_t *item)
: 将 _item_ 附加到 _list_。PyStatus PyWideStringList_Insert(PyWideStringList *list, Py_ssize_t index, const wchar_t *item)
: 在 _list_ 的 _index_ 处插入 _item_。如果 _index_ 大于 _list_ 长度,则将 _item_ 附加到 _list_。
如果 _length_ 不为零,则 _items_ 必须不为 NULL,并且所有字符串必须不为 NULL。
PyStatus
PyStatus
是一种结构,用于存储初始化函数的状:成功、错误或退出。对于错误,它可以存储创建错误的 C 函数名称。
示例
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;
}
PyStatus
字段
exitcode
(int
): 传递给exit()
的参数。err_msg
(const char*
): 错误消息。func
(const char *
): 创建错误的函数名称,可以是NULL
。- 私有
_type
字段:仅供内部使用。
用于创建状态的函数
PyStatus_Ok()
: 成功。PyStatus_Error(err_msg)
: 带有消息的初始化错误。PyStatus_NoMemory()
: 内存分配失败(内存不足)。PyStatus_Exit(exitcode)
: 以指定的退出代码退出 Python。
处理状态的函数
PyStatus_Exception(status)
: 结果是错误还是退出?如果为真,则必须处理异常;例如,通过调用Py_ExitStatusException(status)
。PyStatus_IsError(status)
: 结果是错误吗?PyStatus_IsExit(status)
: 结果是退出吗?Py_ExitStatusException(status)
: 如果 _status_ 是退出,则调用exit(exitcode)
。如果 _status_ 是错误,则打印错误消息并以非零退出代码退出。只有在PyStatus_Exception(status)
为真时才应调用。
注意
在内部,Python 使用设置 PyStatus.func
的宏,而创建状态的函数将 func
设置为 NULL
。
使用 PyPreConfig 进行预初始化
PyPreConfig
结构用于预初始化 Python
- 设置 Python 内存分配器
- 配置 LC_CTYPE 区域设置
- 设置 UTF-8 模式
使用预初始化启用 UTF-8 模式的示例
PyStatus status;
PyPreConfig preconfig;
PyPreConfig_InitPythonConfig(&preconfig);
preconfig.utf8_mode = 1;
status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
/* at this point, Python will speak UTF-8 */
Py_Initialize();
/* ... use Python API here ... */
Py_Finalize();
初始化预配置的函数
PyStatus PyPreConfig_InitIsolatedConfig(PyPreConfig *preconfig)
PyStatus PyPreConfig_InitPythonConfig(PyPreConfig *preconfig)
预初始化 Python 的函数
PyStatus Py_PreInitialize(const PyPreConfig *preconfig)
PyStatus Py_PreInitializeFromBytesArgs(const PyPreConfig *preconfig, int argc, char * const *argv)
PyStatus Py_PreInitializeFromArgs(const PyPreConfig *preconfig, int argc, wchar_t * const * argv)
调用者有责任使用 PyStatus_Exception()
和 Py_ExitStatusException()
处理异常(错误或退出)。
对于Python 配置 (PyPreConfig_InitPythonConfig()
),如果 Python 是用命令行参数初始化的,那么命令行参数也必须传递给预初始化 Python,因为它们对预配置(如编码)有影响。例如,-X utf8
命令行选项启用 UTF-8 模式。
PyPreConfig
字段
allocator
(int
): 内存分配器的名称(例如:PYMEM_ALLOCATOR_MALLOC
)。有效值PYMEM_ALLOCATOR_NOT_SET
(0
): 不改变内存分配器(使用默认值)PYMEM_ALLOCATOR_DEFAULT
(1
): 默认内存分配器PYMEM_ALLOCATOR_DEBUG
(2
): 带有调试钩子的默认内存分配器PYMEM_ALLOCATOR_MALLOC
(3
): 强制使用malloc()
PYMEM_ALLOCATOR_MALLOC_DEBUG
(4
): 强制使用带有调试钩子的malloc()
PYMEM_ALLOCATOR_PYMALLOC
(5
): Python “pymalloc” 分配器PYMEM_ALLOCATOR_PYMALLOC_DEBUG
(6
): 带有调试钩子的 Python “pymalloc” 分配器- 注意:如果 Python 使用
--without-pymalloc
配置,则不支持PYMEM_ALLOCATOR_PYMALLOC
和PYMEM_ALLOCATOR_PYMALLOC_DEBUG
configure_locale
(int
): 将 LC_CTYPE 区域设置为用户首选区域?如果等于 0,则将coerce_c_locale
和coerce_c_locale_warn
设置为 0。coerce_c_locale
(int
): 如果等于 2,则强制 C 区域设置;如果等于 1,则读取 LC_CTYPE 区域设置以决定是否应强制。coerce_c_locale_warn
(int
): 如果非零,则在强制 C 区域设置时发出警告。dev_mode
(int
): 请参阅PyConfig.dev_mode
。isolated
(int
): 请参阅PyConfig.isolated
。legacy_windows_fs_encoding
(int
, 仅限 Windows): 如果非零,则禁用 UTF-8 模式,将 Python 文件系统编码设置为mbcs
,将文件系统错误处理程序设置为replace
。parse_argv
(int
): 如果非零,Py_PreInitializeFromArgs()
和Py_PreInitializeFromBytesArgs()
以与常规 Python 解析命令行参数相同的方式解析其argv
参数:请参阅 命令行参数。use_environment
(int
): 请参阅PyConfig.use_environment
。utf8_mode
(int
): 如果非零,则启用 UTF-8 模式。
legacy_windows_fs_encoding
字段仅在 Windows 上可用。#ifdef MS_WINDOWS
宏可用于 Windows 特定的代码。
PyPreConfig
私有字段,仅供内部使用
_config_init
(int
): 用于初始化PyConfig
的函数,用于预初始化。
PyMem_SetAllocator()
可以在 Py_PreInitialize()
之后和 Py_InitializeFromConfig()
之前调用,以安装自定义内存分配器。如果 allocator
设置为 PYMEM_ALLOCATOR_NOT_SET
(默认值),则可以在 Py_PreInitialize()
之前调用。
Python 内存分配函数(如 PyMem_RawMalloc()
)在 Python 预初始化之前不得使用,而直接调用 malloc()
和 free()
始终是安全的。Py_DecodeLocale()
在预初始化之前不得调用。
使用 PyConfig 进行初始化
PyConfig
结构包含配置 Python 的大多数参数。
设置程序名称的示例
void init_python(void)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Set the program name. Implicitly preinitialize Python. */
status = PyConfig_SetString(&config, &config.program_name,
L"/path/to/my_program");
if (PyStatus_Exception(status)) {
goto fail;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto fail;
}
PyConfig_Clear(&config);
return;
fail:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
PyConfig
方法
void PyConfig_InitPythonConfig(PyConfig *config)
使用Python 配置初始化配置。void PyConfig_InitIsolatedConfig(PyConfig *config)
: 使用隔离配置初始化配置。PyStatus PyConfig_SetString(PyConfig *config, wchar_t * const *config_str, const wchar_t *str)
: 将宽字符字符串 _str_ 复制到*config_str
。如果需要,预初始化 Python。PyStatus PyConfig_SetBytesString(PyConfig *config, wchar_t * const *config_str, const char *str)
: 使用Py_DecodeLocale()
解码 _str_ 并将结果设置到*config_str
。如果需要,预初始化 Python。PyStatus PyConfig_SetArgv(PyConfig *config, int argc, wchar_t * const *argv)
: 从宽字符字符串设置命令行参数。如果需要,预初始化 Python。PyStatus PyConfig_SetBytesArgv(PyConfig *config, int argc, char * const *argv)
: 设置命令行参数:使用Py_DecodeLocale()
解码字节。如果需要,预初始化 Python。PyStatus PyConfig_Read(PyConfig *config)
: 读取所有 Python 配置。已经初始化的字段保持不变。如果需要,预初始化 Python。void PyConfig_Clear(PyConfig *config)
: 释放配置内存。
大多数 PyConfig
方法会在需要时预初始化 Python。在这种情况下,Python 预初始化配置基于 PyConfig
。如果与 PyPreConfig
共同的配置字段被调整,它们必须在调用 PyConfig
方法之前设置
dev_mode
isolated
parse_argv
use_environment
此外,如果使用了 PyConfig_SetArgv()
或 PyConfig_SetBytesArgv()
,则此方法必须首先调用,在其他方法之前,因为预初始化配置依赖于命令行参数(如果 parse_argv
非零)。
初始化 Python 的函数
PyStatus Py_InitializeFromConfig(const PyConfig *config)
: 从 _config_ 配置初始化 Python。
这些方法和函数的调用者有责任使用 PyStatus_Exception()
和 Py_ExitStatusException()
处理异常(错误或退出)。
PyConfig
字段
argv
(PyWideStringList
): 命令行参数,sys.argv
。请参阅parse_argv
以与常规 Python 解析 Python 命令行参数相同的方式解析argv
。如果argv
为空,则添加一个空字符串以确保sys.argv
始终存在且不为空。base_exec_prefix
(wchar_t*
):sys.base_exec_prefix
。base_prefix
(wchar_t*
):sys.base_prefix
。buffered_stdio
(int
): 如果等于 0,则启用无缓冲模式,使 stdout 和 stderr 流无缓冲。bytes_warning
(int
): 如果等于 1,则在比较bytes
或bytearray
与str
,或比较bytes
与int
时发出警告。如果等于或大于 2,则引发BytesWarning
异常。check_hash_pycs_mode
(wchar_t*
):--check-hash-based-pycs
命令行选项值(请参阅 PEP 552)。有效值:always
、never
和default
。默认值为default
。configure_c_stdio
(int
): 如果非零,则配置 C 标准流(stdio
、stdout
、stdout
)。例如,在 Windows 上将其模式设置为O_BINARY
。dev_mode
(int
): 开发模式dump_refs
(int
): 如果非零,则在退出时转储所有仍然存活的对象。需要使用定义了Py_REF_DEBUG
宏的特殊 Python 构建。exec_prefix
(wchar_t*
):sys.exec_prefix
。executable
(wchar_t*
):sys.executable
。faulthandler
(int
): 如果非零,则调用faulthandler.enable()
。filesystem_encoding
(wchar_t*
): 文件系统编码,sys.getfilesystemencoding()
。filesystem_errors
(wchar_t*
): 文件系统编码错误,sys.getfilesystemencodeerrors()
。use_hash_seed
(int
),hash_seed
(unsigned long
): 随机哈希函数种子。home
(wchar_t*
): Python 主目录。import_time
(int
): 如果非零,则分析导入时间。inspect
(int
): 执行脚本或命令后进入交互模式。install_signal_handlers
(int
): 安装信号处理程序?interactive
(int
): 交互模式。isolated
(int
): 如果大于 0,则启用隔离模式sys.path
既不包含脚本目录(从argv[0]
或当前目录计算),也不包含用户 site-packages 目录。- Python REPL 不导入
readline
,也不在交互式提示符下启用默认 readline 配置。 - 将
use_environment
和user_site_directory
设置为 0。
legacy_windows_stdio
(int
, 仅限 Windows): 如果非零,则对sys.stdin
、sys.stdout
和sys.stderr
使用io.FileIO
而不是WindowsConsoleIO
。malloc_stats
(int
): 如果非零,则在退出时转储pymalloc
内存分配器的统计信息。如果 Python 使用--without-pymalloc
构建,则忽略此选项。pythonpath_env
(wchar_t*
): 模块搜索路径,作为由 DELIM(通常是:
字符)分隔的字符串。默认情况下从PYTHONPATH
环境变量值初始化。module_search_paths_set
(int
),module_search_paths
(PyWideStringList
):sys.path
。如果module_search_paths_set
等于 0,则module_search_paths
将被计算路径配置的函数覆盖。optimization_level
(int
): 编译优化级别- 0: 窥孔优化器(并且
__debug__
设置为True
) - 1: 删除断言,将
__debug__
设置为False
- 2: 剥离文档字符串
- 0: 窥孔优化器(并且
parse_argv
(int
): 如果非零,则以与常规 Python 命令行参数相同的方式解析argv
,并从argv
中剥离 Python 参数:请参阅 命令行参数。parser_debug
(int
): 如果非零,则打开解析器调试输出(仅限专家,取决于编译选项)。pathconfig_warnings
(int
): 如果等于 0,则在计算路径配置时禁止警告(仅限 Unix,Windows 不记录任何警告)。否则,警告将写入 stderr。prefix
(wchar_t*
):sys.prefix
。program_name
(wchar_t*
): 程序名称。pycache_prefix
(wchar_t*
):.pyc
缓存前缀。quiet
(int
): 静默模式。例如,即使在交互模式下也不显示版权和版本信息。run_command
(wchar_t*
):python3 -c COMMAND
参数。run_filename
(wchar_t*
):python3 FILENAME
参数。run_module
(wchar_t*
):python3 -m MODULE
参数。show_alloc_count
(int
): 在退出时显示分配计数?需要使用定义了COUNT_ALLOCS
宏的特殊 Python 构建。show_ref_count
(int
): 在退出时显示总引用计数?需要 Python 的调试构建(应定义Py_REF_DEBUG
宏)。site_import
(int
): 在启动时导入site
模块?skip_source_first_line
(int
): 跳过源代码的第一行?stdio_encoding
(wchar_t*
),stdio_errors
(wchar_t*
):sys.stdin
,sys.stdout
和sys.stderr
的编码和编码错误。tracemalloc
(int
): 如果非零,则调用tracemalloc.start(value)
。user_site_directory
(int
): 如果非零,则将用户站点目录添加到sys.path
。verbose
(int
): 如果非零,则启用详细模式。warnoptions
(PyWideStringList
):warnings
模块的选项,用于构建警告过滤器。write_bytecode
(int
): 如果非零,则写入.pyc
文件。xoptions
(PyWideStringList
):sys._xoptions
。
legacy_windows_stdio
字段仅在 Windows 上可用。#ifdef MS_WINDOWS
宏可用于 Windows 特定的代码。
如果 parse_argv
非零,则 argv
参数以与常规 Python 解析命令行参数相同的方式进行解析,并且 Python 参数将从 argv
中剥离:请参阅 命令行参数。
xoptions
选项被解析以设置其他选项:请参阅 -X 选项。
PyConfig
私有字段,仅供内部使用
_config_init
(int
): 用于初始化PyConfig
的函数,用于预初始化。_install_importlib
(int
): 安装 importlib?_init_main
(int
): 如果等于 0,则在“main”阶段之前停止 Python 初始化(请参阅 PEP 432)。
更完整的示例,修改默认配置,读取配置,然后覆盖一些参数
PyStatus init_python(const char *program_name)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Set the program name before reading the configuration
(decode byte string from the locale encoding).
Implicitly preinitialize Python. */
status = PyConfig_SetBytesString(&config, &config.program_name,
program_name);
if (PyStatus_Exception(status)) {
goto done;
}
/* Read all configuration at once */
status = PyConfig_Read(&config);
if (PyStatus_Exception(status)) {
goto done;
}
/* Append our custom search path to sys.path */
status = PyWideStringList_Append(&config.module_search_paths,
L"/path/to/more/modules");
if (PyStatus_Exception(status)) {
goto done;
}
/* Override executable computed by PyConfig_Read() */
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;
}
注意
PyImport_FrozenModules
, PyImport_AppendInittab()
和 PyImport_ExtendInittab()
函数仍然相关,并继续像以前一样工作。它们应该在 Python 预初始化之后和 Python 初始化之前设置或调用。
隔离配置
PyPreConfig_InitIsolatedConfig()
和 PyConfig_InitIsolatedConfig()
函数创建配置以将 Python 与系统隔离。例如,将 Python 嵌入到应用程序中。
此配置忽略全局配置变量、环境变量和命令行参数(argv
不被解析)。C 标准流(例如:stdout
)和 LC_CTYPE 区域设置默认保持不变。
配置文件仍用于此配置。设置路径配置(“输出字段”)以忽略这些配置文件并避免计算默认路径配置的函数。
Python 配置
PyPreConfig_InitPythonConfig()
和 PyConfig_InitPythonConfig()
函数创建用于构建行为与常规 Python 相同的定制 Python 的配置。
环境变量和命令行参数用于配置 Python,而全局配置变量被忽略。
此函数根据 LC_CTYPE 区域设置、PYTHONUTF8
和 PYTHONCOERCECLOCALE
环境变量启用 C 区域强制转换 (PEP 538) 和 UTF-8 模式 (PEP 540)。
始终在隔离模式下运行的自定义 Python 示例
int main(int argc, char **argv)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
config.isolated = 1;
/* Decode command line arguments.
Implicitly preinitialize Python (in isolated mode). */
status = PyConfig_SetBytesArgv(&config, argc, argv);
if (PyStatus_Exception(status)) {
goto fail;
}
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto fail;
}
PyConfig_Clear(&config);
return Py_RunMain();
fail:
PyConfig_Clear(&config);
if (PyStatus_IsExit(status)) {
return status.exitcode;
}
/* Display the error message and exit the process with
non-zero exit code */
Py_ExitStatusException(status);
}
此示例是 PEP 432 中讨论的“系统 Python 可执行文件”的基本实现。
路径配置
PyConfig
包含多个路径配置字段
- 路径配置输入字段
home
pythonpath_env
pathconfig_warnings
- 路径配置输出字段
exec_prefix
executable
prefix
module_search_paths_set
,module_search_paths
如果至少一个“输出字段”未设置,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
继承其值。
Py_RunMain()
和 Py_Main()
修改 sys.path
- 如果
run_filename
已设置且是包含__main__.py
脚本的目录,则将run_filename
添加到sys.path
的开头。 - 如果
isolated
为零- 如果
run_module
已设置,则将当前目录添加到sys.path
的开头。如果无法读取当前目录,则不执行任何操作。 - 如果
run_filename
已设置,则将文件名所在的目录添加到sys.path
的开头。 - 否则,将一个空字符串添加到
sys.path
的开头。
- 如果
如果 site_import
非零,则 sys.path
可由 site
模块修改。如果 user_site_directory
非零且用户 site-package 目录存在,则 site
模块将用户 site-package 目录附加到 sys.path
。
另请参阅路径配置使用的配置文件。
Py_BytesMain()
Python 3.7 提供了一个高级 Py_Main()
函数,该函数要求将命令行参数作为 wchar_t*
字符串传递。使用正确的编码解码字节并非易事。Python 在 C 区域强制转换和 UTF-8 模式方面有其自身的一系列问题。
本 PEP 添加了一个新的 Py_BytesMain()
函数,它将命令行参数作为字节
int Py_BytesMain(int argc, char **argv)
Py_RunMain()
新的 Py_RunMain()
函数执行命令行或配置中指定的命令 (PyConfig.run_command
)、脚本 (PyConfig.run_filename
) 或模块 (PyConfig.run_module
),然后完成 Python。它返回一个可以传递给 exit()
函数的退出状态。
int Py_RunMain(void);
有关使用 Py_RunMain()
始终在隔离模式下运行的定制 Python 示例,请参阅Python 配置。
多阶段初始化私有临时 API
本节是引入多阶段初始化的私有临时 API,这是 PEP 432 的核心功能
- “核心”初始化阶段,“最少 Python”
- 内置类型;
- 内置异常;
- 内置和冻结模块;
sys
模块仅部分初始化(例如:sys.path
尚不存在);
- “主”初始化阶段,Python 完全初始化
- 安装并配置
importlib
; - 应用路径配置;
- 安装信号处理程序;
- 完成
sys
模块初始化(例如:创建sys.stdout
和sys.path
); - 启用可选功能,如
faulthandler
和tracemalloc
; - 导入
site
模块; - 等等。
- 安装并配置
私有临时 API
PyConfig._init_main
: 如果设置为 0,Py_InitializeFromConfig()
在“Core”初始化阶段停止。PyStatus _Py_InitializeMain(void)
: 移动到“主”初始化阶段,完成 Python 初始化。
在“核心”阶段不导入任何模块,并且 importlib
模块未配置:路径配置仅在“主”阶段应用。这可能允许在 Python 中自定义 Python,以覆盖或调整路径配置,或许安装自定义 sys.meta_path 导入器或导入钩子等。
在核心阶段之后和主阶段之前,在 Python 中计算路径配置成为可能,这是 PEP 432 的一个动机。
“核心”阶段尚未明确定义:在该阶段应该可用和不应该可用的内容尚未指定。该 API 被标记为私有和临时:在设计出适当的公共 API 之前,该 API 可以随时修改甚至删除。
在“核心”和“主”初始化阶段之间运行 Python 代码的示例
void init_python(void)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
config._init_main = 0;
/* ... customize 'config' configuration ... */
status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
/* Use sys.stderr because sys.stdout is only created
by _Py_InitializeMain() */
int res = PyRun_SimpleString(
"import sys; "
"print('Run Python code before _Py_InitializeMain', "
"file=sys.stderr)");
if (res < 0) {
exit(1);
}
/* ... put more configuration code here ... */
status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
}
向后兼容性
本 PEP 只添加了一个新的 API:它保持现有 API 不变,对向后兼容性没有影响。
Python 3.7 的 Py_Initialize()
函数现在默认禁用 C 区域设置强制转换 (PEP 538) 和 UTF-8 模式 (PEP 540) 以防止乱码。需要使用Python 配置的新 API 来自动启用它们。
附件
Python 和隔离配置的比较
PyPreConfig_InitPythonConfig()
和 PyPreConfig_InitIsolatedConfig()
之间的差异
PyPreConfig | Python | 隔离 |
---|---|---|
coerce_c_locale_warn |
-1 | 0 |
coerce_c_locale |
-1 | 0 |
configure_locale |
1 | 0 |
dev_mode |
-1 | 0 |
isolated |
0 | 1 |
legacy_windows_fs_encoding |
-1 | 0 |
use_environment |
0 | 0 |
parse_argv |
1 | 0 |
utf8_mode |
-1 | 0 |
PyConfig_InitPythonConfig()
和 PyConfig_InitIsolatedConfig()
之间的差异
PyConfig | Python | 隔离 |
---|---|---|
configure_c_stdio |
1 | 0 |
install_signal_handlers |
1 | 0 |
isolated |
0 | 1 |
parse_argv |
1 | 0 |
pathconfig_warnings |
1 | 0 |
use_environment |
1 | 0 |
user_site_directory |
1 | 0 |
优先级和规则
配置参数的优先级,从高到低
PyConfig
PyPreConfig
- 配置文件
- 命令行选项
- 环境变量
- 全局配置变量
警告选项的优先级,从高到低
PyConfig.warnoptions
PySys_AddWarnOption()
PyConfig.bytes_warning
(如果大于 1 则添加"error::BytesWarning"
过滤器,如果等于 1 则添加"default::BytesWarning
过滤器)-W opt
命令行参数PYTHONWARNINGS
环境变量PyConfig.dev_mode
(添加"default"
过滤器)
关于 PyConfig
参数的规则
- 如果
isolated
非零,则use_environment
和user_site_directory
设置为 0。 - 如果
dev_mode
非零,则allocator
设置为"debug"
,faulthandler
设置为 1,并且"default"
过滤器添加到warnoptions
。但是PYTHONMALLOC
环境变量在设置内存分配器方面优先于dev_mode
。 - 如果
base_prefix
未设置,它将继承prefix
的值。 - 如果
base_exec_prefix
未设置,它将继承exec_prefix
的值。 - 如果
python._pth
配置文件存在,则isolated
设置为 1 且site_import
设置为 0;但如果python._pth
包含import site
,则site_import
设置为 1。
关于 PyConfig
和 PyPreConfig
参数的规则
- 如果
PyPreConfig.legacy_windows_fs_encoding
非零,则将PyPreConfig.utf8_mode
设置为 0,将PyConfig.filesystem_encoding
设置为mbcs
,并将PyConfig.filesystem_errors
设置为replace
。
配置文件
由路径配置使用的 Python 配置文件
pyvenv.cfg
python._pth
(仅限 Windows)pybuilddir.txt
(仅限 Unix)
全局配置变量
映射到 PyPreConfig
字段的全局配置变量
变量 | 字段 |
---|---|
Py_IgnoreEnvironmentFlag |
use_environment (NOT) |
Py_IsolatedFlag |
isolated |
Py_LegacyWindowsFSEncodingFlag |
legacy_windows_fs_encoding |
Py_UTF8Mode |
utf8_mode |
(NOT) 表示 PyPreConfig
值与全局配置变量值相反。Py_LegacyWindowsFSEncodingFlag
仅在 Windows 上可用。
映射到 PyConfig
字段的全局配置变量
变量 | 字段 |
---|---|
Py_BytesWarningFlag |
bytes_warning |
Py_DebugFlag |
parser_debug |
Py_DontWriteBytecodeFlag |
write_bytecode (NOT) |
Py_FileSystemDefaultEncodeErrors |
filesystem_errors |
Py_FileSystemDefaultEncoding |
filesystem_encoding |
Py_FrozenFlag |
pathconfig_warnings (NOT) |
Py_HasFileSystemDefaultEncoding |
filesystem_encoding |
Py_HashRandomizationFlag |
use_hash_seed, hash_seed |
Py_IgnoreEnvironmentFlag |
use_environment (NOT) |
Py_InspectFlag |
inspect |
Py_InteractiveFlag |
interactive |
Py_IsolatedFlag |
isolated |
Py_LegacyWindowsStdioFlag |
legacy_windows_stdio |
Py_NoSiteFlag |
site_import (NOT) |
Py_NoUserSiteDirectory |
user_site_directory (NOT) |
Py_OptimizeFlag |
optimization_level |
Py_QuietFlag |
quiet |
Py_UnbufferedStdioFlag |
buffered_stdio (NOT) |
Py_VerboseFlag |
verbose |
_Py_HasFileSystemDefaultEncodeErrors |
filesystem_errors |
(NOT) 表示 PyConfig
值与全局配置变量值相反。Py_LegacyWindowsStdioFlag
仅在 Windows 上可用。
命令行参数
用法
python3 [options]
python3 [options] -c COMMAND
python3 [options] -m MODULE
python3 [options] SCRIPT
映射到 PyPreConfig
字段的伪操作的命令行选项
选项 | PyConfig 字段 |
---|---|
-E |
use_environment = 0 |
-I |
isolated = 1 |
-X dev |
dev_mode = 1 |
-X utf8 |
utf8_mode = 1 |
-X utf8=VALUE |
utf8_mode = VALUE |
映射到 PyConfig
字段的伪操作的命令行选项
选项 | PyConfig 字段 |
---|---|
-b |
bytes_warning++ |
-B |
write_bytecode = 0 |
-c COMMAND |
run_command = COMMAND |
--check-hash-based-pycs=MODE |
check_hash_pycs_mode = MODE |
-d |
parser_debug++ |
-E |
use_environment = 0 |
-i |
inspect++ 和 interactive++ |
-I |
isolated = 1 |
-m MODULE |
run_module = MODULE |
-O |
optimization_level++ |
-q |
quiet++ |
-R |
use_hash_seed = 0 |
-s |
user_site_directory = 0 |
-S |
site_import |
-t |
忽略(为向后兼容性保留) |
-u |
buffered_stdio = 0 |
-v |
verbose++ |
-W WARNING |
将 WARNING 添加到 warnoptions |
-x |
skip_source_first_line = 1 |
-X OPTION |
将 OPTION 添加到 xoptions |
-h
、-?
和 -V
选项在没有 PyConfig
的情况下处理。
-X 选项
-X 选项映射到 PyConfig
字段的伪操作
选项 | PyConfig 字段 |
---|---|
-X dev |
dev_mode = 1 |
-X faulthandler |
faulthandler = 1 |
-X importtime |
import_time = 1 |
-X pycache_prefix=PREFIX |
pycache_prefix = PREFIX |
-X showalloccount |
show_alloc_count = 1 |
-X showrefcount |
show_ref_count = 1 |
-X tracemalloc=N |
tracemalloc = N |
环境变量
映射到 PyPreConfig
字段的环境变量
变量 | PyPreConfig 字段 |
---|---|
PYTHONCOERCECLOCALE |
coerce_c_locale, coerce_c_locale_warn |
PYTHONDEVMODE |
dev_mode |
PYTHONLEGACYWINDOWSFSENCODING |
legacy_windows_fs_encoding |
PYTHONMALLOC |
allocator |
PYTHONUTF8 |
utf8_mode |
映射到 PyConfig
字段的环境变量
变量 | PyConfig 字段 |
---|---|
PYTHONDEBUG |
parser_debug |
PYTHONDEVMODE |
dev_mode |
PYTHONDONTWRITEBYTECODE |
write_bytecode |
PYTHONDUMPREFS |
dump_refs |
PYTHONEXECUTABLE |
program_name |
PYTHONFAULTHANDLER |
faulthandler |
PYTHONHASHSEED |
use_hash_seed, hash_seed |
PYTHONHOME |
home |
PYTHONINSPECT |
inspect |
PYTHONIOENCODING |
stdio_encoding, stdio_errors |
PYTHONLEGACYWINDOWSSTDIO |
legacy_windows_stdio |
PYTHONMALLOCSTATS |
malloc_stats |
PYTHONNOUSERSITE |
user_site_directory |
PYTHONOPTIMIZE |
optimization_level |
PYTHONPATH |
pythonpath_env |
PYTHONPROFILEIMPORTTIME |
import_time |
PYTHONPYCACHEPREFIX, |
pycache_prefix |
PYTHONTRACEMALLOC |
tracemalloc |
PYTHONUNBUFFERED |
buffered_stdio |
PYTHONVERBOSE |
verbose |
PYTHONWARNINGS |
warnoptions |
PYTHONLEGACYWINDOWSFSENCODING
和 PYTHONLEGACYWINDOWSSTDIO
仅适用于 Windows。
默认 Python 配置
PyPreConfig_InitPythonConfig()
:
- allocator = PYMEM_ALLOCATOR_NOT_SET
coerce_c_locale_warn
= -1coerce_c_locale
= -1configure_locale
= 1dev_mode
= -1isolated
= 0legacy_windows_fs_encoding
= -1use_environment
= 1utf8_mode
= -1
PyConfig_InitPythonConfig()
:
argv
= []- base_exec_prefix = NULL
- base_prefix = NULL
buffered_stdio
= 1bytes_warning
= 0- check_hash_pycs_mode = NULL
configure_c_stdio
= 1dev_mode
= 0dump_refs
= 0- exec_prefix = NULL
- executable = NULL
faulthandler
= 0- filesystem_encoding = NULL
- filesystem_errors = NULL
hash_seed
= 0- home = NULL
import_time
= 0inspect
= 0install_signal_handlers
= 1interactive
= 0isolated
= 0malloc_stats
= 0- module_search_path_env = NULL
module_search_paths
= []optimization_level
= 0parse_argv
= 1parser_debug
= 0pathconfig_warnings
= 1prefix
=NULL
program_name
=NULL
pycache_prefix
=NULL
quiet
= 0run_command
=NULL
run_filename
=NULL
run_module
=NULL
show_alloc_count
= 0show_ref_count
= 0site_import
= 1skip_source_first_line
= 0stdio_encoding
=NULL
stdio_errors
=NULL
tracemalloc
= 0use_environment
= 1use_hash_seed
= 0user_site_directory
= 1verbose
= 0warnoptions
= []write_bytecode
= 1xoptions
= []_init_main
= 1_install_importlib
= 1
默认隔离配置
PyPreConfig_InitIsolatedConfig()
:
- allocator = PYMEM_ALLOCATOR_NOT_SET
coerce_c_locale_warn
= 0coerce_c_locale
= 0configure_locale
= 0dev_mode
= 0isolated
= 1legacy_windows_fs_encoding
= 0use_environment
= 0utf8_mode
= 0
PyConfig_InitIsolatedConfig()
:
argv
= []- base_exec_prefix = NULL
- base_prefix = NULL
buffered_stdio
= 1bytes_warning
= 0- check_hash_pycs_mode = NULL
configure_c_stdio
= 0dev_mode
= 0dump_refs
= 0- exec_prefix = NULL
- executable = NULL
faulthandler
= 0- filesystem_encoding = NULL
- filesystem_errors = NULL
hash_seed
= 0- home = NULL
import_time
= 0inspect
= 0install_signal_handlers
= 0interactive
= 0isolated
= 1malloc_stats
= 0- module_search_path_env = NULL
module_search_paths
= []optimization_level
= 0parse_argv
= 0parser_debug
= 0pathconfig_warnings
= 0prefix
=NULL
program_name
=NULL
pycache_prefix
=NULL
quiet
= 0run_command
=NULL
run_filename
=NULL
run_module
=NULL
show_alloc_count
= 0show_ref_count
= 0site_import
= 1skip_source_first_line
= 0stdio_encoding
=NULL
stdio_errors
=NULL
tracemalloc
= 0use_environment
= 0use_hash_seed
= 0user_site_directory
= 0verbose
= 0warnoptions
= []write_bytecode
= 1xoptions
= []_init_main
= 1_install_importlib
= 1
Python 3.7 API
Python 3.7在其C API中有4个函数用于初始化和终结Python
Py_Initialize()
,Py_InitializeEx()
: 初始化 PythonPy_Finalize()
,Py_FinalizeEx()
: 终结 Python
Python 3.7可以通过全局配置变量、环境变量和以下函数进行配置
PyImport_AppendInittab()
PyImport_ExtendInittab()
PyMem_SetAllocator()
PyMem_SetupDebugHooks()
PyObject_SetArenaAllocator()
Py_SetPath()
Py_SetProgramName()
Py_SetPythonHome()
Py_SetStandardStreamEncoding()
PySys_AddWarnOption()
PySys_AddXOption()
PySys_ResetWarnOptions()
还有一个高级的Py_Main()
函数和可以被重写的PyImport_FrozenModules
变量。
参阅初始化、终结和线程文档。
Python 问题
此PEP将直接或间接修复的问题
- bpo-1195571:“针对 Py_FatalError 的简单回调系统”
- bpo-11320:“使用API方法Py_SetPath导致Py_Initialize()中的错误(仅限Posix)”
- bpo-13533:“希望Py_Initialize能与宿主应用友好协作”
- bpo-14956:“自定义 PYTHONPATH 可能会破坏嵌入 Python 的应用”
- bpo-19983:“Python在启动期间被中断时,不应调用 abort() 而应调用 exit()”
- bpo-22213:“使pyvenv风格的虚拟环境在嵌入Python时更容易配置”。
- bpo-29778:“当嵌入器使用Py_SetPath设置模块路径时,_Py_CheckPython3使用未初始化的dllpath”
- bpo-30560:“添加Py_SetFatalErrorAbortFunc:允许嵌入程序处理致命错误”。
- bpo-31745:“重载“Py_GetPath”不起作用”
- bpo-32573:“所有 sys 属性(.argv, ...)都应存在于嵌入式环境中”。
- bpo-33135:“定义各种配置结构体的字段前缀”。该PEP现在明确定义了警告选项的处理方式。
- bpo-34725:“Py_GetProgramFullPath()在Windows中行为异常”
- bpo-36204:“是否弃用在Py_Initialize()之后调用Py_Main()?是否添加Py_InitializeFromArgv()?”
PEP实施的问题
- bpo-16961:“没有-E和单独环境变量的回归测试”
- bpo-20361:“-W命令行选项和PYTHONWARNINGS环境变量不应覆盖-b / -bb命令行选项”
- bpo-26122:“隔离模式不忽略PYTHONHASHSEED”
- bpo-29818:“Py_SetStandardStreamEncoding 在调试模式下导致内存错误”
- bpo-31845:“PYTHONDONTWRITEBYTECODE 和 PYTHONOPTIMIZE 无效”
- bpo-32030:“PEP 432: 重写 Py_Main()”
- bpo-32124:“记录在Py_Initialize()之前可以安全调用的函数”
- bpo-33042:“新的3.7启动序列导致PyInstaller崩溃”
- bpo-33932:“现在,调用两次Py_Initialize()会触发致命错误(Python 3.7)”
- bpo-34008:“我们支持在 Py_Initialize() 之后调用 Py_Main() 吗?”
- bpo-34170:“Py_Initialize():计算路径配置不得有副作用(PEP 432)”
- bpo-34589:“Py_Initialize() 和 Py_Main() 不应启用 C 语言环境强制”
- bpo-34639:“使用-E或-I选项时,PYTHONCOERCECLOCALE被忽略”
- bpo-36142:“在Python初始化中添加一个新的_PyPreConfig步骤来设置内存分配器和编码”
- bpo-36202:“在_PyPreConfig_Write()之前调用Py_DecodeLocale()可能会产生乱码”
- bpo-36301:“添加 _Py_PreInitialize() 函数”
- bpo-36443:“在_PyPreConfig中默认禁用coerce_c_locale和utf8_mode?”
- bpo-36444:“Python初始化:移除 _PyMainInterpreterConfig”
- bpo-36471:“PEP 432, PEP 587: 添加 _Py_RunMain()”
- bpo-36763:“PEP 587: 重做初始化 API 以准备 PEP 的第二个版本”
- bpo-36775:“重做文件系统编码器实现”
- bpo-36900:“使用_PyCoreConfig而不是全局配置变量”
与此PEP相关的问题
- bpo-12598:“将sys变量初始化从import.c移至sysmodule.c”
- bpo-15577:“嵌入式解释器中的真实 argc 和 argv”
- bpo-16202:“sys.path[0] 安全问题”
- bpo-18309:“使python稍微更可重定位”
- bpo-22257:“PEP 432: 重新设计解释器启动序列”
- bpo-25631:“嵌入式Python中无效的Unicode命令行参数导致分段错误”
- bpo-26007:“支持将标准库嵌入到可执行文件中”
- bpo-31210:“如果sys.prefix包含DELIM,则无法导入模块”。
- bpo-31349:“嵌入式初始化忽略 Py_SetProgramName()”
- bpo-33919:“向Python公开 _PyCoreConfig 结构”
- bpo-35173:“重用现有功能,允许Python 2.7.x(嵌入式和独立版本)根据共享库定位模块路径”
讨论
- 2019年5月
- 2019年3月
- 2019年2月
- 2018年7月-8月
版本历史
- 版本 5
- 将
PyInitError
重命名为PyStatus
- 将
PyInitError_Failed()
重命名为PyStatus_Exception()
- 将
Py_ExitInitError()
重命名为Py_ExitStatusException()
- 添加
PyPreConfig._config_init
私有字段。 - 修复 Python 配置默认值:isolated=0 和 use_environment=1,而不是 -1。
- 添加“多阶段初始化私有暂定API”和“讨论”部分
- 将
- 版本 4
- 引入定义更明确的“Python配置”和“隔离配置”。将所有宏替换为函数。
- 将
PyPreConfig_INIT
和PyConfig_INIT
宏替换为函数PyPreConfig_InitIsolatedConfig()
,PyConfig_InitIsolatedConfig()
PyPreConfig_InitPythonConfig()
,PyConfig_InitPythonConfig()
PyPreConfig
不再使用动态内存,allocator
字段类型变为整型,添加configure_locale
和parse_argv
字段。PyConfig
: 将module_search_path_env
重命名为pythonpath_env
,将use_module_search_paths
重命名为module_search_paths_set
,移除program
和dll_path
。- 将
Py_INIT_xxx()
宏替换为PyInitError_xxx()
函数。 - 删除“常量 PyConfig”部分。删除
Py_InitializeFromArgs()
和Py_InitializeFromBytesArgs()
函数。
- 版本 3
PyConfig
:添加configure_c_stdio
和parse_argv
;将_frozen
重命名为pathconfig_warnings
。- 重命名使用字节字符串和宽字符字符串的函数。例如,
Py_PreInitializeFromWideArgs()
变为Py_PreInitializeFromArgs()
,而PyConfig_SetArgv()
变为PyConfig_SetBytesArgv()
。 - 添加
PyWideStringList_Insert()
函数。 - 新增“路径配置”、“隔离 Python”、“Python 问题”和“版本历史”章节。
PyConfig_SetString()
和PyConfig_SetBytesString()
现在需要配置作为第一个参数。- 将
Py_UnixMain()
重命名为Py_BytesMain()
- 版本 2:添加
PyConfig
方法(例如:PyConfig_Read()
),添加PyWideStringList_Append()
,将PyWideCharList
重命名为PyWideStringList
。 - 版本 1:初始版本。
接受
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0587.rst
最后修改时间:2025-02-01 08:55:40 GMT