简体   繁体   English

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

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

I have a requirements.txt file that I'm using with Travis-CI.我有一个与 Travis-CI 一起使用的requirements.txt文件。 It seems silly to duplicate the requirements in both requirements.txt and setup.py , so I was hoping to pass a file handle to the install_requires kwarg in setuptools.setup .requirements.txtsetup.py中复制需求似乎很愚蠢,所以我希望将文件句柄传递给 setuptools.setup 中的install_requires setuptools.setup

Is this possible?这可能吗? If so, how should I go about doing it?如果可以,我应该怎么办呢 go一下?

Here is my requirements.txt file:这是我的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

You can flip it around and list the dependencies in setup.py and have a single character — a dot .您可以翻转它并在setup.py中列出依赖项,并有一个字符 - 一个点. — in requirements.txt instead. — 在requirements.txt中。


Alternatively, even if not advised, it is still possible to parse the requirements.txt file (if it doesn't refer any external requirements by URL) with the following hack (tested with pip 9.0.1 ):或者,即使不建议,仍然可以使用以下 hack(使用pip 9.0.1测试)解析requirements.txt文件(如果它没有通过 URL 引用任何外部需求):

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

This doesn't filter environment markers though.不过,这不会过滤环境标记


In old versions of pip, more specifically older than 6.0 , there is a public API that can be used to achieve this.在旧版本的 pip 中,更具体地说是早于 6.0的版本,有一个公共 API 可用于实现此目的。 A requirement file can contain comments ( # ) and can include some other files ( --requirement or -r ).需求文件可以包含注释( # ),也可以包含其他一些文件( --requirement-r )。 Thus, if you really want to parse a requirements.txt you can use the pip parser:因此,如果您真的想解析requirements.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
)

On the face of it, it does seem that requirements.txt and setup.py are silly duplicates, but it's important to understand that while the form is similar, the intended function is very different.从表面上看, requirements.txtsetup.py似乎是愚蠢的重复,但重要的是要理解,虽然形式相似,但预期的功能却大不相同。

The goal of a package author, when specifying dependencies, is to say "wherever you install this package, these are the other packages you need, in order for this package to work."包作者在指定依赖项时的目标是说“无论您在何处安装此包,这些都是您需要的其他包,以便该包工作。”

In contrast, the deployment author (which may be the same person at a different time) has a different job, in that they say "here's the list of packages that we've gathered together and tested and that I now need to install".相比之下,部署作者(可能是不同时间的同一个人)有不同的工作,因为他们说“这是我们收集并测试过的软件包列表,我现在需要安装”。

The package author writes for a wide variety of scenarios, because they're putting their work out there to be used in ways they may not know about, and have no way of knowing what packages will be installed alongside their package.包作者为各种各样的场景写作,因为他们将他们的工作放在那里以他们可能不知道的方式使用,并且无法知道哪些包将与他们的包一起安装。 In order to be a good neighbor and avoid dependency version conflicts with other packages, they need to specify as wide a range of dependency versions as can possibly work.为了成为一个好邻居并避免与其他包的依赖版本冲突,他们需要指定尽可能广泛的依赖版本。 This is what install_requires in setup.py does.这就是setup.py中的install_requires所做的。

The deployment author writes for a very different, very specific goal: a single instance of an installed application or service, installed on a particular computer.部署作者写了一个非常不同的、非常具体的目标:安装在特定计算机上的已安装应用程序或服务的单个实例。 In order to precisely control a deployment, and be sure that the right packages are tested and deployed, the deployment author must specify the exact version and source-location of every package to be installed, including dependencies and dependency's dependencies.为了精确控制部署,并确保测试和部署正确的包,部署作者必须指定要安装的每个包的确切版本和源位置,包括依赖项和依赖项的依赖项。 With this spec, a deployment can be repeatably applied to several machines, or tested on a test machine, and the deployment author can be confident that the same packages are deployed every time.有了这个规范,一个部署可以重复地应用到多台机器上,或者在一台测试机器上进行测试,并且部署作者可以确信每次都部署了相同的包。 This is what a requirements.txt does.这就是requirements.txt所做的。

So you can see that, while they both look like a big list of packages and versions, these two things have very different jobs.所以你可以看到,虽然它们看起来都像一个很大的包和版本列表,但这两个东西有非常不同的工作。 And it's definitely easy to mix this up and get it wrong!而且很容易把它弄混并弄错! But the right way to think about this is that requirements.txt is an "answer" to the "question" posed by the requirements in all the various setup.py package files.但正确的思考方式是, requirements.txt是对所有各种setup.py包文件中的需求所提出的“问题”的“答案”。 Rather than write it by hand, it's often generated by telling pip to look at all the setup.py files in a set of desired packages, find a set of packages that it thinks fits all the requirements, and then, after they're installed, "freeze" that list of packages into a text file (this is where the pip freeze name comes from).它不是手动编写的,而是通过告诉 pip 查看一组所需包中的所有setup.py文件,找到一组它认为符合所有要求的包,然后在它们安装后生成, 将该包列表“冻结”到一个文本文件中(这是pip freeze名称的来源)。

So the takeaway:所以外卖:

  • setup.py should declare the loosest possible dependency versions that are still workable. setup.py应该声明仍然可行的最松散的可能依赖版本。 Its job is to say what a particular package can work with.它的工作是说明一个特定的包可以使用什么。
  • requirements.txt is a deployment manifest that defines an entire installation job, and shouldn't be thought of as tied to any one package. requirements.txt是一个部署清单,它定义了整个安装作业,不应被认为与任何一个包相关联。 Its job is to declare an exhaustive list of all the necessary packages to make a deployment work.它的工作是声明一个详尽的列表,列出所有必要的包以使部署工作。
  • Because these two things have such different content and reasons for existing, it's not feasible to simply copy one into the other.因为这两个东西有如此不同的内容和存在的理由,简单地把一个复制到另一个是行不通的。

References:参考:

It can't take a file handle.它不能接受文件句柄。 The install_requires argument 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,
...)

