我有一个我正在使用Travis-CI的requirements.txt文件。在requirements.txt和setup.py中复制需求似乎很愚蠢,所以我希望在setuptools.setup中传递一个文件句柄给install_requires kwarg。

这可能吗?如果是,我该怎么做呢?

这是我的requirements.txt文件:

guessit>=0.5.2
tvdb_api>=1.8.2
hachoir-metadata>=1.3.3
hachoir-core>=1.3.3
hachoir-parser>=1.3.4

当前回答

另一个parse_requirements黑客也将环境标记解析为extras_require:

from collections import defaultdict
from pip.req import parse_requirements

requirements = []
extras = defaultdict(list)
for r in parse_requirements('requirements.txt', session='hack'):
    if r.markers:
        extras[':' + str(r.markers)].append(str(r.req))
    else:
        requirements.append(str(r.req))

setup(
    ...,
    install_requires=requirements,
    extras_require=extras
)

它应该同时支持sdist和binary dist。

正如其他人所说,parse_requirements有几个缺点,所以这不是您在公共项目中应该做的,但对于内部/个人项目可能足够了。

其他回答

小心parse_requirements行为!

请注意,pip.req。Parse_requirements将把下划线改为破折号。这件事让我生气了几天,后来我才发现。示例演示:

from pip.req import parse_requirements  # tested with v.1.4.1

reqs = '''
example_with_underscores
example-with-dashes
'''

with open('requirements.txt', 'w') as f:
    f.write(reqs)

req_deps = parse_requirements('requirements.txt')
result = [str(ir.req) for ir in req_deps if ir.req is not None]
print result

生产

['example-with-underscores', 'example-with-dashes']

需求文件使用扩展的pip格式,这只在您需要用更强的约束来补充setup.py时有用,例如指定某些依赖项必须来自的确切url,或者pip freeze的输出将整个包集冻结为已知的工作版本。如果不需要额外的约束,则只使用setup.py。如果你觉得你真的需要发布一个requirements.txt,你可以让它变成一行:

.

它将是有效的,并准确地引用同一目录下的setup.py的内容。

另一个parse_requirements黑客也将环境标记解析为extras_require:

from collections import defaultdict
from pip.req import parse_requirements

requirements = []
extras = defaultdict(list)
for r in parse_requirements('requirements.txt', session='hack'):
    if r.markers:
        extras[':' + str(r.markers)].append(str(r.req))
    else:
        requirements.append(str(r.req))

setup(
    ...,
    install_requires=requirements,
    extras_require=extras
)

它应该同时支持sdist和binary dist。

正如其他人所说,parse_requirements有几个缺点,所以这不是您在公共项目中应该做的,但对于内部/个人项目可能足够了。

它不能接受文件句柄。install_requires参数只能是字符串或字符串列表。

当然,您可以在设置脚本中读取您的文件,并将其作为字符串列表传递给install_requires。

import os
from setuptools import setup

with open('requirements.txt') as f:
    required = f.read().splitlines()

setup(...
install_requires=required,
...)

如果你不想强迫你的用户安装pip,你可以模仿它的行为:

import sys

from os import path as p

try:
    from setuptools import setup, find_packages
except ImportError:
    from distutils.core import setup, find_packages


def read(filename, parent=None):
    parent = (parent or __file__)

    try:
        with open(p.join(p.dirname(parent), filename)) as f:
            return f.read()
    except IOError:
        return ''


def parse_requirements(filename, parent=None):
    parent = (parent or __file__)
    filepath = p.join(p.dirname(parent), filename)
    content = read(filename, parent)

    for line_number, line in enumerate(content.splitlines(), 1):
        candidate = line.strip()

        if candidate.startswith('-r'):
            for item in parse_requirements(candidate[2:].strip(), filepath):
                yield item
        else:
            yield candidate

setup(
...
    install_requires=list(parse_requirements('requirements.txt'))
)