PEP 741 – Python 配置 C API

猫勺猫勺 04-17 216 阅读 0 评论

抽象

将 C API 添加到受限的 C API 以配置 Python 初始化。 它可以与稳定的ABI一起使用。

通过添加可以 用于添加内置扩展模块;前面提到的功能 作为“inittab”。PyInitConfig_AddModule()

将 和 函数添加到 在运行时获取并设置当前运行时配置。PyConfig_Get()PyConfig_Set()

PEP 587(英语:PEP 587)“Python 初始化配置”统一了所有方式 配置 Python 初始化。这个 PEP(几乎完全)统一 还有 Python 预初始化的配置和 Python 在单个 API 中初始化,即使仍然需要预初始化才能解码来自 区域设置编码

这个新 API 取代了已弃用且不完整的旧 API,该 API 是 计划在 Python 3.13 和 Python 3.15 之间删除。

理由

PyConfig 不是受限 C API 的一部分

当 PEP 587 的第一个版本“Python 初始化配置”时 讨论过,有一个私人领域(): 配置版本,用于 ABI 兼容性。决定了 如果应用程序嵌入了 Python,它将坚持使用 Python 版本 无论如何,因此无需为 ABI 兼容性而烦恼。_config_versionint

PEP 587 的最终 PyConfig API 被排除在有限的 C API 之外 因为它的主要结构没有版本控制。Python 不能 保证ABI向后和向前兼容,它与 稳定的 ABI。PyConfig

自从 PyConfig 被添加到 Python 3.8 中以来,有限的 C API 和稳定的 ABI越来越受欢迎。例如,PyO3 项目等 Rust 绑定可以针对要嵌入的有限 C API Rust 中的 Python(但这不是默认的)。在实践中,PyO3 可以使用 针对特定需求的无限制 C API,但使用它们会避免稳定 ABI的优势。

旧版 API 的局限性

用于配置 Python 初始化的旧 API 基于 遗留函数。它现在大部分已被弃用:Py_Initialize()

  • 设置初始化配置,例如: 在 Python 3.11 中已弃用,在 Python 3.13 中已删除。Py_SetPath()

  • 全局配置变量,例如: 在 Python 3.12 中已弃用,并计划在 Python 3.14 中移除。Py_VerboseFlag

  • 获取当前配置,例如: 在 Python 3.13 中已弃用,并计划在 Python 3.15 中移除。Py_GetPath()

旧版 API 不支持“Python 配置”和 PEP 587 PyConfig API 的“隔离配置”。它只提供 “遗留配置”介于两者之间, 并且还使用旧版全局配置变量(例如 )。Py_Initialize()Py_VerboseFlag

有些选项只能通过环境变量来设置,比如环境变量来设置。问题是 环境变量由子进程继承,子进程可以是 令人惊讶和不受欢迎的行为。homePYTHONHOME

某些配置选项(如 )根本无法 被设置。configure_locale

受限 C API 的局限性

受限的 C API 是旧版 API 的子集。例如 全局配置变量,例如 ,不是 受限 C API 的一部分。Py_VerboseFlag

虽然从有限的 C API 版本 3.13 中删除了一些函数, 它们仍然是稳定 ABI 的一部分。例如,构建一个 具有有限 C API 版本 3.12 的应用程序仍然可以运行 Python 3.13 稳定 ABI。

获取运行时配置

PEP 587 没有 API 来获取当前的运行时配置, 仅配置 Python 初始化。

例如,全局配置变量在 Python 3.12 中已弃用,建议改用 using 。它只适用于 配置 Python,没有公共 API 获取。Py_UnbufferedStdioFlagPyConfig.buffered_stdioPyConfig.buffered_stdio

受限 C API 的用户要求使用公共 API 来获取 当前运行时配置。

Cython 需要获取配置选项:issue。optimization_level

当全局配置变量在 2022 年被弃用时,Marc-André Lemburg 请求一个 C API 来在运行时访问这些配置变量(不仅 在 Python 初始化期间)。

安全修复

要修复 CVE-2020-10735, 将非常大的字符串转换为整数(在基数中)时出现拒绝服务 10),讨论了在稳定版中添加新成员 影响 ABI 的分支。PyConfig

Gregory P. Smith 提出了一个使用基于文本的配置的不同 API 文件不受成员限制: FR:允许私有 运行时配置,用于在不破坏 PyConfig ABI 的情况下启用扩展(2022 年 8 月)。PyConfig