Requirements files use an expanded pip format, which is only useful if you need to complement your setup.py with stronger constraints, for example specifying the exact urls some of the dependencies must come from, or the output of pip freeze to freeze the entire package set to known-working versions.需求文件使用扩展的 pip 格式,这仅在您需要用更强的约束补充setup.py时才有用,例如指定某些依赖项必须来自的确切 url,或者pip freeze的输出以冻结整个包设置为已知工作版本。 If you don't need the extra constraints, use only a setup.py .如果您不需要额外的约束,请仅使用setup.py If you feel like you really need to ship a requirements.txt anyway, you can make it a single line:如果你觉得你真的需要发送一个requirements.txt ,你可以把它写成一行:

.

It will be valid and refer exactly to the contents of the setup.py that is in the same directory.它将是有效的,并且完全引用同一目录中的setup.py的内容。

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.虽然不是这个问题的确切答案,但我推荐 Donald Stufft 在https://caremad.io/2013/07/setup-vs-requirement/上的博客文章,以便很好地解决这个问题。 I've been using it to great success.我一直在使用它取得了巨大的成功。

In short, requirements.txt is not a setup.py alternative, but a deployment complement.简而言之, requirements.txt不是setup.py的替代品,而是部署的补充。 Keep an appropriate abstraction of package dependencies in setup.py .setup.py中保留适当的包依赖抽象。 Set requirements.txt or more of 'em to fetch specific versions of package dependencies for development, testing, or production.设置requirements.txt或更多的 'em 以获取特定版本的包依赖项以用于开发、测试或生产。

Eg with packages included in the repo under deps/ :例如,包含在deps/下的 repo 中的软件包:

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

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

pip executes package's setup.py and installs the specific versions of dependencies declared in install_requires . pip 执行包的setup.py并安装install_requires中声明的特定版本的依赖项。 There's no duplicity and the purpose of both artifacts is preserved.没有重复性,并且保留了两个工件的目的。

Using parse_requirements is problematic because the pip API isn't publicly documented and supported.使用parse_requirements是有问题的,因为 pip API 没有公开记录和支持。 In pip 1.6, that function is actually moving, so existing uses of it are likely to break.在 pip 1.6 中,该功能实际上正在移动,因此它的现有用途可能会中断。

A more reliable way to eliminate duplication between setup.py and requirements.txt is to specific your dependencies in setup.py and then put -e .消除setup.pyrequirements.txt之间重复的更可靠的方法是在setup.py中指定您的依赖项,然后放入-e . into your requirements.txt file.进入你的requirements.txt文件。 Some information from one of the pip developers about why that's a better way to go is available here: https://caremad.io/blog/setup-vs-requirement/此处提供了来自pip开发人员之一的一些关于为什么这是一种更好的方法的信息: https ://caremad.io/blog/setup-vs-requirement/

