You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pypa build构建带Cython二进制的Python包生成通用wheel问题

问题:含Cython代码的Python分发包构建wheel时平台标识缺失

我正尝试构建一个含Cython代码的Python分发包,希望在上传至PyPI前将Cython代码编译为二进制文件。使用pypa的build工具,在项目根目录执行python -m build,该命令可完成代码的Cython化并生成适配本地系统的二进制文件,同时在dist目录创建sdist和wheel包。但生成的wheel命名为--py3-none-any.whl,解压后虽能找到对应二进制文件(如cycode.cp39-win_amd64.pyd),但当计划在GitHub workflow中为多Python版本、多操作系统构建二进制包时,不同系统生成的wheel同名,上传PyPI时会出现覆盖或版本重复错误。且跨系统从PyPI安装时,因缺少对应系统的二进制文件,又因是wheel包不会重新编译Cython代码,导致出现“模块找不到”错误。

使用环境为64位Windows、MacOS、Ubuntu,Python版本为3.8-3.10,另有少量依赖包。简化后的项目结构与配置文件如下:

简化项目结构

Tests\
Project\
    __init__.py
    pycode.py
    cymod\
        __init__.py
        _cycode.pyx
_build.py
pyproject.toml

pyproject.toml

[project]
name='Project'
version = '0.1.0'
description = 'My Project'
authors = ...
requires-python = ...
dependencies = ...

[build-system]
requires = [
    'setuptools>=64.0.0',
    'numpy>=1.22',
    'cython>=0.29.30',
    'wheel>=0.38'
]
build-backend = "setuptools.build_meta"

[tool.setuptools]
py-modules = ["_build"]
include-package-data = true
packages = ["Project",
            "Project.cymod"]

[tool.setuptools.cmdclass]
build_py = "_build._build_cy"

_build.py

import os
from setuptools.extension import Extension
from setuptools.command.build_py import build_py as _build_py


class _build_cy(_build_py):

    def run(self):
        self.run_command("build_ext")
        return super().run()

    def initialize_options(self):
        super().initialize_options()
        import numpy as np
        from Cython.Build import cythonize
        print('!-- Cythonizing')
        if self.distribution.ext_modules == None:
            self.distribution.ext_modules = []

        # Add to ext_modules list
        self.distribution.ext_modules.append(
                Extension(
                        'Project.cymod.cycode',
                        sources=[os.path.join('Project', 'cymod', '_cycode.pyx')],
                        include_dirs=[os.path.join('Project', 'cymod'), np.get_include()]
                        )
                )

        # Add cythonize ext_modules
        self.distribution.ext_modules = cythonize(
                self.distribution.ext_modules,
                compiler_directives={'language_level': "3"},
                include_path=['.', np.get_include()]
                )
        print('!-- Finished Cythonizing')

解决方案

核心问题分析

生成py3-none-any.whl这种通用wheel的原因是:自定义的build_py命令逻辑导致setuptools无法正确识别扩展模块的平台依赖属性,进而默认生成不包含平台/版本标识的通用wheel,最终引发跨平台安装失败、多版本包上传覆盖的问题。

具体修复步骤

1. 重构构建逻辑,直接声明扩展模块

移除通过_build.py动态添加扩展模块的方式,改用setup.py直接声明Cython扩展,让setuptools能正确识别模块的平台属性。

新增setup.py文件:

import os
import numpy as np
from setuptools import setup, Extension
from Cython.Build import cythonize

# 声明Cython扩展模块
ext_modules = [
    Extension(
        'Project.cymod.cycode',
        sources=[os.path.join('Project', 'cymod', '_cycode.pyx')],
        include_dirs=[os.path.join('Project', 'cymod'), np.get_include()]
    )
]

# Cythonize扩展模块
ext_modules = cythonize(
    ext_modules,
    compiler_directives={'language_level': "3"},
    include_path=['.', np.get_include()]
)

# 传递扩展模块给setuptools
setup(
    ext_modules=ext_modules
)

2. 更新pyproject.toml,移除自定义build_py配置

删除原配置中自定义的build_py命令,让setuptools默认处理扩展模块的构建与wheel生成:

[project]
name='Project'
version = '0.1.0'
description = 'My Project'
authors = ...
requires-python = ...
dependencies = ...

[build-system]
requires = [
    'setuptools>=64.0.0',
    'numpy>=1.22',
    'cython>=0.29.30',
    'wheel>=0.38'
]
build-backend = "setuptools.build_meta"

[tool.setuptools]
include-package-data = true
packages = ["Project",
            "Project.cymod"]

# 移除[tool.setuptools.cmdclass]相关配置

3. 验证平台特定wheel生成

现在执行python -m build,会生成带平台和Python版本标识的wheel,例如:

  • Windows平台:Project-0.1.0-cp39-cp39-win_amd64.whl
  • Ubuntu平台:Project-0.1.0-cp39-cp39-manylinux_2_17_x86_64.whl
  • macOS平台:Project-0.1.0-cp39-cp39-macosx_10_9_x86_64.whl

这类带标识的wheel上传PyPI后不会互相覆盖,用户跨系统安装时会自动匹配对应平台的二进制包。

4. GitHub Actions多平台构建优化(可选)

如果需要批量构建多平台、多Python版本的wheel,推荐使用cibuildwheel工具,它能自动处理不同环境的编译配置。示例.github/workflows/build.yml配置:

name: Build Wheels
on: [push, pull_request]

jobs:
  build_wheels:
    name: Build wheels on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.9'

      - name: Install cibuildwheel
        run: python -m pip install cibuildwheel==2.16.5

      - name: Build wheels
        run: python -m cibuildwheel --output-dir dist

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: wheels
          path: dist/*.whl

内容的提问来源于stack exchange,提问作者Oniow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:55:12