最后,决定不添加新成员 稳定的分支,但只在开发分支(成为 Python 3.12)中添加一个新成员。一个专用的 私有全局变量(与 无关)用于稳定 分支。PyConfigPyConfig.int_max_str_digitsPyConfig

PyPreConfig 和 PyConfig 之间的冗余

Python 预初始化使用结构和 Python 初始化使用该结构。两种结构 有四个重复的成员:、 和 。PyPreConfigPyConfigdev_modeparse_argvisolateduse_environment

冗余是由以下事实引起的:这两个结构是 分开的,而一些成员是需要的 预初始化。PyConfig

嵌入 Python

嵌入 Python 的应用程序

例子:

  • Blender 3D 图形。

  • Fontforge 字体编辑器。

  • 吉普。

  • LibreOffice的。

  • OBS 工作室。

  • 平铺。

  • VIM 文本编辑器。

在 Linux、FreeBSD 和 macOS 上,应用程序通常是静态的 链接到 ,或动态加载 。共享库是版本控制的,例如:对于 Linux 上的 Python 3.12。libpythonlibpythonlibpythonlibpython3.12.so

vim 项目可以针对稳定的 ABI。通常,“系统 Python” 使用版本。目前无法选择哪个 Python 要使用的版本。用户希望能够选择更新的 Python 按需。

在 Linux 上,部署嵌入 Python 的应用程序的另一种方法, 比如 GIMP,就是在 Flatpack、AppImage 或 Snap 中加入 Python “容器”。在这种情况下,应用程序自带 Python 副本 带有容器的版本。

嵌入 Python 的库

例子:

  • Apache mod_wsgi(来源)。

  • nimpy: Nim - Python 桥。

  • PyO3: Python 解释器的 Rust 绑定。

创建独立应用程序的实用程序

  • 适用于 macOS 的 py2app。

  • py2exe for Windows.

  • pyinstaller。

  • PyOxidizer: 它使用 PEP 587 PyConfig API。

这些实用程序创建独立应用程序,它们未链接到 libpython。

使用稳定的 ABI

罗纳德·奥索伦(Ronald Oussoren):

对于像 py2app/py2exe/pyinstaller 这样的工具,必须重新构建启动器可执行文件是非常不方便的 用于在有 bug 修复时启动打包的应用程序 Python 的发布。

格雷戈里·史密斯(Gregory P.Smith):

你不能扩展一个结构体,并假设嵌入了所有人 重建。他们没有。存在实际嵌入用途,这些用途使用 安装了 Python 次要版本作为共享库。将其更新为 在公共 API 中使用不同大小的结构,有人会 玩得不好。这就是为什么我认为结构冻结在 rc1 的原因 时间,即使只用于嵌入/编写自己的 发射器外壳。

科尔顿·墨菲:

我正在尝试使用非 C 嵌入 Python 解释器 语言。我必须坚持使用有限的 API 和私有 头文件中的配置结构是禁忌。基本上 我需要能够仅使用以下方式分配和配置所有内容 可导出的函数和堆...没有私有结构细节。

(...)

我严格限制在共享库 (DLL) 中的内容。我没有标题,我不能每次都静态地“重新编译” 新版本的 Python 问世了。这对我来说是无法维护的。

米利安·沃尔夫(Milian Wolff)的信息引述:

我们的应用程序是一个大型复杂的 C++ 代码库,其中包含大量 针对所有三个主要桌面平台的依赖项。

最初,我们希望能够使用稳定的 python ABI 来 允许生物学家“自带蟒蛇”。这个想法是 他们可能有一组自定义的 Python 库和代码 他们想继续使用。我们的集成 API - 所以我们 thought - 是一个很小的补充,应该适用于任何 Python out 在那里,所以我们使用了稳定的 ABI。

事实证明这是一条死胡同,我相信我们现在可以(应该?) 使用 python 的非稳定 ABI。允许最终用户 BYO Python 给我们带来了太多的设置问题和支持问题,以至于它 最后不值得。相反,我们现在更想交付一个 自定义 Python 带有自定义前缀,他们可以 pip 安装自定义 库根据需要输入。

我们面临的问题与稳定的 ABI 没有直接关系—— 恰恰相反。相反,这是由于第三方 python 我们交付的库本身不兼容 Python 版本递增。例如,对于我们使用的集成控制台 qtconsole/jupyter,它与 python 一起在古老的版本中工作 3.9,但需要更新版本的 Python 3.11+。

