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,该 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.preconfigPyPreConfig类型)和PyInterpreterState.configPyConfig类型)字段添加到这些内部结构中。PyInterpreterState.config成为新的参考配置,取代全局配置变量和其他私有变量。

PyWideStringList

PyWideStringList是一个wchar_t*字符串列表。

PyWideStringList结构字段

  • lengthPy_ssize_t
  • itemswchar_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插入到listindex位置。如果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字段

  • exitcodeint):传递给exit()的参数。
  • err_msgconst char*):错误消息。
  • funcconst 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() 解析其 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,则在比较 bytesbytearraystr,或比较 bytesint 时发出警告。如果等于或大于 2,则引发 BytesWarning 异常。
  • check_hash_pycs_mode (wchar_t*):--check-hash-based-pycs 命令行选项值(参见 [PEP 552](../pep-0552/))。有效值: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 内存分配器的统计信息。如果使用 --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:去除文档字符串
  • 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.stdinsys.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_FrozenModulesPyImport_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_setmodule_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_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 不为零,则 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.stdoutsys.path);
    • 启用可选功能,例如 faulthandlertracemalloc
    • 导入 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_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(非)
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_seedhash_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_seedhash_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_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(嵌入式和独立式)根据共享库找到模块路径”

讨论

版本历史

  • 版本 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 字段类型变为 int,添加 configure_localeparse_argv 字段。
    • PyConfig:将 module_search_path_env 重命名为 pythonpath_env,将 use_module_search_paths 重命名为 module_search_paths_set,移除 programdll_path
    • PyInitError_xxx() 函数替换 Py_INIT_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 587 于 2019 年 5 月 26 日由 Thomas Wouters 接受


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

上次修改时间:2023-10-11 12:05:51 GMT