在一个`setup.py`中多次调用`setup()`是否安全?

时间:2017-09-08 13:51:59

标签: python python-2.7 python-3.x setuptools distutils

我正在开发一个包含Cython扩展的包。

根据https://github.com/pypa/pip/issues/1958,我将使用setup_requires并推迟导入Cython。 我提出的最佳解决方案是在setup()中拨打setup.py两次:

... # initial imports
setup(setup_requires=['cython'])
from Cython.Build import cythonize
bar = Extension('foo.bar', sources = ['bar.pyx'])
setup(name = 'foo',
      ... # parameters
      ext_modules = cythonize([bar]),
      ... # more parameters
      )

但是我觉得setup()的名字表明它只会被调用一次。像我一样多次打电话是否安全?

我不能只分发轮子,因为该软件包也可供Linux用户使用。

[编辑]

此外,我认为这个问题比处理编译器依赖项更为通用。有人可能想要导入一些包(例如sphinxpweave)来预处理那些包的描述。

2 个答案:

答案 0 :(得分:4)

简单的答案是:。一旦调用setup,它将解析命令行参数并开始执行其工作。

至于Cython依赖关系,setup_requires在这里无法提供帮助。它可能会在没有安装的情况下尝试下载Cython。正如SpotlightKid所评论的那样:

  

distutils不会尝试成为编译器或将gcc安装为依赖

根据setuptools

  

如果您使用的是distutils扩展,则需要此参数(setup_requires)

因此,不适用于像Cython这样的包。

我认为用户有责任在致电Cython之前安装setup.py。如果您想提供更友好的错误消息,请尝试使用

try:
    from Cython.Build import cythonize
except ImportError:
    # Kindly ask the user to install Cython

以下帖子可能有所帮助:

答案 1 :(得分:2)

我有一个不同的场景,我需要多次运行setup():在我的情况下,我正在构建来自相同来源的两个包。第一个包是基于Fabric的命令行工具,第二个包只是库(API,工具等)。将项目拆分为两个存储库以用于这样一个小项目似乎太不切实际了,因为CLI部分是真的只是一个包装。使用不同参数多次运行setup()导致构建崩溃各种错误(主要是丢失文件)。我的解决方案是将每个setup()作为不同的Process

运行
from setuptools import setup, find_packages
from multiprocessing import Process

if __name__ == '__main__':
    setups = [
        {
            'name': 'cli-tool',
            'description': 'Some description...',
            'packages': find_packages(),
            'entry_points': {
                'console_scripts': [
                    'cli-tool = fabfile:main'
                ]
            },
            '...': 'etc. needed for setup() ...'
        },
        {
            'name': 'cli-tool-lib',
            'packages': find_packages(exclude=('fabfile',)),
            '...': 'etc.'
        }
    ]

    for s in setups:
        name = s['name']
        print("Building '{}'.".format(name))
        p = Process(target=setup, kwargs=s)
        p.start()
        p.join()
        print("Building of '{}' done.\n".format(name))