Umap 拉入的大量依赖关系甚至更糟,numba 而 pydnndescent 和 llvmlite 通常需要几个月的时间来支持更新的 Python 版本。

PyO3 项目的 David Hewitt:

我认为使配置结构不透明并使用 API 按名称设置/获取配置是一种受欢迎的简化:

  • 它是一个较小的 API,用于 PyO3 等语言绑定,用于包装和 重新曝光,以及

  • 人们更容易支持多个 Python 版本 嵌入到他们的应用程序中;无需有条件地 编译结构字段访问,只需使用正常的错误处理即可 如果配置值不适用于特定版本 在运行时。

Paul P. 消息的引述:

我完全同意,到处都是同样的故事 必须嵌入 CPython。我维护了一个运行时+生态系统 Android 4.4 +一段时间(为了更舒适地使用Panda3D 独立而不是 Kivy),修补 CPython 并为其制作 CI 还可以。

但我不得不放弃,因为我不得不经常重新编译所有已知的 模块:这对一个人来说是不可持续的。

所以我放弃了 Android arch,只去 WebAssembly (Emscripten)。 但一如既往的(困难和无聊)问题:必须重建 许多通常与 2D/3D 框架一起使用的软件包。(...)

除了ONE冠军赛,Harfang3d。自从 Python 以来,我没有重建这个 3.11 初始端口...猜猜为什么?它是一个有限的 C API - abi3 模块!

有限的 API abi3 是新鲜空气、快速和便携的。并相关 有了稳定的配置运行时,这将是完美的方式!


另请参阅问题 gh-116139 building an 嵌入 Python 3.11 并尝试使用 Python 运行它的应用程序 3.10:它确实崩溃了,因为结构ABI不稳定 在两个 Python 3.x 次要版本之间。PyConfig

设置运行时配置

Marc-André Lemburg 请求一个 C API 来设置运行时某些配置选项的值:

  • optimization_level

  • verbose

  • parser_debug

  • inspect

  • write_bytecode

以前,可以直接设置全局配置 变量:

  • Py_OptimizeFlag

  • Py_VerboseFlag

  • Py_DebugFlag

  • Py_InspectFlag

  • Py_DontWriteBytecodeFlag

但是这些配置标志在 Python 3.12 中被弃用了,并且是 计划在 Python 3.14 中删除。

规范

添加 C API 函数和结构以配置 Python 初始化:

  • 创建配置:

  • PyInitConfig不透明结构。

  • PyInitConfig_CreatePython().

  • PyInitConfig_CreateIsolated().

  • PyInitConfig_Free(config).


  • 获取选项:

  • PyInitConfig_HasOption(config, name).

  • PyInitConfig_GetInt(config, name, &value).

  • PyInitConfig_GetStr(config, name, &value).

  • PyInitConfig_GetWStr(config, name, &value).

  • PyInitConfig_GetStrList(config, name, &length, &items).

  • PyInitConfig_FreeStrList().

  • PyInitConfig_GetWStrList(config, name, &length, &items).

  • PyInitConfig_FreeWStrList().

  • 设置选项:

  • PyInitConfig_SetInt(config, name, value).

  • PyInitConfig_SetStr(config, name, value).

  • PyInitConfig_SetStrLocale(config, name, value).

  • PyInitConfig_SetWStr(config, name, value).

  • PyInitConfig_SetStrList(config, name, length, items).

  • PyInitConfig_SetStrLocaleList(config, name, length, items).

  • PyInitConfig_SetWStrList(config, name, length, items).

  • PyInitConfig_AddModule(config, name, initfunc)

  • 初始化:

  • Py_PreInitializeFromInitConfig(config).

  • Py_InitializeFromInitConfig(config).

  • 错误处理:

  • PyInitConfig_GetError(config, &err_msg).

  • PyInitConfig_GetExitcode(config, &exitcode).

添加 C API 函数以获取和设置当前运行时配置:

  • PyConfig_Get(name)→ .object

  • PyConfig_GetInt(name, &value).

  • PyConfig_Set(name).

  • PyConfig_Names()→ .frozenset

C API 使用以 null 结尾的 UTF-8 编码字符串来引用 配置选项。

所有 C API 函数都添加到受限的 C API 版本 3.13 中。

