如何从现有本地库创建Python Wheel?

31 投票
3 回答
16566 浏览
提问于 2025-04-18 08:48

假设我有一个本地共享库(.dll 或 .so 文件),这个库是独立于任何 Python 机制构建的,还有一个 Python 模块使用 ctypes 来和这个库进行交互。有没有办法把它们打包成一个 .whl 文件?如果可以的话,应该怎么做呢?

假设这是可行的,我想我需要安装 wheel 包,并使用 python setup.py bdist_wheel 命令,但我的 setup.py 文件应该是什么样子的呢?

我想这样做是为了能够把不同平台的 Wheels 上传到一个私有的包索引中,然后可以根据我所使用的平台来安装合适的包。

3 个回答

1

使用 cibuildwheel,这是来自 pypa 的解决方案,pypa 是负责 pip 的团队。

cibuildwheel 可以构建带有本地库的轮子(wheel),它会自动修复一些问题,比如 LD_LIBRARY_PATH,并将 dll/so 文件整合到 bdist 轮子中。

11

虽然当时选择的答案是正确的,但这个请求导致了这个功能的失效。

我认为这个答案在新版的wheel包中正确地解决了这个问题。

(本来想把这个作为评论加上,但我没有足够的声望。)

47

这里有一种方法。举个例子,这里使用了libeay32.dll来提供一个md5的功能包。

项目的结构是:

MD5
│   setup.py
│
└───md5
    __init__.py   
    libeay32.dll

setup.py文件内容是:

from setuptools import setup, Distribution
 
 
class BinaryDistribution(Distribution):
    def has_ext_modules(foo):
        return True


setup(
    name='md5',
    version='1.0',
    description='MD5 Library',
    packages=['md5'],
    package_data={
        'md5': ['libeay32.dll'],
    },
    distclass=BinaryDistribution
)

有几点需要注意:

  1. 这个DLL文件被列为包数据,这样它就会被包含在生成的wheel文件里。
  2. 使用了一个自定义的distclass,这个类表明这个wheel文件有一个扩展模块,并且因为这个wheel是在Windows上构建的,所以它是一个win32的wheel。

Python的ctypes代码可以相对自身加载这个DLL(这段代码在__init.py__里):

lib_path = os.path.join(os.path.dirname(__file__), 'libeay32.dll')
lib = CDLL(lib_path)

在用pip安装了'wheel'之后,我可以运行python setup.py bdist_wheel来生成dist\md5-1.0-cp34-none-win32.whl。我正好在使用cpython 3.4,但如果你想要一个通用的wheel,可以在setup调用中添加options={"bdist_wheel": {"universal": True}}

现在我可以创建并激活一个新的虚拟环境,然后用pip安装md5-1.0-cp34-none-win32.whl,接着就可以使用我的包了:

>>> import md5
>>> md5.digest('hello')
'8d11aa0625ce42cfe9429d5e93b5ab0a'

撰写回答