Most of the other answers above don't work with the current version of pip's API.上面的大多数其他答案都不适用于当前版本的 pip 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 的正确*方法(撰写本文时为 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. * 正确,因为这是将 parse_requirements 与当前 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 并没有真正维护 API。

I would not recommend doing such a thing.我不建议做这样的事情。 As mentioned multiple times install_requires and requirements.txt are definitely not supposed to be the same list.正如多次提到的install_requiresrequirements.txt绝对不应该是同一个列表。 But since there are a lot of misleading answers all around involving private internal APIs of pip , it might be worth looking at saner alternatives...但是,由于涉及pip的私有内部 API 有很多误导性的答案,因此可能值得寻找更明智的替代方案......


It is possible to parse a relatively simple requirements.txt file from a setuptools setup.py script without pip .可以从没有pipsetuptools setup.py脚本解析一个相对简单的requirements.txt文件。 The setuptools project already contains necessary tools in its top level package pkg_resources . setuptools项目已在其顶级pkg_resources中包含必要的工具。

It could more or less look like this:它或多或少看起来像这样:

#!/usr/bin/env python3

import pathlib

import pkg_resources
import setuptools

with pathlib.Path('requirements.txt').open() as requirements_txt:
    install_requires = [
        str(requirement)
        for requirement
        in pkg_resources.parse_requirements(requirements_txt)
    ]

setuptools.setup(
    install_requires=install_requires,
)

Again, this will work only for simple requirements.txt files.同样,这仅适用于简单的requirements.txt文件。 See Requirements parsing in the documentation page for pkg_resources to get details about what is handled.请参阅pkg_resources文档页面中的需求解析以获取有关处理内容的详细信息。 In short, each line should be a valid PEP 508 requirement .简而言之,每一行都应该是有效的PEP 508要求 Notations that are really specific to pip are not supported and it will cause a failure.不支持真正特定于pip的符号,这将导致失败。


Word of caution警告

As stated already, this is not recommended.如前所述,不建议这样做。 The requirements.txt file and the list of "install dependencies" are two different concepts, they are not interchangeable. requirements.txt文件和“安装依赖项”列表是两个不同的概念,它们不能互换。

But if you do write a setup.py install script that reads requirements.txt then make sure that the requirements.txt file is included in the "source distribution" ( sdist ) otherwise installation will obviously fail.但是,如果您确实编写了一个读取requirements.txtsetup.py安装脚本,那么请确保requirements.txt文件包含在“源分发”( sdist )中,否则安装显然会失败。


Since setuptools version 62.6 it is possible to write something like this in setup.cfg :从 setuptools 版本 62.6 开始,可以在setup.cfg中编写类似这样的内容:

[options]
install_requires = file: requirements.txt

Alternatively in pyproject.toml :或者在pyproject.toml

[project]
dynamic = ["dependencies"]

[tool.setuptools.dynamic]
dependencies = requirements.txt

Same words of caution as above apply:与上述相同的警告词适用:

  • only very simple files are supported仅支持非常简单的文件
  • the file must be added to the sdist该文件必须添加到sdist

Additionally, it is considered a "beta" feature for now.此外,它目前被认为是“测试版”功能。


Notes :备注

Install the current package in Travis.在 Travis 中安装当前包。 This avoids the use of a requirements.txt file.这避免了使用requirements.txt文件。 For example:例如:

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

This simple approach reads the requirements file from setup.py .这种简单的方法从setup.py读取需求文件。 It is a variation of the answer by Dmitiry S. .这是Dmitiry S.答案的变体。 This answer is compatible only with Python 3.6+.此答案仅与 Python 3.6+ 兼容。

Per DS , requirements.txt can document concrete requirements with specific version numbers, whereas setup.py can document abstract requirements with loose version ranges.根据DSrequirements.txt可以记录具有特定版本号的具体需求,而setup.py可以记录具有松散版本范围的抽象需求。

Below is an excerpt of my setup.py .以下是我的setup.py的摘录。

import distutils.text_file
from pathlib import Path
from typing import List

def _parse_requirements(filename: str) -> List[str]:
    """Return requirements from requirements file."""
    # Ref: https://stackoverflow.com/a/42033122/
    return distutils.text_file.TextFile(filename=str(Path(__file__).with_name(filename))).readlines()

setup(...
      install_requires=_parse_requirements('requirements.txt'),
   ...)

Note that distutils.text_file.TextFile will strip comments.请注意, distutils.text_file.TextFile将删除注释。 Also, per my experience, you apparently do not need to take any special step to bundle in the requirements file.此外,根据我的经验,您显然不需要采取任何特殊步骤来捆绑需求文件。

from pip.req import parse_requirements did 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,
    ...
)

The following interface became deprecated in pip 10:以下接口在 pip 10 中被弃用:

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

So I switched it just to simple text parsing:所以我将它切换为简单的文本解析:

with open('requirements.txt', 'r') as f:
    install_reqs = [
        s for s in [
            line.split('#', 1)[0].strip(' \t\n') for line in f
        ] if s != ''
    ]

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'))
)

