Python C扩展包DLL和pyd

2024-03-29 01:16:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我的项目向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进行虚拟环境管理

建议的答案{a2}和{a3}概述了一个非常类似的{}和包含DLL-through{}选项的相同方法。答案似乎表明DLL和.pyd被放在同一个级别上,这对我来说是不可能的。我不太清楚我错过了什么才能得到同样的行为

python 3.8.6
setuptools 51.0.0
pip 20.3.1

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


Tags: pysrc目录packagepackagessetuprequiredstatic
1条回答
网友
1楼 · 发布于 2024-03-29 01:16:04

经过一些挖掘,我找到了一种适合我的方法This thread已经阐明了在Windows上加载DLL的问题以及最近(Python 3.8)在这个问题上的发展

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

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

大致来说,将package_data添加到setup.py应该可以做到这一点(除了清单文件附带的恶作剧和使用package_data,请阅读更多here

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

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

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带来了令人难以置信的沮丧体验

相关问题 更多 >