该结构是通过将四个组合来实现的 API 的结构,其成员为 井:PyInitConfigPyConfiginittab

  • PyPreConfig preconfig

  • PyConfig config

  • PyStatus status

  • struct _inittab *inittab为PyInitConfig_AddModule()

状态不再是分离的,而是统一结构的一部分,这使得 API 更易于使用。PyStatusPyInitConfig

配置选项

配置选项以 和 结构成员命名。请参阅 PyPreConfig 文档和 PyConfig 文档。PyPreConfigPyConfig

弃用和删除配置选项超出了 PEP,应根据具体情况进行讨论。

公共配置选项

以下选项可以获取并设置和。PyConfig_Get()PyConfig_Set()

选择类型评论
argvlist[str]应用程序接口:。sys.argv
base_exec_prefixstr应用程序接口:。sys.base_exec_prefix
base_executablestr应用程序接口:。sys.base_executable
base_prefixstr应用程序接口:。sys.base_prefix
bytes_warningint应用程序接口:。sys.flags.bytes_warning
exec_prefixstr应用程序接口:。sys.base_prefix
executablestr应用程序接口:。sys.executable
inspectboolAPI:()。sys.flags.inspectint
int_max_str_digitsint

API: 和

 .sys.flags.int_max_str_digitssys.get_int_max_str_digits()sys.set_int_max_str_digits()

interactivebool应用程序接口:。sys.flags.interactive
module_search_pathslist[str]应用程序接口:。sys.path
optimization_levelint应用程序接口:。sys.flags.optimize
parser_debugboolAPI:()。sys.flags.debugint
parser_debugstr应用程序接口:。sys.platlibdir
prefixstr应用程序接口:。sys.base_prefix
pycache_prefixstr应用程序接口:。sys.pycache_prefix
quietboolAPI:()。sys.flags.quietint
stdlib_dirstr应用程序接口:。sys._stdlib_dir
use_environmentboolAPI:()。sys.flags.ignore_environmentint
verboseint应用程序接口:。sys.flags.verbose
warnoptionslist[str]应用程序接口:。sys.warnoptions
write_bytecodeboolAPI:() 和 ()。sys.flags.dont_write_bytecodeintsys.dont_write_bytecodebool
xoptionsdict[str, str]应用程序接口:。sys._xoptions

某些选项名称与属性不同,例如选项和属性。 设置相应的属性。sysoptimization_levelsys.flags.optimizePyConfig_Set()sys

是字符串的列表,其中每个字符串 字符串的格式(值是隐式的)或 .在当前的运行时配置中,它变为 字典 ( → ).xoptionsPyInitConfigkeyTruekey=valuekey: strvalue: str | True

只读配置选项

以下选项可以 get ,但不能由 设置。PyConfig_Get()PyConfig_Set()

选择类型评论
allocatorint
buffered_stdiobool
check_hash_pycs_modestr应用程序接口:。imp.check_hash_pycs_mode
code_debug_rangesbool
coerce_c_localebool
coerce_c_locale_warnbool
configure_c_stdiobool
configure_localebool
cpu_countintAPI:()。os.cpu_count()int | None
dev_modebool应用程序接口:。sys.flags.dev_mode
dump_refsbool
dump_refs_filestr
faulthandlerbool应用程序接口:。faulthandler.is_enabled()
filesystem_encodingstr应用程序接口:。sys.getfilesystemencoding()
filesystem_errorsstr应用程序接口:。sys.getfilesystemencodeerrors()
hash_seedint
homestr
import_timebool
install_signal_handlersbool
isolatedboolAPI:()。sys.flags.isolatedint
legacy_windows_fs_encodingbool
legacy_windows_stdiobool仅限 Windows
malloc_statsbool
module_search_paths_setbool
orig_argvlist[str]应用程序接口:。sys.orig_argv
pathconfig_warningsbool
parse_argvbool
perf_profilingbool应用程序接口:。sys.is_stack_trampoline_active()
program_namestr
pythonpath_envstr
run_commandstr
run_filenamestr
run_modulestr
run_presitestr需要调试版本。
safe_pathbool
show_ref_countbool
site_importboolAPI:()。sys.flags.no_siteint
skip_source_first_linebool
stdio_encodingstrAPI: 和 .sys.stdin.encodingsys.stdout.encodingsys.stderr.encoding
stdio_errorsstrAPI: 和 .sys.stdin.errorssys.stdout.errorssys.stderr.errors
sys_path_0str
tracemallocintAPI:()。tracemalloc.is_tracing()bool
use_frozen_modulesbool
use_hash_seedbool
utf8_modebool
user_site_directoryboolAPI:()。sys.flags.no_user_siteint
warn_default_encodingbool
_install_importlibbool
_init_mAInbool
_is_python_buildbool
_pystatsbool应用程序接口:。 需要构建。sys._stats_on()sys._stats_off()Py_STATS

