Python setuptools setup.py 文件中 install_requires kwarg 的参考 requirements.txt

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14399534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:17:46  来源:igfitidea点击:

Reference requirements.txt for the install_requires kwarg in setuptools setup.py file

pythonpipsetuptoolsrequirements.txt

提问by blz

I have a requirements.txtfile that I'm using with Travis-CI. It seems silly to duplicate the requirements in both requirements.txtand setup.py, so I was hoping to pass a file handle to the install_requireskwarg in setuptools.setup.

我有一个requirements.txt与 Travis-CI 一起使用的文件。看来愚蠢复制中都要求requirements.txtsetup.py,所以我希望到一个文件句柄传递给install_requires在kwarg setuptools.setup

Is this possible? If so, how should I go about doing it?

这可能吗?如果是这样,我应该如何去做?

Here is my requirements.txtfile:

这是我的requirements.txt文件:

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

采纳答案by Romain Hardouin

You can flip it around and list the dependencies in setup.pyand have a single character — a dot .— in requirements.txtinstead.

你可以翻转它周围,列表中的依赖关系setup.py,并有单字符-点.-在requirements.txt代替。



Alternatively, even if not advised, it is still possible to parse the requirements.txtfile (if it doesn't refer any external requirements by URL) with the following hack (tested with pip 9.0.1):

或者,即使不建议,仍然可以requirements.txt使用以下 hack(测试pip 9.0.1)来解析文件(如果它没有通过 URL 引用任何外部要求):

install_reqs = parse_requirements('requirements.txt', session='hack')

This doesn't filter environment markersthough.

但这不会过滤环境标记



In old versions of pip, more specifically older than 6.0, there is a public API that can be used to achieve this. A requirement file can contain comments (#) and can include some other files (--requirementor -r). Thus, if you really want to parse a requirements.txtyou can use the pip parser:

在旧版本的 pip 中,更具体地说是早于 6.0,有一个公共 API 可用于实现此目的。需求文件可以包含注释 ( #) 并且可以包含一些其他文件 (--requirement-r)。因此,如果你真的想解析 arequirements.txt你可以使用 pip 解析器:

from pip.req import parse_requirements

# parse_requirements() returns generator of pip.req.InstallRequirement objects
install_reqs = parse_requirements(<requirements_path>)

# reqs is a list of requirement
# e.g. ['django==1.5.1', 'mezzanine==1.4.6']
reqs = [str(ir.req) for ir in install_reqs]

setup(
    ...
    install_requires=reqs
)

回答by Fredrick Brennan

It can't take a file handle. The install_requiresargument can only be a string or a list of strings.

它不能接受文件句柄。该install_requires参数可以仅仅是一个字符串或字符串列表

You can, of course, read your file in the setup script and pass it as a list of strings to install_requires.

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

import os
from setuptools import setup

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

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

回答by vdboor

Install the current package in Travis. This avoids the use of a requirements.txtfile. For example:

在 Travis 中安装当前包。这避免了使用requirements.txt文件。例如:

language: python
python:
  - "2.7"
  - "2.6"
install:
  - pip install -q -e .
script:
  - python runtests.py

回答by Tobu

Requirements files use an expanded pip format, which is only useful if you need to complement your setup.pywith stronger constraints, for example specifying the exact urls some of the dependencies must come from, or the output of pip freezeto freeze the entire package set to known-working versions. If you don't need the extra constraints, use only a setup.py. If you feel like you really need to ship a requirements.txtanyway, you can make it a single line:

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

.

It will be valid and refer exactly to the contents of the setup.pythat is in the same directory.

它将是有效的,并且完全引用setup.py同一目录中的内容。

回答by Diego Navarro

from pip.req import parse_requirementsdid not work for me and I think it's for the blank lines in my requirements.txt, but this function does work

from pip.req import parse_requirements对我不起作用,我认为它适用于我的 requirements.txt 中的空行,但此功能确实有效

def parse_requirements(requirements):
    with open(requirements) as f:
        return [l.strip('\n') for l in f if l.strip('\n') and not l.startswith('#')]

reqs = parse_requirements(<requirements_path>)

setup(
    ...
    install_requires=reqs,
    ...
)

回答by Wilfredo Sánchez Vega

Using parse_requirementsis problematic because the pip API isn't publicly documented and supported. In pip 1.6, that function is actually moving, so existing uses of it are likely to break.

使用parse_requirements是有问题的,因为 pip API 没有公开记录和支持。在 pip 1.6 中,该函数实际上正在移动,因此它的现有用途可能会中断。

A more reliable way to eliminate duplication between setup.pyand requirements.txtis to specific your dependencies in setup.pyand then put -e .into your requirements.txtfile. Some information from one of the pipdevelopers about why that's a better way to go is available here: https://caremad.io/blog/setup-vs-requirement/

消除setup.py和之间重复的更可靠方法requirements.txt是在文件中指定依赖项setup.py,然后将其-e .放入requirements.txt文件中。来自一位pip开发人员的有关为什么这是更好的方法的一些信息可在此处获得:https: //caremad.io/blog/setup-vs-requirement/

回答by famousgarkin

While not an exact answer to the question, I recommend Donald Stufft's blog post at https://caremad.io/2013/07/setup-vs-requirement/for a good take on this problem. I've been using it to great success.

虽然不是这个问题的确切答案,但我推荐 Donald Stufft 的博客文章https://caremad.io/2013/07/setup-vs-requirement/来很好地解决这个问题。我一直在使用它取得了巨大的成功。

In short, requirements.txtis not a setup.pyalternative, but a deployment complement. Keep an appropriate abstraction of package dependencies in setup.py. Set requirements.txtor more of 'em to fetch specific versions of package dependencies for development, testing, or production.

总之,requirements.txt不是setup.py替代,而是部署的补充。在setup.py. 设置requirements.txt或多个 'em 以获取特定版本的包依赖项以进行开发、测试或生产。

E.g. with packages included in the repo under deps/:

例如,包含在 repo 下的包deps/

# fetch specific dependencies
--no-index
--find-links deps/

# install package
# NOTE: -e . for editable mode
.

pip executes package's setup.pyand installs the specific versions of dependencies declared in install_requires. There's no duplicity and the purpose of both artifacts is preserved.

pip 执行包setup.py并安装在install_requires. 没有口是心非,两个工件的目的都得到了保留。

回答by MikeTwo

BEWARE OF parse_requirementsBEHAVIOUR!

当心parse_requirements行为!

Please note that pip.req.parse_requirementswill change underscores to dashes. This was enraging me for a few days before I discovered it. Example demonstrating:

请注意,这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

produces

产生

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

回答by reubano

If you don't want to force your users to install pip, you can emulate its behavior with this:

如果您不想强迫您的用户安装 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'))
)

回答by fabianvf

Most of the other answers above don't work with the current version of pip's API. Here is the correct* way to do it with the current version of pip (6.0.8 at the time of writing, also worked in 7.1.2. You can check your version with pip -V).

上面的大多数其他答案不适用于当前版本的 pip API。这是使用当前版本的 pip 执行此操作的正确*方法(撰写本文时为 6.0.8,也适用于 7.1.2。您可以使用 pip -V 检查您的版本)。

from pip.req import parse_requirements
from pip.download import PipSession

install_reqs = parse_requirements(<requirements_path>, session=PipSession())

reqs = [str(ir.req) for ir in install_reqs]

setup(
    ...
    install_requires=reqs
    ....
)

* Correct, in that it is the way to use parse_requirements with the current pip. It still probably isn't the best way to do it, since, as posters above said, pip doesn't really maintain an API.

* 正确,这是在当前 pip 中使用 parse_requirements 的方法。它仍然可能不是最好的方法,因为正如上面的海报所说,pip 并没有真正维护 API。