将预编译扩展放在非纯Python Wheels包的根文件夹中

2022-05-27 00:00:00 python setuptools distutils

问题描述

tl;dr如何使distutils/setuptools正确包含非纯数据文件?

我有一个项目,它使用自定义工具链执行一些重要的代码生成,然后用SWIG包装生成的代码,最后构建Python扩展。Cmake很好地封装了所有这些内容,最后我在项目的根目录下有了一个文件夹,它的工作方式与其他任何Python包完全一样。

我希望有一个简单的setup.py,这样我就可以将这个包包装到一个轮子中,并将其发送到PyPI,这样普通的Python用户就不必处理构建过程。关于如何强制setupTools生成非纯轮子,然后您可以使用package_data字段或MANIFEST.in文件捆绑扩展,有很多答案。

问题是这种方法会导致错误的轮子,因为扩展名包含在purelib下,而不是根目录下(它们属于Root-Is-Pure: False轮子中)。某些工具和发行版依赖于此分离是否正确。

我不感兴趣的答案:从setup.py内部运行cmake的自定义扩展(我不想添加另一层间接层来配置项目,也不想在构建选项更改时维护它),修改生成的轮子,我希望避免向项目根目录添加任何文件,而不仅仅是setup.py


解决方案

这行得通。distutilssetuptools必须是现有的一些设计最差的中央Python基础结构。

from setuptools import setup, find_packages, Extension
from setuptools.command.build_ext import build_ext
import os
import pathlib
import shutil

suffix = '.pyd' if os.name == 'nt' else '.so'

class CustomDistribution(Distribution):
  def iter_distribution_names(self):
    for pkg in self.packages or ():
      yield pkg
    for module in self.py_modules or ():
      yield module

class CustomExtension(Extension):
  def __init__(self, path):
    self.path = path
    super().__init__(pathlib.PurePath(path).name, [])

class build_CustomExtensions(build_ext):
  def run(self):
    for ext in (x for x in self.extensions if isinstance(x, CustomExtension)):
      source = f"{ext.path}{suffix}"
      build_dir = pathlib.PurePath(self.get_ext_fullpath(ext.name)).parent
      os.makedirs(f"{build_dir}/{pathlib.PurePath(ext.path).parent}",
          exist_ok = True)
      shutil.copy(f"{source}", f"{build_dir}/{source}")

def find_extensions(directory):
  extensions = []
  for path, _, filenames in os.walk(directory):
    for filename in filenames:
      filename = pathlib.PurePath(filename)
      if pathlib.PurePath(filename).suffix == suffix:
        extensions.append(CustomExtension(os.path.join(path, filename.stem)))
  return extensions

setup(
  # Stuff
  ext_modules = find_extensions("PackageRoot"),
  cmdclass = {'build_ext': build_CustomExtensions}
  distclass = CustomDistribution
)

我将扩展复制到构建目录中,仅此而已。我们重写分发,向鸡蛋信息编写者撒谎,告诉他们有任何扩展,一切都很容易。

相关文章