预初始化

调用预初始化 Python。为 例如,它设置内存分配器,并且可以配置语言环境和配置标准 C 流,例如 和 。Py_PreInitializeFromInitConfig()LC_CTYPEstdinstdout

以下选项只能在 Python 期间设置 预初始化:

  • allocator,

  • coerce_c_locale,

  • coerce_c_locale_warn,

  • configure_locale,

  • legacy_windows_fs_encoding,

  • utf8_mode.

尝试在 Python 预初始化后设置这些选项失败,并显示 错误。

PyInitConfig_SetStrLocale()如果 Python 不是,则调用函数 已预先初始化。PyInitConfig_SetStrLocaleList()Py_PreInitializeFromInitConfig()

创建配置

PyInitConfig结构:

      用于配置 Python 预初始化和 Python 初始化。

PyInitConfig* PyInitConfig_CreatePython(void):

      使用默认值创建新的初始化配置 Python 配置。

      它必须用 .PyInitConfig_Free()

      内存分配失败时返回。NULL

PyInitConfig* PyInitConfig_CreateIsolated(void):

     与 类似,但使用默认值 隔离配置。PyInitConfig_CreatePython()

void PyInitConfig_Free(PyInitConfig *config):

     初始化配置的可用内存。


获取选项

配置选项名称参数必须为非 NULL 以 null 结尾的 UTF-8 编码字符串。

  • int PyInitConfig_HasOption(PyInitConfig *config, const char *name):

  • 测试配置是否具有名为 name 的选项。

    如果该选项存在,则返回,否则返回。10

  • int PyInitConfig_GetInt(PyInitConfig *config, const char *name, int64_t *value):

  • 获取整数配置选项。

    • 设置 *值,并返回成功。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_GetStr(PyInitConfig *config, const char *name, char **value):

  • 获取以 null 结尾的 UTF-8 结尾的字符串配置选项 编码字符串。

    成功后,必须使用 释放字符串。free(value)

    • 设置 *值,并返回成功。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_GetWStr(PyInitConfig *config, const char *name, wchar_t **value):

  • 获取以 null 结尾的宽字符串形式的字符串配置选项。

    成功后,必须使用 释放字符串。free(value)

    • 设置 *value 并成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_GetStrList(PyInitConfig *config, const char *name, size_t *length, char ***items):

  • 获取字符串列表配置选项作为数组 以 null 结尾的 UTF-8 编码字符串。

    成功后,必须使用 .PyInitConfig_FreeStrList(length, items)

    • 设置 *length 和 *value,并在成功时返回。0

    • 配置中设置错误并在错误时返回。-1

  • void PyInitConfig_FreeStrList(size_t length, char **items):

  • 创建的字符串列表的可用内存。PyInitConfig_GetStrList()

  • int PyInitConfig_GetWStrList(PyInitConfig *config, const char *name, size_t *length, wchar_t ***items):

  • 获取字符串列表配置选项作为数组 以 null 结尾的宽字符串。

    成功后,必须使用 .PyInitConfig_FreeWStrList(length, items)

    • 设置 *length 和 *value,并在成功时返回。0

    • 配置中设置错误并在错误时返回。-1

  • void PyInitConfig_FreeWStrList(size_t length, wchar_t **items):

  • 创建的字符串列表的可用内存。PyInitConfig_GetWStrList()

设置选项

配置选项名称参数必须为非 NULL 以 null 结尾的 UTF-8 编码字符串。

