Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

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_PYMALLOCPYMEM_ALLOCATOR_PYMALLOC_DEBUG
  • configure_locale (int): 将 LC_CTYPE 区域设置为用户首选区域?如果等于 0,则将 coerce_c_localecoerce_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,则在比较 bytesbytearraystr,或比较 bytesint 时发出警告。如果等于或大于 2,则引发 BytesWarning 异常。
  • check_hash_pycs_mode (wchar_t*): --check-hash-based-pycs 命令行选项值(请参阅 PEP 552)。有效值:alwaysneverdefault。默认值为 default
  • configure_c_stdio (int): 如果非零,则配置 C 标准流(stdiostdoutstdout)。例如,在 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_environmentuser_site_directory 设置为 0。
  • legacy_windows_stdio (int, 仅限 Windows): 如果非零,则对 sys.stdinsys.stdoutsys.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: 剥离文档字符串
  • 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.stdoutsys.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 区域设置、PYTHONUTF8PYTHONCOERCECLOCALE 环境变量启用 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_prefixbase_exec_prefix 字段未设置,它们将分别从 prefixexec_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.stdoutsys.path);
    • 启用可选功能,如 faulthandlertracemalloc
    • 导入 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_environmentuser_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。

关于 PyConfigPyPreConfig 参数的规则

  • 如果 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

PYTHONLEGACYWINDOWSFSENCODINGPYTHONLEGACYWINDOWSSTDIO 仅适用于 Windows。

默认 Python 配置

PyPreConfig_InitPythonConfig():

  • allocator = PYMEM_ALLOCATOR_NOT_SET
  • coerce_c_locale_warn = -1
  • coerce_c_locale = -1
  • configure_locale = 1
  • dev_mode = -1
  • isolated = 0
  • legacy_windows_fs_encoding = -1
  • use_environment = 1
  • utf8_mode = -1

PyConfig_InitPythonConfig():

  • argv = []
  • base_exec_prefix = NULL
  • base_prefix = NULL
  • buffered_stdio = 1
  • bytes_warning = 0
  • check_hash_pycs_mode = NULL
  • configure_c_stdio = 1
  • dev_mode = 0
  • dump_refs = 0
  • exec_prefix = NULL
  • executable = NULL
  • faulthandler = 0
  • filesystem_encoding = NULL
  • filesystem_errors = NULL
  • hash_seed = 0
  • home = NULL
  • import_time = 0
  • inspect = 0
  • install_signal_handlers = 1
  • interactive = 0
  • isolated = 0
  • malloc_stats = 0
  • module_search_path_env = NULL
  • module_search_paths = []
  • optimization_level = 0
  • parse_argv = 1
  • parser_debug = 0
  • pathconfig_warnings = 1
  • prefix = NULL
  • program_name = NULL
  • pycache_prefix = NULL
  • quiet = 0
  • run_command = NULL
  • run_filename = NULL
  • run_module = NULL
  • show_alloc_count = 0
  • show_ref_count = 0
  • site_import = 1
  • skip_source_first_line = 0
  • stdio_encoding = NULL
  • stdio_errors = NULL
  • tracemalloc = 0
  • use_environment = 1
  • use_hash_seed = 0
  • user_site_directory = 1
  • verbose = 0
  • warnoptions = []
  • write_bytecode = 1
  • xoptions = []
  • _init_main = 1
  • _install_importlib = 1

默认隔离配置

PyPreConfig_InitIsolatedConfig():

  • allocator = PYMEM_ALLOCATOR_NOT_SET
  • coerce_c_locale_warn = 0
  • coerce_c_locale = 0
  • configure_locale = 0
  • dev_mode = 0
  • isolated = 1
  • legacy_windows_fs_encoding = 0
  • use_environment = 0
  • utf8_mode = 0

PyConfig_InitIsolatedConfig():

  • argv = []
  • base_exec_prefix = NULL
  • base_prefix = NULL
  • buffered_stdio = 1
  • bytes_warning = 0
  • check_hash_pycs_mode = NULL
  • configure_c_stdio = 0
  • dev_mode = 0
  • dump_refs = 0
  • exec_prefix = NULL
  • executable = NULL
  • faulthandler = 0
  • filesystem_encoding = NULL
  • filesystem_errors = NULL
  • hash_seed = 0
  • home = NULL
  • import_time = 0
  • inspect = 0
  • install_signal_handlers = 0
  • interactive = 0
  • isolated = 1
  • malloc_stats = 0
  • module_search_path_env = NULL
  • module_search_paths = []
  • optimization_level = 0
  • parse_argv = 0
  • parser_debug = 0
  • pathconfig_warnings = 0
  • prefix = NULL
  • program_name = NULL
  • pycache_prefix = NULL
  • quiet = 0
  • run_command = NULL
  • run_filename = NULL
  • run_module = NULL
  • show_alloc_count = 0
  • show_ref_count = 0
  • site_import = 1
  • skip_source_first_line = 0
  • stdio_encoding = NULL
  • stdio_errors = NULL
  • tracemalloc = 0
  • use_environment = 0
  • use_hash_seed = 0
  • user_site_directory = 0
  • verbose = 0
  • warnoptions = []
  • write_bytecode = 1
  • xoptions = []
  • _init_main = 1
  • _install_importlib = 1

Python 3.7 API

Python 3.7在其C API中有4个函数用于初始化和终结Python

  • Py_Initialize(), Py_InitializeEx(): 初始化 Python
  • Py_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(嵌入式和独立版本)根据共享库定位模块路径”

讨论

版本历史

  • 版本 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_INITPyConfig_INIT 宏替换为函数
      • PyPreConfig_InitIsolatedConfig(), PyConfig_InitIsolatedConfig()
      • PyPreConfig_InitPythonConfig(), PyConfig_InitPythonConfig()
    • PyPreConfig 不再使用动态内存,allocator 字段类型变为整型,添加 configure_localeparse_argv 字段。
    • PyConfig: 将 module_search_path_env 重命名为 pythonpath_env,将 use_module_search_paths 重命名为 module_search_paths_set,移除 programdll_path
    • Py_INIT_xxx() 宏替换为 PyInitError_xxx() 函数。
    • 删除“常量 PyConfig”部分。删除 Py_InitializeFromArgs()Py_InitializeFromBytesArgs() 函数。
  • 版本 3
    • PyConfig:添加 configure_c_stdioparse_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:初始版本。

接受

PEP 5872019年5月26日被 Thomas Wouters 接受


来源:https://github.com/python/peps/blob/main/peps/pep-0587.rst

最后修改时间:2025-02-01 08:55:40 GMT