BEWARE OF parse_requirements BEHAVIOUR!当心parse_requirements行为!

Please note that pip.req.parse_requirements will change underscores to dashes.请注意pip.req.parse_requirements会将下划线更改为破折号。 This was enraging me for a few days before I discovered it.在我发现它之前,这让我愤怒了几天。 Example demonstrating:示例演示:

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']

I created a reusable function for this.我为此创建了一个可重用的函数。 It actually parses an entire directory of requirements files and sets them to extras_require.它实际上解析了需求文件的整个目录并将它们设置为 extras_require。

Latest always available here: https://gist.github.com/akatrevorjay/293c26fefa24a7b812f5最新的总是在这里可用: https ://gist.github.com/akatrevorjay/293c26fefa24a7b812f5

import glob
import itertools
import os

# This is getting ridiculous
try:
    from pip._internal.req import parse_requirements
    from pip._internal.network.session import PipSession
except ImportError:
    try:
        from pip._internal.req import parse_requirements
        from pip._internal.download import PipSession
    except ImportError:
        from pip.req import parse_requirements
        from pip.download import PipSession


def setup_requirements(
        patterns=[
            'requirements.txt', 'requirements/*.txt', 'requirements/*.pip'
        ],
        combine=True):
    """
    Parse a glob of requirements and return a dictionary of setup() options.
    Create a dictionary that holds your options to setup() and update it using this.
    Pass that as kwargs into setup(), viola

    Any files that are not a standard option name (ie install, tests, setup) are added to extras_require with their
    basename minus ext. An extra key is added to extras_require: 'all', that contains all distinct reqs combined.

    Keep in mind all literally contains `all` packages in your extras.
    This means if you have conflicting packages across your extras, then you're going to have a bad time.
    (don't use all in these cases.)

    If you're running this for a Docker build, set `combine=True`.
    This will set `install_requires` to all distinct reqs combined.

    Example:

    >>> import setuptools
    >>> _conf = dict(
    ...     name='mainline',
    ...     version='0.0.1',
    ...     description='Mainline',
    ...     author='Trevor Joynson <github@trevor.joynson,io>',
    ...     url='https://trevor.joynson.io',
    ...     namespace_packages=['mainline'],
    ...     packages=setuptools.find_packages(),
    ...     zip_safe=False,
    ...     include_package_data=True,
    ... )
    >>> _conf.update(setup_requirements())
    >>> # setuptools.setup(**_conf)

    :param str pattern: Glob pattern to find requirements files
    :param bool combine: Set True to set install_requires to extras_require['all']
    :return dict: Dictionary of parsed setup() options
    """
    session = PipSession()

    # Handle setuptools insanity
    key_map = {
        'requirements': 'install_requires',
        'install': 'install_requires',
        'tests': 'tests_require',
        'setup': 'setup_requires',
    }
    ret = {v: set() for v in key_map.values()}
    extras = ret['extras_require'] = {}
    all_reqs = set()

    files = [glob.glob(pat) for pat in patterns]
    files = itertools.chain(*files)

    for full_fn in files:
        # Parse
        reqs = {
            str(r.req)
            for r in parse_requirements(full_fn, session=session)
            # Must match env marker, eg:
            #   yarl ; python_version >= '3.0'
            if r.match_markers()
        }
        all_reqs.update(reqs)

        # Add in the right section
        fn = os.path.basename(full_fn)
        barefn, _ = os.path.splitext(fn)
        key = key_map.get(barefn)

        if key:
            ret[key].update(reqs)
            extras[key] = reqs

        extras[barefn] = reqs

    if 'all' not in extras:
        extras['all'] = list(all_reqs)

    if combine:
        extras['install'] = ret['install_requires']
        ret['install_requires'] = list(all_reqs)

    def _listify(dikt):
        ret = {}

        for k, v in dikt.items():
            if isinstance(v, set):
                v = list(v)
            elif isinstance(v, dict):
                v = _listify(v)
            ret[k] = v

        return ret

    ret = _listify(ret)

    return ret


__all__ = ['setup_requirements']

if __name__ == '__main__':
    reqs = setup_requirements()
    print(reqs)

Another possible solution...另一种可能的解决方案...