某些配置选项会对其他选项产生副作用。这 逻辑仅在以下情况下实现 调用,而不是由下面的“设置”函数调用。例如,设置为 不会设置为 。Py_InitializeFromInitConfig()dev_mode1faulthandler1

  • int PyInitConfig_SetInt(PyInitConfig *config, const char *name, int64_t value):

  • 设置整数配置选项。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetStr(PyInitConfig *config, const char *name, const char *value):

  • 从以 null 结尾的 UTF-8 设置字符串配置选项 编码字符串。字符串被复制。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetStrLocale(PyInitConfig *config, const char *name, const char *value):

  • 从以 null 结尾的字节设置字符串配置选项 以区域设置编码编码的字符串。字符串被复制。

    字节字符串由 解码。 在调用之前必须调用 此函数。Py_DecodeLocale()Py_PreInitializeFromInitConfig()

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetWStr(PyInitConfig *config, const char *name, const wchar_t *value):

  • 从以 null 结尾的宽设置字符串配置选项 字符串。字符串被复制。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetStrList(PyInitConfig *config, const char *name, size_t length, char * const *items):

  • 从数组中设置字符串列表配置选项 以 null 结尾的 UTF-8 编码字符串。将复制字符串列表。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetStrLocaleList(PyInitConfig *config, const char *name, size_t length, char * const *items):

  • 从数组中设置字符串列表配置选项 以 null 结尾的字节 以区域设置编码编码的字符串。 将复制字符串列表。

    字节字符串由 解码。 在调用之前必须调用 此函数。Py_PreInitializeFromInitConfig()

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_SetWStrList(PyInitConfig *config, const char *name, size_t length, wchar_t * const *items):

  • 从错误设置字符串列表配置选项 以 null 结尾的宽字符串。将复制字符串列表。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int PyInitConfig_AddModule(PyInitConfig *config, const char *name, PyObject* (*initfunc)(void)):

  • 将内置扩展模块添加到内置模块表中。

    新模块可以通过名称 name 导入,并使用 函数 initfunc 作为 首次尝试导入。

    如果 Python 被多次初始化,则必须在每个 Python 上调用 初始化。PyInitConfig_AddModule()

    与该函数类似。PyImport_AppendInittab()

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

初始化 Python

  • int Py_PreInitializeFromInitConfig(PyInitConfig *config):

  • 从初始化配置预初始化 Python。

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

  • int Py_InitializeFromInitConfig(PyInitConfig *config):

  • 从初始化配置初始化 Python。

    请参阅 exitcode 案例。PyInitConfig_GetExitcode()

    • 成功回报。0

    • 配置中设置错误并在错误时返回。-1

    • 在 config 中设置退出代码,如果 Python 想要,则返回 退出。-1

错误处理

  • int PyInitConfig_GetError(PyInitConfig* config, const char **err_msg):

  • 获取配置错误消息。

    错误消息是 UTF-8 编码的字符串。

    如果 config 有退出代码,请将退出代码格式化为错误 消息。

    在使用 config 调用另一个函数之前,错误消息将保持有效。调用方不必释放 错误信息。PyInitConfig

    • 设置 *err_msg,如果设置了错误,则返回。1

    • 将 *err_msg设置为,否则返回。NULL0

  • int PyInitConfig_GetExitcode(PyInitConfig* config, int *exitcode):

  • 获取配置退出代码。

    只有函数可以设置出口 如果选项不为零,则进行代码。例如,一个 默认情况下,隔离配置无法设置退出代码,因为默认情况下为零。Py_InitializeFromInitConfig()parse_argvparse_argv

    解析命令行失败时可以设置退出代码(exit 代码 2) 或当命令行选项要求显示命令时 线路帮助(退出代码 0)。

    • 设置 *exitcode 并在 Python 想要退出时返回。1

    • 如果 config 未设置退出代码,则返回。0

获取和设置运行时配置

配置选项名称参数必须为非 NULL 以 null 结尾的 UTF-8 编码字符串。

  • PyObject* PyConfig_Get(const char *name):

  • 获取配置选项的当前运行时值作为 对象。

    • 在成功时返回新的引用。

    • 设置异常并在错误时返回。NULL

对象类型取决于选项:请参阅配置选项表。


  • 其他选项来自内部和结构。PyPreConfigPyConfig

    调用方必须持有 GIL。之前无法调用该函数 Python 初始化,也不是在 Python 最终化之后。

  • int PyConfig_GetInt(const char *name, int *value):

  • 与 类似,但以整数形式获取值。PyConfig_Get()

    • 设置并返回成功。*value0

    • 设置异常并在错误时返回。-1

  • PyObject* PyConfig_Names(void):

  • 以 .frozenset

    设置异常并在错误时返回。NULL

    调用方必须持有 GIL。

  • PyObject* PyConfig_Set(const char *name, PyObject *value):

  • 设置配置选项的当前运行时值。

    无法设置只读配置选项。

    调用方必须持有 GIL。之前无法调用该函数 Python 初始化,也不是在 Python 最终化之后。

    • 如果没有选项名称,则引发 a。ValueError

    • 引发 if 为无效值。ValueError

    • 如果选项是只读的:,则引发 a 无法设置。ValueError

    • 引发 if 的类型不正确。TypeError

