抽象
将 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()
选择 | 类型 | 评论 |
argv | list[str] | 应用程序接口:。sys.argv |
base_exec_prefix | str | 应用程序接口:。sys.base_exec_prefix |
base_executable | str | 应用程序接口:。sys.base_executable |
base_prefix | str | 应用程序接口:。sys.base_prefix |
bytes_warning | int | 应用程序接口:。sys.flags.bytes_warning |
exec_prefix | str | 应用程序接口:。sys.base_prefix |
executable | str | 应用程序接口:。sys.executable |
inspect | bool | API:()。sys.flags.inspectint |
int_max_str_digits | int | API: 和 .sys.flags.int_max_str_digitssys.get_int_max_str_digits()sys.set_int_max_str_digits() |
interactive | bool | 应用程序接口:。sys.flags.interactive |
module_search_paths | list[str] | 应用程序接口:。sys.path |
optimization_level | int | 应用程序接口:。sys.flags.optimize |
parser_debug | bool | API:()。sys.flags.debugint |
parser_debug | str | 应用程序接口:。sys.platlibdir |
prefix | str | 应用程序接口:。sys.base_prefix |
pycache_prefix | str | 应用程序接口:。sys.pycache_prefix |
quiet | bool | API:()。sys.flags.quietint |
stdlib_dir | str | 应用程序接口:。sys._stdlib_dir |
use_environment | bool | API:()。sys.flags.ignore_environmentint |
verbose | int | 应用程序接口:。sys.flags.verbose |
warnoptions | list[str] | 应用程序接口:。sys.warnoptions |
write_bytecode | bool | API:() 和 ()。sys.flags.dont_write_bytecodeintsys.dont_write_bytecodebool |
xoptions | dict[str, str] | 应用程序接口:。sys._xoptions |
某些选项名称与属性不同,例如选项和属性。 设置相应的属性。sysoptimization_levelsys.flags.optimizePyConfig_Set()sys
是字符串的列表,其中每个字符串 字符串的格式(值是隐式的)或 .在当前的运行时配置中,它变为 字典 ( → ).xoptionsPyInitConfigkeyTruekey=valuekey: strvalue: str | True
只读配置选项
以下选项可以 get ,但不能由 设置。PyConfig_Get()PyConfig_Set()
选择 | 类型 | 评论 |
---|---|---|
allocator | int | |
buffered_stdio | bool | |
check_hash_pycs_mode | str | 应用程序接口:。imp.check_hash_pycs_mode |
code_debug_ranges | bool | |
coerce_c_locale | bool | |
coerce_c_locale_warn | bool | |
configure_c_stdio | bool | |
configure_locale | bool | |
cpu_count | int | API:()。os.cpu_count() int | None |
dev_mode | bool | 应用程序接口:。sys.flags.dev_mode |
dump_refs | bool | |
dump_refs_file | str | |
faulthandler | bool | 应用程序接口:。faulthandler.is_enabled() |
filesystem_encoding | str | 应用程序接口:。sys.getfilesystemencoding() |
filesystem_errors | str | 应用程序接口:。sys.getfilesystemencodeerrors() |
hash_seed | int | |
home | str | |
import_time | bool | |
install_signal_handlers | bool | |
isolated | bool | API:()。sys.flags.isolated int |
legacy_windows_fs_encoding | bool | |
legacy_windows_stdio | bool | 仅限 Windows |
malloc_stats | bool | |
module_search_paths_set | bool | |
orig_argv | list[str] | 应用程序接口:。sys.orig_argv |
pathconfig_warnings | bool | |
parse_argv | bool | |
perf_profiling | bool | 应用程序接口:。sys.is_stack_trampoline_active() |
program_name | str | |
pythonpath_env | str | |
run_command | str | |
run_filename | str | |
run_module | str | |
run_presite | str | 需要调试版本。 |
safe_path | bool | |
show_ref_count | bool | |
site_import | bool | API:()。sys.flags.no_site int |
skip_source_first_line | bool | |
stdio_encoding | str | API: 和 .sys.stdin.encoding sys.stdout.encoding sys.stderr.encoding |
stdio_errors | str | API: 和 .sys.stdin.errors sys.stdout.errors sys.stderr.errors |
sys_path_0 | str | |
tracemalloc | int | API:()。tracemalloc.is_tracing() bool |
use_frozen_modules | bool | |
use_hash_seed | bool | |
utf8_mode | bool | |
user_site_directory | bool | API:()。sys.flags.no_user_site int |
warn_default_encoding | bool | |
_install_importlib | bool | |
_init_main | bool | |
_is_python_build | bool | |
_pystats | bool | 应用程序接口:。 需要构建。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 的选项。
如果该选项存在,则返回,否则返回。
1
0
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_mode
1
faulthandler
1
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 结尾的字节 以区域设置编码编码的字符串。 将复制字符串列表。
成功回报。
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设置为,否则返回。
NULL
0
int PyInitConfig_GetExitcode(PyInitConfig* config, int *exitcode)
:获取配置退出代码。
只有函数可以设置出口 如果选项不为零,则进行代码。例如,一个 默认情况下,隔离配置无法设置退出代码,因为默认情况下为零。
Py_InitializeFromInitConfig()
parse_argv
parse_argv
解析命令行失败时可以设置退出代码(exit 代码 2) 或当命令行选项要求显示命令时 线路帮助(退出代码 0)。
设置 *exitcode 并在 Python 想要退出时返回。
1
如果 config 未设置退出代码,则返回。
0
获取和设置运行时配置
配置选项名称参数必须为非 NULL 以 null 结尾的 UTF-8 编码字符串。
PyObject* PyConfig_Get(const char *name)
:获取配置选项的当前运行时值作为 对象。
在成功时返回新的引用。
设置异常并在错误时返回。
NULL
对象类型取决于选项:请参阅配置选项表。
其他选项来自内部和结构。
PyPreConfig
PyConfig
调用方必须持有 GIL。之前无法调用该函数 Python 初始化,也不是在 Python 最终化之后。
int PyConfig_GetInt(const char *name, int *value)
:与 类似,但以整数形式获取值。
PyConfig_Get()
设置并返回成功。
*value
0
设置异常并在错误时返回。
-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_doc
const char *name
int option
接受自定义选项在使用 整数,因为维护“命名空间”(范围)更难 整数选项。使用字符串,带有冒号分隔符的简单前缀 可以使用。
整数还需要维护整数常量列表,等等 使 C API 和 Python API 更大。
Python 3.13 只有大约 62 个配置选项,因此性能 并不是真正的阻碍问题。如果以后需要更好的性能,则 哈希表可用于按其名称获取选项。
如果在热代码中使用获取配置选项,则该值可以是 读取一次并缓存。顺便说一句,大多数配置选项不能 在运行时更改。
完全删除预初始化
延迟解码
如果没有函数,就可以存储和编码的字符串,并且只初始化区域设置和 解码 中的字符串。PyInitConfig_Get*()
PyInitConfig_SetStrLocale()
PyInitConfig_SetStrLocaleList()
LC_CTYPE
Py_InitializeFromInitConfig()
问题是用户要求功能。 例如,必须对 locale 编码,然后将其编码为 UTF-8 编码。PyInitConfig_Get*()
PyInitConfig_GetStr()
但是,如果 和 字符串按照设计解码 PEP,没有 mojibake 的风险:回报 预期的解码字符串。PyInitConfig_SetStrLocale()
PyInitConfig_SetStrLocaleList()
PyInitConfig_GetStr()
删除 Python 配置
如果删除,则预初始化为 不再需要,因为默认情况下未配置 AND 设置选项总是会失败。PyInitConfig_CreatePython()
LC_CTYPE
PyInitConfig_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 并使 它们更简单(相反)。具有多个类似的结构 目的可能导致重复的成员,与重复类似的问题 现有和结构之间的成员。PyPreConfig
PyConfig
版权
本文档位于公共领域或 CC0-1.0-通用许可证,以更宽松者为准。
文章声明:以上内容(如有图片或视频在内)除非注明,否则均为腾龙猫勺儿原创文章,转载或复制请以超链接形式并注明出处。
本文作者:猫勺本文链接:https://www.jo6.cn/post/83.html
还没有评论,来说两句吧...