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,该 API 允许使用 Python 对象配置 Python 以完成完整初始化)。但是,这些功能在此 PEP 中被省略,因为即使本机 CPython CLI 也不以这种方式工作 - 此 PEP 中的公共 API 提案仅限于已实现并作为私有 API 供我们在本机 CPython CLI 中使用的功能。
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)
:将item插入到list的index位置。如果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()
解析其argv
参数的方式与常规 Python 解析命令行参数的方式相同:请参阅 命令行参数。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](../pep-0552/))。有效值: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
内存分配器的统计信息。如果使用--without-pymalloc
构建 Python,则忽略此选项。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
将被计算 [路径配置](#path-configuration) 的函数覆盖。optimization_level
(int
):编译优化级别- 0:窥孔优化器(并且
__debug__
设置为True
) - 1:删除断言,将
__debug__
设置为False
- 2:去除文档字符串
- 0:窥孔优化器(并且
parse_argv
(int
):如果非零,则以与常规 Python 命令行参数相同的方式解析argv
,并从argv
中去除 Python 参数:请参阅 [命令行参数](#command-line-arguments)。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_set
设置为 1,则 module_search_paths
被认为已设置。在这种情况下,路径配置输入字段也会被忽略。
将 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
不为零,则 site
模块可能会修改 sys.path
。如果 user_site_directory
不为零并且存在用户的站点包目录,则 site
模块会将用户的站点包目录添加到 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);
有关始终在隔离模式下运行的自定义 Python 示例,请参阅Python 配置,该示例使用 Py_RunMain()
。
多阶段初始化私有临时 API
本节是介绍多阶段初始化的私有临时 API,这是PEP 432的核心功能
- “核心”初始化阶段,“最基本的 Python”
- 内置类型;
- 内置异常;
- 内置模块和冻结模块;
sys
模块仅部分初始化(例如,sys.path
尚未存在);
- “Main”初始化阶段,Python 已完全初始化
- 安装和配置
importlib
; - 应用路径配置;
- 安装信号处理程序;
- 完成
sys
模块的初始化(例如,创建sys.stdout
和sys.path
); - 启用可选功能,例如
faulthandler
和tracemalloc
; - 导入
site
模块; - 等等。
- 安装和配置
私有临时 API
PyConfig._init_main
:如果设置为 0,Py_InitializeFromConfig()
将在“核心”初始化阶段停止。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 (非) |
Py_IsolatedFlag |
isolated |
Py_LegacyWindowsFSEncodingFlag |
legacy_windows_fs_encoding |
Py_UTF8Mode |
utf8_mode |
(非)表示 PyPreConfig
的值与全局配置变量的值相反。Py_LegacyWindowsFSEncodingFlag
仅在 Windows 上可用。
映射到 PyConfig
字段的全局配置变量
变量 | 字段 |
---|---|
Py_BytesWarningFlag |
bytes_warning |
Py_DebugFlag |
parser_debug |
Py_DontWriteBytecodeFlag |
write_bytecode (非) |
Py_FileSystemDefaultEncodeErrors |
filesystem_errors |
Py_FileSystemDefaultEncoding |
filesystem_encoding |
Py_FrozenFlag |
pathconfig_warnings (非) |
Py_HasFileSystemDefaultEncoding |
filesystem_encoding |
Py_HashRandomizationFlag |
use_hash_seed ,hash_seed |
Py_IgnoreEnvironmentFlag |
use_environment (非) |
Py_InspectFlag |
inspect |
Py_InteractiveFlag |
interactive |
Py_IsolatedFlag |
isolated |
Py_LegacyWindowsStdioFlag |
legacy_windows_stdio |
Py_NoSiteFlag |
site_import (非) |
Py_NoUserSiteDirectory |
user_site_directory (非) |
Py_OptimizeFlag |
optimization_level |
Py_QuietFlag |
quiet |
Py_UnbufferedStdioFlag |
buffered_stdio (非) |
Py_VerboseFlag |
verbose |
_Py_HasFileSystemDefaultEncodeErrors |
filesystem_errors |
(非)表示 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 选项
映射到 PyConfig
字段的 -X 选项
选项 | 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
= 0check_hash_pycs_mode
=NULL
configure_c_stdio
= 1dev_mode
= 0dump_refs
= 0exec_prefix
=NULL
executable
=NULL
faulthandler
= 0filesystem_encoding
=NULL
filesystem_errors
=NULL
hash_seed
= 0home
=NULL
import_time
= 0inspect
= 0install_signal_handlers
= 1interactive
= 0isolated
= 0malloc_stats
= 0module_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
= 0check_hash_pycs_mode
=NULL
configure_c_stdio
= 0dev_mode
= 0dump_refs
= 0exec_prefix
=NULL
executable
=NULL
faulthandler
= 0filesystem_encoding
=NULL
filesystem_errors
=NULL
hash_seed
= 0home
=NULL
import_time
= 0inspect
= 0install_signal_handlers
= 0interactive
= 0isolated
= 1malloc_stats
= 0module_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_CheckPython3 在嵌入器使用 Py_SetPath 设置模块路径时使用未初始化的 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: “将 _PyCoreConfig 结构公开给 Python”
- 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
字段类型变为 int,添加configure_locale
和parse_argv
字段。PyConfig
:将module_search_path_env
重命名为pythonpath_env
,将use_module_search_paths
重命名为module_search_paths_set
,移除program
和dll_path
。- 用
PyInitError_xxx()
函数替换Py_INIT_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
上次修改时间:2023-10-11 12:05:51 GMT