稳定 ABI 的范围

此 PEP 添加的有限 C API 和稳定 ABI 仅提供 稳定的接口来对 Python 初始化进行编程

选项的行为、默认选项值和 Python 行为可以在每个 Python 版本中更改:它们不是“稳定”的。

此外,还可以添加、弃用和删除配置选项 遵循通常的 PEP 387 弃用过程。

例子


初始化 Python

初始化 Python 示例,设置不同类型的配置选项, 错误时返回 -1:


int init_python(void)
{  
  PyInitConfig *config = PyInitConfig_CreatePython();    
  if (config == NULL) {     
     printf("PYTHON INIT ERROR: memory allocation failed\n");        
     return -1;    
  }    
  // Set an integer (dev mode)    
  if (PyInitConfig_SetInt(config, "dev_mode", 1) < 0) {     
     goto error;    
  }    
  // Set a list of wide strings (argv)    
  wchar_t *argv[] = {L"my_program", L"-c", L"pass"};    
  if (PyInitConfig_SetWStrList(config, "argv",         
                          Py_ARRAY_LENGTH(argv), argv) < 0) {        
     goto error;    
  }    
  // Set a wide string (program name)    
  if (PyInitConfig_SetWStr(config, "program_name", L"my_program") < 0) {     
     goto error;    
  }    
  // Set a list of bytes strings (xoptions).    
  // Preinitialize implicitly Python to decode the bytes string.    
  char* xoptions[] = {"faulthandler"};    
  if (PyInitConfig_SetStrList(config, "xoptions",         
                        Py_ARRAY_LENGTH(xoptions), xoptions) < 0) {        
      goto error;    
  }    
  // Initialize Python with the configuration    
  if (Py_InitializeFromInitConfig(config) < 0) {      
     goto error;    
  }    
  PyInitConfig_Free(config);    
  return 0;
error:  
     // Display the error message    
     const char *err_msg;    
     (void)PyInitConfig_GetError(config, &err_msg);    
     printf("PYTHON INIT ERROR: %s\n", err_msg);    
     PyInitConfig_Free(config);    
     
     return -1;
}

增加初始化bytes_warning选项

增加初始化选项的示例 配置:bytes_warning

int config_bytes_warning(PyInitConfig *config)
{ 
   int bytes_warning;    
   if (PyInitConfig_GetInt(config, "bytes_warning", &bytes_warning)) {        
       return -1;    
   }    
   bytes_warning += 1;    
   if (PyInitConfig_SetInt(config, "bytes_warning", bytes_warning)) {        
       return -1;    
   }    
   return 0;
}

获取运行时详细选项

获取配置选项的当前运行时值的示例:verbose

int get_verbose(void)
{  
  int verbose;    
  if (PyConfig_GetInt("verbose", &verbose) < 0) {      
    // Silently ignore the error        
    PyErr_Clear();        
    return -1;    
  }    
  return verbose;
}

出错时,该函数以静默方式忽略该错误并返回 。在 实践,获得期权不会失败,除非未来 Python 版本删除了该选项。-1verbose

实现

  • 问题:没有限制 C API 来自定义 Python 初始化

  • PR:添加 PyInitConfig C API

  • PR:添加 PyConfig_Get() 函数

向后兼容性

更改完全向后兼容。仅添加新的 API。

保留现有的 API,例如 C API (PEP 587) 变。PyConfig

被拒绝的想法

以文本形式配置

建议将配置作为文本提供,以制作 API 与稳定的 ABI 兼容,并允许自定义选项。

例:

# integer
bytes_warning = 2

# string
filesystem_encoding = "utf8"   

# comment# list of strings
argv = ['python', '-c', 'code']

API 会将配置作为字符串,而不是文件。例 具有假设功能:PyInit_SetConfig()

void stable_abi_init_demo(int set_path)
{  
  PyInit_SetConfig(      
    "isolated = 1\n"        
    "argv = ['python', '-c', 'code']\n"        
    "filesystem_encoding = 'utf-8'\n"   
  );    
  if (set_path) {    
    PyInit_SetConfig("pythonpath = '/my/path'");    
  }
}

