【问题标题】:Python C extension packaging DLL along with pydPython C 扩展打包 DLL 和 pyd
【发布时间】:2020-12-17 03:52:30
【问题描述】:

我的项目向 CPython (3.8) 解释器公开了一个静态库(称为 static.lib)。它由一个静态库组成,该库又依赖于一个 DLL FTDI 驱动程序。在阅读this thread 之后,似乎提供第三方 DLL 的最佳解决方案是将它们与 Python 包捆绑在一起 - 以确保 DLL 与.pyd 二进制文件位于同一目录中。

我遇到的问题是,在为我的包运行 pip install . 后,所需的 DLL(称为 required.dll)被放置在 site-packages/package/required.dll 中,而实际的 C 扩展库(称为 package.pyd)被放置在site-packages/package.pyd

因为当我尝试在 Python 中使用库时它不在同一个目录中,所以我得到了

ImportError: DLL load failed while importing package: The specified module could not be found.

下面是我的setup.py

setuptools.setup(
        name="package",
        version="1.0.0",
        packages=setuptools.find_packages(where="src"),
        package_dir={"": "src"},
        py_modules=[splitext(basename(path))[0] for path in glob("src/*.py")],
        use_scm_version=True,
        package_data={
            "package": [
                "_clibs/libs/required.dll",
            ],
        },
        ext_modules=[
            setuptools.Extension(
                "package",
                include_dirs=["src/package/_clibs/inc"],
                sources=[
                    "src/package/_clibs/src/api.cpp",
                    "src/package/_clibs/src/utils.cpp",
                ],
                library_dirs=[
                    "src/package/_clibs/libs",
                ],
                libraries=["static", "User32"],
                language="c++"
            ),
        ],
    )

项目的目录布局如下:

/
setup.py
.tox
src/
...package/
......wrapper.py
......__init__.py
......_clibs/
.........inc/
.........src/
............api.cpp
............utils.cpp
.........libs/
............required.dll
............static.lib

我还使用tox 进行虚拟环境管理。

建议的答案 herehere 概述了一个非常相似的 setup.py 以及包含 DLL 的相同方法 - 通过 package_data 选项。答案似乎表明 DLL 和 .pyd 然后被放置在同一级别,这对我来说不会发生。我无法完全确定获得相同行为所缺少的内容。

python 3.8.6
setuptools 51.0.0
pip 20.3.1

TL;DR DLL 被放置在与.pyd 二进制文件不同的目录中,从而使其对 Windows 加载程序不可见

【问题讨论】:

  • 这些是很好的答案,但是我仍然遇到了原始帖子中描述的问题。我尝试将required.dll 放在不同的目录中 - 结果相同。我将编辑答案以包括我的包目录布局。
  • PIP 是如何知道如何构建 library.pyd 的?我在 setup.py 中没有看到它的引用。
  • @CristiFati library.pydsetup.py 的乘积,将library.pyd 替换为最终分配给二进制文件的任何名称。为了与我提供的脚本保持一致,它应该是package.pyd 或其一些变体。为混淆道歉,我试图隐藏与这个问题无关的实际库名称,这只是一个不一致

标签: python python-3.x windows dll python-c-api


【解决方案1】:

经过一番挖掘,我找到了一种适合我的方法。 This thread 阐明了 Windows 上的 DLL 加载问题以及该问题的最新 (Python 3.8) 开发。

我使用的解决方案是从numpy 借来的。 为了正确地将 DLL 与您的 C 扩展捆绑在一起:

  1. 在您的包中创建一个目录,该目录将包含您的扩展将使用的所有必需的 DLL
  2. 修改您的构建过程以包含此目录以及 sdist 和 wheel 分发版
  3. 一旦用户导入您的包,您要做的第一件事就是动态修改搜索 DLL 的路径(两种不同的方法,具体取决于您使用的是 3.8 或更低版本)

大致上,将package_data 添加到您的setup.py 应该可以解决问题(减去 MANIFEST 文件附带的恶作剧和使用package_data,阅读更多here

 package_data={"your package name": ["path_to_DLLs/*"]},

要实现 #3,作为包的 __init__.py 中的一个选项,添加以下内容(从 numpy __config__.py 逐行获取 99%,由其非常复杂的构建系统自动生成。

import os
import sys

PATH_TO_DLL = "YOUR DLL DIRECTORY IN YOUR PACKAGE"
extra_dll_dir = os.path.join(os.path.dirname(__file__), PATH_TO_DLL)

if sys.version_info >= (3, 8):
    os.add_dll_directory(extra_dll_dir)
else:
    # legacy DLL loading mechanism through PATH env variable manipulations
    os.environ.setdefault("PATH", "")
    os.environ["PATH"] += os.pathsep + extra_dll_dir

非常感谢任何反馈。链接到这张票的线程谈到需要更好的 C 扩展入职文档,但我一直找不到。到目前为止,C 扩展 + Windows + setuptools 带来了令人难以置信的令人沮丧的体验。

【讨论】:

    猜你喜欢
    • 2012-01-05
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 2012-04-26
    • 2021-06-19
    • 1970-01-01
    • 2012-02-07
    相关资源
    最近更新 更多