CSS*_*782 5 c python dll cython python-c-api
我有一个 C 函数,涉及使用 zstd 解压缩数据。我正在尝试使用 Cython 调用该函数。
使用文档中的此页面作为指南,我可以毫无问题地编译和运行下面的代码。
(我实际上并没有在这里使用 zstd lib)
// hello.c
#include <stdio.h>
#include <zstd.h>
int hello() {
printf("Hello, World!\n");
void *next_in = malloc(0);
void *next_out = malloc(0);
return 0;
}
# Hello.pyx
cdef extern from "hello.c":
int hello()
cpdef int callHello():
hello()
# hello_wrapper.setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize
ext_modules = [
Extension(
"hello_wrapper",
["hello_wrapper.pyx"],
libraries=["zstd"],
library_dirs=["path/to/zstd/lib"],
include_dirs=['path/to/zstd/include'],
)
]
setup(
ext_modules = cythonize(ext_modules, gdb_debug=True)
)
Run Code Online (Sandbox Code Playgroud)
使用如下命令我得到预期的输出:
>py hello_wrapper.setup.py build_ext --inplace
>py
Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello_wrapper
>>> hello_wrapper.callHello()
Hello, World!
0
Run Code Online (Sandbox Code Playgroud)
但是,当我修改hello.c为实际使用 zstd 库时:
// hello.c
#include <stdio.h>
#include <zstd.h>
int hello() {
printf("Hello, World!\n");
void *next_in = malloc(0);
void *next_out = malloc(0);
size_t const dSize = ZSTD_decompress(next_out, 0, next_in, 0); //the added line
return 0;
}
Run Code Online (Sandbox Code Playgroud)
虽然hello_wrapper.setup.py编译正常,但当我到达 import 语句时,出现以下错误:
>>> import hello_wrapper
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: DLL load failed while importing hello_wrapper: The specified module could not be found.
Run Code Online (Sandbox Code Playgroud)
通过阅读这篇 SO 文章,我发现这个错误意味着我没有正确指向或可能首先创建 zstd.lib 发挥其魔力所需的 DLL 文件。它是否正确?如果是这样,我该怎么做?如果没有,问题出在哪里?
我们将 cython 扩展链接到 windows-dll,这意味着:
*.lib-file (即) 在编译时zstd.lib需要"path/to/zstd/lib"*.dll-file(即zstd.dll)需要在导入模块时 Windows 可以找到它的地方。通常,Windows 不会查找"path/to/zstd/lib". 所以我们得到了一条有点神秘的错误消息:
ImportError: DLL 加载失败: 找不到指定的模块。
这并不意味着该模块有问题 - 它只是碰巧依赖于无法找到的 dll。
虽然 linux 有- 选项用于可以传递的-rpath链接器"path/to/zstd/lib"(可以使用runtime_library_dirs- 参数添加到Extension),但 Windows 上没有这样的选项。
适用于 Windows 的 dll-search-algorithmus可在此处找到。简而言之,搜索 dll(可能按照此处介绍的另一种顺序)
C:\Windows\System32)C:\Windows)PATH-变量中列出的目录然而,从Python3.8开始,上述默认算法不再用于CPython:当前工作目录和PATH-变量在调用过程中不再使用,但os.add_dll_directory可以用来添加解析依赖项时使用的路径。
将 dll 放入系统或 Windows 目录听起来不太吸引人,这给我们留下了以下选择:
zstd.dll到已编译的扩展中os.add_dll_directory将位置添加到 Python>=3.8 的搜索中PATH,例如set PATH="path/to/zstd/lib";%PATH%(对于 Python<3.8)另一种选择有点棘手:鉴于
如果具有相同模块名称的 DLL 已加载到内存中,则系统在解析加载的 DLL 之前仅检查重定向和清单,无论它位于哪个目录。系统不会搜索 DLL。
我们可以使用ctypes“预加载”正确的 dll,在导入包装模块时将使用该 dll(无需在光盘上搜索它),即:
import ctypes;
ctypes.CDLL("path/to/zstd/lib/zstd.dll"); # we preload with the full path
import hello_wrapper # works now!
Run Code Online (Sandbox Code Playgroud)
如果扩展是在同一系统上构建和使用的(例如通过build_ext --inplace),则上述内容适用。安装/分发有点麻烦(这个SO-post涵盖了这一点),一个想法是:
*.h-、*.lib- 和*.dll-files 放入“package_data”中(无论如何它似乎都会自动发生)library_path(或以编程方式设置绝对路径)。setup.py*.lib*.pyd安装中编译的文件旁边。一个示例可能是以下或多或少的minimal setup.py,其中所有内容(pyx文件、h文件、lib文件、dll文件)都放入包/文件夹中src/zstd:
from setuptools import setup, Extension, find_packages
from Cython.Build import cythonize
ext_modules = [
Extension(
"zstd.zstdwrapper",
["src/zstd/zstdwrapper.pyx"],
libraries=["zstd"],
library_dirs=["src/zstd"],
include_dirs=[], # set automatically to src/zstd during the build
)
]
print(find_packages(where='src'))
setup(
name = 'zstdwrapper',
ext_modules = cythonize(ext_modules),
packages = find_packages(where='src'),
package_dir = {"": "src"},
)
Run Code Online (Sandbox Code Playgroud)
现在它可以安装python setup.py install或用于创建例如源发行版,python setup.py sdist然后可以通过该发行版安装pip。
| 归档时间: |
|
| 查看次数: |
1864 次 |
| 最近记录: |