该示例忽略错误处理,使其更易于阅读。

问题在于生成此类配置文本需要添加 引号转换为字符串和转义字符串中的引号。格式化数组 的字符串变得不平凡。

提供 API 来格式化字符串或字符串数组并不是真的 值得,而 Python 可以直接提供一个 API 来设置 配置选项,其中值直接作为字符串传递,或者 字符串数组。它避免赋予某些人特殊的含义 字符,例如必须转义的换行符。


引用具有整数的选项

使用字符串引用配置选项需要比较 字符串可能比比较整数慢。

使用整数,类似于类型“slots”,例如 ,来引用 配置选项。参数被替换 跟。Py_tp_docconst char *nameint option

接受自定义选项在使用 整数,因为维护“命名空间”(范围)更难 整数选项。使用字符串,带有冒号分隔符的简单前缀 可以使用。

整数还需要维护整数常量列表,等等 使 C API 和 Python API 更大。

Python 3.13 只有大约 62 个配置选项,因此性能 并不是真正的阻碍问题。如果以后需要更好的性能,则 哈希表可用于按其名称获取选项。

如果在热代码中使用获取配置选项,则该值可以是 读取一次并缓存。顺便说一句,大多数配置选项不能 在运行时更改。

完全删除预初始化

延迟解码

如果没有函数,就可以存储和编码的字符串,并且只初始化区域设置和 解码 中的字符串。PyInitConfig_Get*()PyInitConfig_SetStrLocale()PyInitConfig_SetStrLocaleList()LC_CTYPEPy_InitializeFromInitConfig()

问题是用户要求功能。 例如,必须对 locale 编码,然后将其编码为 UTF-8 编码。PyInitConfig_Get*()PyInitConfig_GetStr()

但是,如果 和 字符串按照设计解码 PEP,没有 mojibake 的风险:回报 预期的解码字符串。PyInitConfig_SetStrLocale()PyInitConfig_SetStrLocaleList()PyInitConfig_GetStr()

删除 Python 配置

如果删除,则预初始化为 不再需要,因为默认情况下未配置 AND 设置选项总是会失败。PyInitConfig_CreatePython()LC_CTYPEPyInitConfig_CreateIsolated()"configure_locale"

问题是用户要求能够编写自己的自定义 Python,所以有一个类似 Python 的程序,但默认值不同 配置。该功能是必需的 为此。PyInitConfig_CreatePython()

此外,Python 配置也是 PEP 587 设计的一部分,在 Python 3.8 中实现。

“禁止”设置该选项也有类似的问题。"configure_locale"

多阶段初始化(类似于 PEP 432)

埃里克·斯诺(Eric Snow)表示担心,该提案可能会通过嵌入者强化以下观点: 初始化是一个整体步骤。他认为初始化 涉及 5 个不同的阶段,甚至建议 API 应该 明确地反映这一点。埃里克提出,至少, 初始化的实现应在一定程度上反映各个阶段 用于改进代码运行状况。总的来说,他的解释有一些 与 PEP 432 和 PEP 587 的相似之处。

埃里克与这个 PEP 相关的另一个关键点是,理想情况下, 传递给的配置应该是完整的 在调用该函数之前,而当前初始化 实际修改配置。Py_InitializeFromConfig()

虽然 Eric 并不一定建议 PEP 741 的替代方案, 任何围绕阶段添加粒度初始化 API 的建议都是 实际上与这个 PEP 试图实现的目标相反。 这样的 API 比较复杂,需要添加新的公共结构 以及新的公共职能。它使 Python 初始化更加 复杂,而不是这个 PEP 试图统一现有的 API 并使 它们更简单(相反)。具有多个类似的结构 目的可能导致重复的成员,与重复类似的问题 现有和结构之间的成员。PyPreConfigPyConfig


版权

本文档位于公共领域或 CC0-1.0-通用许可证,以更宽松者为准。

The End 微信扫一扫

文章声明:以上内容(如有图片或视频在内)除非注明,否则均为腾龙猫勺儿原创文章,转载或复制请以超链接形式并注明出处。

本文作者:猫勺本文链接:https://www.jo6.cn/post/83.html

上一篇 下一篇

相关阅读

发表评论

访客 访客
快捷回复: 表情:
评论列表 (暂无评论,216人围观)

还没有评论,来说两句吧...

取消
微信二维码
微信二维码
支付宝二维码