在setup.py或pip需求文件中,如何控制安装包依赖的顺序?

16 投票
4 回答
10026 浏览
提问于 2025-04-16 11:49

我有一个Python包,它的setup.py文件里通过常规方式声明了依赖关系,也就是在install_requires=[...]里面列出了需要的包。其中有一个包叫scikits.timeseries,它的setup.py文件要求numpy必须先安装。因此,我想找个方法先安装numpy。对于这种情况,或者一般来说,能控制依赖安装的顺序吗?怎么做呢?

目前,setup.py在拉取依赖时(在install_requires参数中列出的那些)似乎顺序是随机的。而且,在setup.py的setup(...)中,我尝试使用了这个参数:

extras_require={'scikits.timeseries': ['numpy']}

...但是没有成功,依赖安装的顺序没有改变。

我还尝试设置了一个pip的需求文件,但在这里,pip安装依赖的顺序也没有按照需求文件中的行顺序来,所以也没成功。

还有一种可能性是在setup.py的开头加一个系统调用,先安装numpy再调用setup(...),但我希望能有更好的方法。提前感谢任何帮助。

4 个回答

1

这里有一个实际上可以用的解决方案。虽然这个方法不是特别“好用”,但在“绝境中...”也只能这样了。

基本上,你需要:

  • 重写setuptools的“安装命令”类(还有一些相关的类)
  • 通过命令行语句从脚本中执行pip,这样你可以控制执行的顺序

这样做的缺点是:

  • 必须先安装pip。你不能在没有pip的环境中直接执行setup.py
  • 最开始安装“前置条件”时的控制台输出出不来,原因有点奇怪。(也许我会在后面更新这里,解决这个问题...)

代码:

from setuptools import setup

# Override standard setuptools commands. 
# Enforce the order of dependency installation.
#-------------------------------------------------
PREREQS = [ "ORDERED-INSTALL-PACKAGE" ]

from setuptools.command.install import install
from setuptools.command.develop import develop
from setuptools.command.egg_info import egg_info

def requires( packages ): 
    from os import system
    from sys import executable as PYTHON_PATH
    from pkg_resources import require
    require( "pip" )
    CMD_TMPLT = '"' + PYTHON_PATH + '" -m pip install %s'
    for pkg in packages: system( CMD_TMPLT % (pkg,) )       

class OrderedInstall( install ):
    def run( self ):
        requires( PREREQS )
        install.run( self )        

class OrderedDevelop( develop ):
    def run( self ):
        requires( PREREQS )
        develop.run( self )        

class OrderedEggInfo( egg_info ):
    def run( self ):
        requires( PREREQS )
        egg_info.run( self )        

CMD_CLASSES = { 
     "install" : OrderedInstall
   , "develop" : OrderedDevelop
   , "egg_info": OrderedEggInfo 
}        
#-------------------------------------------------

setup ( 
     ...
    install_requires = [ "UNORDERED-INSTALL-PACKAGE" ],
    cmdclass = CMD_CLASSES
)
4

使用 setup_requires 参数,比如在安装 scipy 之前先安装 numpy,你可以把它放进 setup_requires 中,并添加 __builtins__.__NUMPY_SETUP__ = False 这个钩子,这样就能确保 numpy 正确安装:

setup(
    name='test',
    version='0.1',
    setup_requires=['numpy'],
    install_requires=['scipy']
)

def run(self):
    __builtins__.__NUMPY_SETUP__ = False
    import numpy
6

如果 scikits.timeseries 这个库需要用到 numpy,那么它应该在说明中明确指出这一点。这样的话,使用 pip 安装的时候就会自动处理这些依赖关系(我记得 setuptools 也能做到这一点,不过我很久没用过它了)。如果你是 scikits.timeseries 的开发者,那就应该去修正它的依赖声明。

撰写回答