11

我有一个在 Linux 上使用 C 扩展编译的项目,但在 Windows 上没有它们。当我第一次在 Windows 上使用 生成轮子文件时python setup.py bdist_wheel,它们变得通用,我无法将它们上传到 PyPI,因为这些通用轮子更适​​合pip安装而不是.tar.gz上传(结果来自python setup.py sdist)。

解决这个问题的诀窍是在setup.py

Distribution.is_pure = lambda *args: False

或通过子类化Distribution

class BinaryDistribution(Distribution):
    def is_pure(self):
        return False

并使用额外的关键字参数调用setup()setup.py distclass=BinaryDistribution,

这一切在我运行 Windows XP 64 的 VM 上运行良好,该 VM 安装了 32 位和 64 位版本的 Python 2.6/2.7/3.3/3.4 和 pypy。一个简单的批处理文件给了我:

dist/pkg-1.0-cp26-none-win32.whl
dist/pkg-1.0-cp26-none-win_amd64.whl
dist/pkg-1.0-cp27-none-win32.whl
dist/pkg-1.0-cp27-none-win_amd64.whl
dist/pkg-1.0-cp33-none-win32.whl
dist/pkg-1.0-cp33-none-win_amd64.whl
dist/pkg-1.0-cp34-none-win32.whl
dist/pkg-1.0-cp34-none-win_amd64.whl

pip当你pip在 Windows 上运行和pip在 Linux 上运行时,你会下载并安装相应的包

pkg-1.0.tar.gz

其中包括在安装期间编译的 C 源代码。

问题开始于我没有可以安装 Python 3.5 的备用 Windows 7 许可机器(它不安装在 EOL XP 上)。所以我调查了 Appveyor并创建了appveyor.yml

environment:
  matrix:
    - PYTHON: C:\Python27
    - PYTHON: C:\Python33
    - PYTHON: C:\Python34
    - PYTHON: C:\Python35
    - PYTHON: C:\Python27-x64
    - PYTHON: C:\Python33-x64
      DISTUTILS_USE_SDK: '1'
    - PYTHON: 'C:\Python34-x64'
      DISTUTILS_USE_SDK: '1'
    - PYTHON: 'C:\Python35-x64'

install:
  - |
    %PYTHON%\python.exe -m pip install --upgrade pip
    %PYTHON%\python.exe -m pip install wheel

build: off

test_script:
  - echo Skipped for now

after_test:
  - |
    %PYTHON%\python.exe setup.py bdist_wheel

artifacts:
  - path: dist\*

使用完全相同的来源,上述八次调用的结果python setup.py bdist_wheel是:

pkg-1.0-py2-none-any.whl
pkg-1.0-py3-none-any.whl

如果您将这些上传到 PyPI,Linux 更喜欢它们而不是.tar.gz导致不包含 C 扩展代码。

是什么原因造成的,我如何使用 Appveyor 来构建我的.whl文件(或者至少是 Python 3.5 的文件?

4

3 回答 3

21

我刚刚在 Windows 7 x64 上使用 Python v2.7 和 Wheel v0.29.0 自己遇到了这个问题,我在其中构建了一个带有一些预编译扩展的 Python 包(使用 SWIG 和外部 DLL 进行复杂的 VisualStudio 设置)。

检查源代码后,我发现覆盖Distribution.has_ext_modules有效(自动包括平台名称和 ABI 标记):

from setuptools import setup
from setuptools.dist import Distribution

DISTNAME = "packagename"
DESCRIPTION = ""
MAINTAINER = ""
MAINTAINER_EMAIL = ""
URL = ""
LICENSE = ""
DOWNLOAD_URL = ""
VERSION = '1.2'
PYTHON_VERSION = (2, 7)


# Tested with wheel v0.29.0
class BinaryDistribution(Distribution):
    """Distribution which always forces a binary package with platform name"""
    def has_ext_modules(foo):
        return True


setup(name=DISTNAME,
      description=DESCRIPTION,
      maintainer=MAINTAINER,
      maintainer_email=MAINTAINER_EMAIL,
      url=URL,
      license=LICENSE,
      download_url=DOWNLOAD_URL,
      version=VERSION,
      packages=["packagename"],

      # Include pre-compiled extension
      package_data={"packagename": ["_precompiled_extension.pyd"]},
      distclass=BinaryDistribution)
于 2016-04-27T09:58:15.567 回答
5

当然,不同之处在于环境,在正常工作的 Win XP 上安装了旧版本的wheel软件包(0.24.0),而在 Appveyor 上安装了最新和最好的(和损坏的)版本 0.26 的轮子(0.25也坏了)。

更改 YAML 文件中的安装节以修复 wheel 版本:

install:
  - |
    %PYTHON%\python.exe -m pip install --upgrade pip
    %PYTHON%\python.exe -m pip install wheel==0.24

足以让它快速工作。

但是,您应该将 Linux 机器上的 wheel 包升级到 0.28 版,然后使用新的命令行选项--plat-name

python setup.py sdist
python2 setup.py bdist_wheel --plat-name win32
python2 setup.py bdist_wheel --plat-name win_amd64
python3 setup.py bdist_wheel --plat-name win32
python3 setup.py bdist_wheel --plat-name win_amd64

这将产生:

pkg-1.1.tar.gz
dist/pkg-1.1-py2-none-win32.whl
dist/pkg-1.1-py2-none-win32.whl
dist/pkg-1.1-py3-none-win_amd64.whl
dist/pkg-1.1-py3-none-win32.whl
dist/pkg-1.0-cp34-none-win_amd64.whl

您可以将其上传到 PyPI 并导致在.tar.gzLinux 上下载正确的 ( ) 文件并在 Windows 上下载相应的轮子。只需确保--plat-name win...使用. 生成的轮文件具有较小的(3 个文件及其 SHA256SUM 中的行结尾),但在 Windows 上正常安装。setup()ext_modules=None

这样你就不再需要在 Windows 机器上构建这些本质上是纯包的包

对我来说, Nate Coraor 所做的这一更改使我的总构建时间从 15 多分钟减少到大约 7

于 2016-01-31T09:56:19.877 回答
1

似乎与接受的答案相同但更简洁的替代方法是:

from setuptools import setup

DISTNAME = "packagename"
DESCRIPTION = ""
MAINTAINER = ""
MAINTAINER_EMAIL = ""
URL = ""
LICENSE = ""
DOWNLOAD_URL = ""
VERSION = '1.2'
PYTHON_VERSION = (2, 7)


setup(name=DISTNAME,
      description=DESCRIPTION,
      maintainer=MAINTAINER,
      maintainer_email=MAINTAINER_EMAIL,
      url=URL,
      license=LICENSE,
      download_url=DOWNLOAD_URL,
      version=VERSION,
      packages=["packagename"],

      # Include pre-compiled extension
      package_data={"packagename": ["_precompiled_extension.pyd"]},
      has_ext_modules=lambda: True)
于 2020-11-20T00:08:56.560 回答