def gather_requirements(top_path=None):
    """Captures requirements from repo.

    Expected file format is: requirements[-_]<optional-extras>.txt

    For example:

        pip install -e .[foo]

    Would require:

        requirements-foo.txt

        or

        requirements_foo.txt

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

    session = PipSession()
    top_path = top_path or os.path.realpath(os.getcwd())
    extras = {}
    for filepath in tree(top_path):
        filename = os.path.basename(filepath)
        basename, ext = os.path.splitext(filename)
        if ext == '.txt' and basename.startswith('requirements'):
            if filename == 'requirements.txt':
                extra_name = 'requirements'
            else:
                _, extra_name = re.split(r'[-_]', basename, 1)
            if extra_name:
                reqs = [str(ir.req) for ir in parse_requirements(filepath, session=session)]
                extras.setdefault(extra_name, []).extend(reqs)
    all_reqs = set()
    for key, values in extras.items():
        all_reqs.update(values)
    extras['all'] = list(all_reqs)
    return extras

and then to use...然后使用...

reqs = gather_requirements()
install_reqs = reqs.pop('requirements', [])
test_reqs = reqs.pop('test', [])
...
setup(
    ...
    'install_requires': install_reqs,
    'test_requires': test_reqs,
    'extras_require': reqs,
    ...
)

Cross posting my answer from this SO question for another simple, pip version proof solution.交叉发布我从这个 SO 问题的答案,以获得另一个简单的 pip 版本证明解决方案。

try:  # for pip >= 10
    from pip._internal.req import parse_requirements
    from pip._internal.download import PipSession
except ImportError:  # for pip <= 9.0.3
    from pip.req import parse_requirements
    from pip.download import PipSession

requirements = parse_requirements(os.path.join(os.path.dirname(__file__), 'requirements.txt'), session=PipSession())

if __name__ == '__main__':
    setup(
        ...
        install_requires=[str(requirement.req) for requirement in requirements],
        ...
    )

Then just throw in all your requirements under requirements.txt under project root directory.然后只需在项目根目录下的requirements.txt下输入所有要求。

Yet another parse_requirements hack that also parses environment markers into extras_require :另一个parse_requirements hack 也将环境标记解析为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
)

It should support both sdist and binary dists.它应该支持 sdist 和二进制 dist。

As stated by others, parse_requirements has several shortcomings, so this is not what you should do on public projects, but it may suffice for internal/personal projects.正如其他人所说, parse_requirements有几个缺点,因此这不是您在公共项目中应该做的,但对于内部/个人项目可能就足够了。

I did this:我这样做了:

import re

def requirements(filename):
    with open(filename) as f:
        ll = f.read().splitlines()
    d = {}
    for l in ll:
        k, v = re.split(r'==|>=', l)
        d[k] = v
    return d

def packageInfo():
    try:
        from pip._internal.operations import freeze
    except ImportError:
        from pip.operations import freeze

    d = {}
    for kv in freeze.freeze():
        k, v = re.split(r'==|>=', kv)
        d[k] = v
    return d

req = getpackver('requirements.txt')
pkginfo = packageInfo()

for k, v in req.items():
    print(f'{k:<16}: {v:<6} -> {pkginfo[k]}')

Here is a complete hack (tested with pip 9.0.1 ) based on Romain's answer that parses requirements.txt and filters it according to current environment markers :这是基于Romain 的回答的完整破解(使用pip 9.0.1测试),它解析requirements.txt并根据当前环境标记对其进行过滤:

from pip.req import parse_requirements

requirements = []
for r in parse_requirements('requirements.txt', session='hack'):
    # check markers, such as
    #
    #     rope_py3k    ; python_version >= '3.0'
    #
    if r.match_markers():
        requirements.append(str(r.req))

print(requirements)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 pip-compile 用于 setup.py 的 install_requires 而不是 requirements.txt - pip-compile for setup.py's install_requires instead of requirements.txt 为什么使用点“.”从 setup.py 安装 `install_requires` 在 Dockerfile 中的 requirements.txt 失败? - Why does installing `install_requires` from setup.py using a dot “.” in the requirements.txt fail in Dockerfile? 何时在setup.py中使用pip requirements文件与install_requires? - When to use pip requirements file versus install_requires in setup.py? Requirements.txt中的python版本与setup.py中的安装问题有关 - python version in requirements.txt issues with install requires in setup.py setup.py install_requires 应该包含 setuptools 的任何原因? - Any reason setup.py install_requires should include setuptools? 如何在setup.py中访问setuptools的install_requires安装的模块? - How to access a module installed by setuptools' install_requires within setup.py? 使用 requirements.txt 的 setup.py 文件 - setup.py file using requirements.txt requirements.txt 与 setup.py - requirements.txt vs setup.py 如何安装 github zip 文件与 pip 和 setup.py 来自 requirements.txt? - How can I install a github zip file with pip and setup.py from requirements.txt? python setup.py install 忽略 install_requires - python setup.py install ignores install_requires
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM