最佳实践:如何在setup.py中列出所需的依赖项?

时间:2013-03-15 00:09:33

标签: python

这就是我目前的做法:

import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))

requires = [
    'pyramid',
    'pyramid_debugtoolbar',
    'waitress',
    'requests',
    'mock',
    'gunicorn',
    'mongoengine',
    ]

setup(name='repoapi',
      version='0.0',
      description='repoapi',
      packages=find_packages(),
      include_package_data=True,
      zip_safe=False,
      install_requires=requires,
      tests_require=requires,
      test_suite="repoapi",
      entry_points="""\
      [paste.app_factory]
      main = repoapi:main
      """,
      )

这是好的方式吗?我有一些麻烦。例如,对于金字塔,我不能使用系统范围的nosetests插件来运行测试。我需要在全局python site-packages中安装pyramid

但我不希望如此。所以我必须在这个项目的virtualenv中安装鼻子。但我不希望它成为一种依赖。我觉得它不应该属于requires。事实并非如此。然而,我也不想一直手工安装。是的,我知道我有很多我不想这样做的事情......

但你怎么解决这个问题?我不想篡改全局python站点包,但我想安装nose作为virtualenv的一部分。

另外,pip安装需求文件。它稍微准确一点,因为我不需要手动指定版本,我不需要害怕手动更新setup.py。只需抛出pip freeze > file.txt并完成。

然而,pip可以返回垃圾,因为我们将垃圾包扔进了virtualenv。

这么多刀片。什么是最佳做法?你是如何处理这些问题的?

也许我错过了,但https://github.com/django/django/blob/master/setup.py,Django是怎么做到的?

2 个答案:

答案 0 :(得分:56)

您可以将需求分成"安装"依赖和"测试"像这样的依赖:

import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))

install_requires = [
    'pyramid',
    'pyramid_debugtoolbar',
    'waitress',
    'requests',
    'gunicorn',
    'mongoengine',
    ]

tests_require = [
    'mock',
    'nose',
    ]

setup(name='repoapi',
      ...
      install_requires=install_requires,
      tests_require=tests_require,
      test_suite="nose.collector",
      ...
      )

这样,当有人安装包时,只有"安装"依赖项已安装。因此,如果有人只想使用该软件包(并且他们对运行测试没有兴趣),那么他们就不必安装测试依赖项。

如果您想运行测试,可以使用:

$ python setup.py test

根据docs

  

请注意,这些必需的项目不会安装在运行测试的系统上,但只有在本地尚未安装的情况下才会下载到项目的安装目录中。

一旦"测试"依赖关系到位,然后它将运行" test_suite"命令。由于您提到了鼻子作为首选测试运行器,因此我展示了您use "nose.collector"如何配置它。

顺便说一下,Django setup.py并不是理解setuptools基础知识的最干净的例子。我认为Sentry setup.py是一个值得学习的更好的例子。

答案 1 :(得分:1)

如果您使用的是需求文件,那么另一种方法是读取其内容而不是复制它们:

import pathlib
from setuptools import setup, find_packages

HERE = pathlib.Path(__file__).parent
INSTALL_REQUIRES = (HERE / "requirements.txt").read_text().splitlines()
TESTS_REQUIRE = (HERE / "test-requirements.txt").read_text().splitlines()[1:]

setup(...,
      install_requires=INSTALL_REQUIRES,
      tests_require=TESTS_REQUIRE,
      ...
      )

我发现这种方法更加稳定并且不易出错,因为需求有时会发生变化,而且人们常常忘记更新两个位置。

注意:注意TESTS_REQUIRE从第二行开始,这是由于test-requirements.txt的第一行通常是-r requirements.txt。如果您的情况不同,请随时进行更改。