使用嵌入式 python,在 DLLs 目录之外加载 *.pyd 失败

问题描述

我有一个 C++ 应用程序(X-Plane),其中有一个插件允许使用 python 脚本(XPpython3 插件)。该插件是用 C 语言编写的,使用 python CAPI,效果很好,让我可以编写在 C++ 应用程序中执行的 python 脚本。

在 Windows 10 上,我想通过导入 imgui 来扩展我的 Python 功能我有一个 python cython 构建的 pyd 文件 (_imgui.cp39-win_amd64.pyd)。

  • 如果我将 pyd 文件放在 C\Program Files\python39\DLLs 中,它会按预期工作:C++ 应用程序将 CAPI 调用到 python,python 加载导入和执行 imgui 代码的脚本。

  • 如果我将 pyd 文件放在其他任何地方,嵌入式 python 要么报告“找不到模块”——如果 pyd 不在 sys.path() 上,或者如果它在 sys.path() 上:

    ImportError: DLL load Failed while importing _imgui: The parameter is incorrect.'

更改使用:os.add_dll_directory(r'D:\somewhere\else') 不影响是否找到模块,也不改变“参数不正确”错误。 (有关此更改的详细信息,请参阅 https://bugs.python.org/issue36085。--我猜测是 DLL 的 add_dll_directory 更改查找,而不是 pyd?)sys.path 似乎用于定位 pyd。

是的,pyd 是用 python3.9 编译的:我已经用 mingw 和 Visual Studio 工具链编译了它,以防万一。

为了好玩,我从 python39\DLLs 中移动了 python-standard _zoneinfo.pyd 并且它在嵌入式 python 中以相同的方式失败:“参数不正确”。所以,这似乎排除了我的特定 pyd 文件

关键问题是:

  • 除了在 PythonXX\DLLs 下放置一个 pyd 文件之外,还有没有办法在嵌入式 Python 实现中加载 PYD? (我想避免告诉用户将我的 pyd 文件移动到 python39\DLLs 目录中...因为他们会忘记。)

请注意,使用 IDLE 或 python.exe,我可以加载 pyds 不会出错——在 sys.path 上的任何地方——所以它们不必在 python39\DLLs 下。只有在尝试从嵌入式 python 加载时才会出现“参数不正确”。当然,这在 Mac 上可以完美运行。

(额外问题:什么参数?它似乎是通过 Windows 错误传递的 python。)

解决方法

似乎有一个简单的答案,但我怀疑将其描述为 Python 错误更好。

Python39\DLLs 目录没有什么神奇之处。

问题是在 sys.path 中使用绝对路径和相对路径。

Python 可以使用绝对或相对路径查找模块。因此,如果 zippy.py 在文件夹 foobar 中,

sys.path.append('foobar')
import zippy
# Success

Python 并查找,但不使用相对路径加载 pyd 文件。例如,将 _zoneinfo.pyd 从 PythonXX\LDDs 移动到 foobar

sys.path.append('foobar')
import _zoneinfo
# ImportError: DLL load failed while importing _zoneinfo: The parameter is incorrect.'

相反,使用绝对路径,它会找到并加载 PYD:

sys.path.append(r'c:\MyTest\foobar')
import _zoneinfo
# Success
,

因此,实际上有一种方法可以做到这一点——即,将您的应用程序与所需的库一起交付。解决方案是使用 embedded distribution 并将其与您的应用程序一起提供。您可以在与所需版本相对应的官方 Python 下载页面上找到正确的发行版(这是指向最新 3.9 版本的链接,这似乎是您正在使用的版本:https://www.python.org/downloads/release/python-392/)。查找 Windows 可嵌入程序包。

然后,您可以简单地将 .pyd 文件与标准库文件一起放入(请注意,如果您的第三方库依赖于任何其他库,则您也必须包含它们)。使用可嵌入的发行版发布您的应用程序不仅应该解决您当前的问题,而且还意味着无论用户安装了哪个版本的 Python(或根本没有安装 Python),您的应用程序都可以运行。