如何从Python包的requires.txt读取依赖项

如何从Python包的requires.txt读取依赖项,python,Python,我需要依赖项,因为我想将它们添加到我的RPM元数据中 要构建我的应用程序,请使用: python setup.py bdist_rpm 当我构建包cryptography-2.2.2时,它会创建一个文件/src/cryptography.egg info/requires.txt 它包括: idna>=2.1 asn1crypto>=0.21.0 six>=1.4.1 [:platform_python_implementation != 'PyPy'] cffi>=

我需要依赖项,因为我想将它们添加到我的RPM元数据中

要构建我的应用程序,请使用:

python setup.py bdist_rpm
当我构建包
cryptography-2.2.2
时,它会创建一个文件
/src/cryptography.egg info/requires.txt

它包括:

idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1

[:platform_python_implementation != 'PyPy']
cffi>=1.7

[:python_version < '3']
enum34
ipaddress

我想省略其他部分,如
[doc]
[test]
等等。

因此我能够找到一个解决方案,可能还有其他可能性,但我认为这应该适用于大多数版本

import pkg_resources

lines = open("requirements.txt").readlines()

load_packages = True
for line in lines:
    if line.strip().startswith("#"):
        continue

    if line.startswith("[:"):
        # this is marker, let's evaluate
        load_packages = pkg_resources.evaluate_marker(line.strip()[2:-1])
        continue
    elif line.startswith("["):
        # this is a subsection ignore it
        load_packages = False

    if load_packages and line.strip():
        print(line.strip())
其输出如下所示

idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1
cffi>=1.7
enum34
ipaddress
如果我像下面那样更改
requirements.txt

idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1

[:platform_python_implementation == 'PyPy']
cffi>=1.7

[:python_version > '3']
enum34
ipaddress
输出更改为

idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1

requires.txt
是的一部分,因此您可以使用安装egg时使用的相同工具。假设文件
requires.txt
位于当前目录中:

In [1]: from pkg_resources import Distribution, PathMetadata

In [2]: dist = Distribution(metadata=PathMetadata('.', '.'))
现在,您可以使用
Distribution.requires()
筛选当前平台的所有依赖项:

如果我使用Python 2.7,列表会有所不同:

In [4]: sys.version
Out[4]: '2.7.10 (default, Oct  6 2017, 22:29:07) \n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]'

In [5]: dist.requires()
Out[5]:
[Requirement.parse('idna>=2.1'),
 Requirement.parse('asn1crypto>=0.21.0'),
 Requirement.parse('six>=1.4.1'),
 Requirement.parse('cffi!=1.11.3,>=1.7'),
 Requirement.parse('cffi>=1.7'),
 Requirement.parse('enum34'),
 Requirement.parse('ipaddress')]
或PyPy:

In [2]: sys.version
Out[2]: '3.5.3 (fdd60ed87e941677e8ea11acf9f1819466521bf2, Apr 26 2018, 01:25:35)\n[PyPy 6.0.0 with GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)]'

In [3]: d.requires()
Out[3]:
[Requirement.parse('idna>=2.1'),
 Requirement.parse('asn1crypto>=0.21.0'),
 Requirement.parse('six>=1.4.1'),
 Requirement.parse('cffi!=1.11.3,>=1.7')]
现在,如果您想生成需求字符串列表(例如,当您想为
pip
生成需求文件时),请将需求转换为字符串:

In [8]: os.linesep.join(str(r) for r in dist.requires())
Out[8]:
'idna>=2.1\nasn1crypto>=0.21.0\nsix>=1.4.1\ncffi!=1.11.3,>=1.7'

政治公众人物508 如果您还想独立于当前平台考虑,事情可能会变得有点棘手,但仍然可以管理。首先,使用环境标记转换需求:

In [22]: dep_map_pep508 = {k: v for k, v in dist._build_dep_map().items() if k and k.startswith(':')}

In [24]: reqs_pep508 = [str(r) + ';' + k.lstrip(':') for k, v in dep_map_pep508.items() for r in v]

In [25]: reqs_pep508
Out[25]:
["cffi>=1.7;platform_python_implementation != 'PyPy'",
 "enum34;python_version >= '3'",
 "ipaddress;python_version >= '3'"]
现在处理独立于平台的DEP,这些DEP位于
None
下,输入
dist
的依赖关系图:

In [26]: reqs_no_platform = [str(r) for r in dist._build_dep_map()[None]]

In [27]: reqs_no_platform
Out[27]: ['idna>=2.1', 'asn1crypto>=0.21.0', 'six>=1.4.1', 'cffi!=1.11.3,>=1.7']
将两个列表合并为一个字符串,准备写入需求文件:

In [28]: os.linesep.join(reqs_no_platform + reqs_pep508)
Out[28]: "idna>=2.1\nasn1crypto>=0.21.0\nsix>=1.4.1\ncffi!=1.11.3,>=1.7\ncffi>=1.7;platform_python_implementation != 'PyPy'\nenum34;python_version >= '3'\nipaddress;python_version >= '3'"

您可以测试每行的开头是否与pip依赖项声明格式匹配

正则表达式可能是模式匹配的最佳解决方案:

pattern = r'^[\w\d_-]+'
其中:

  • ^
    与行/文件的开头匹配
  • [\w\d\u-]+
    多次匹配任何单词(
    \w
    )、数字(
    \d
    )、下划线(
    \u
    )和破折号(
    -
总而言之:

from re import match

pattern = r'^[\w\d_-]+'
with open('requires.txt') as file:
    for line in file:
        if match(pattern, line):
            print(line.strip())
结果:

$ python2.7 test.py
idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1
cffi>=1.7
enum34
ipaddress

你试过了吗?@ChristianSauer当我安装pip时,它会计算表达式,例如
[:python\u version<'3']
。我想访问在包的构建过程中使用的内容。
pip freeze
after install提供了正确安装的版本。不是每个依赖项,而是整个集合。否则,请查看
pipenv
@thepot Good idea,但它还列出了构建时依赖项,这些依赖项不是运行时依赖项。我不需要在目标系统上构建文档所需的工具,只需要运行它所需的最低限度。我将签出
pipenv
。此文件与pip兼容吗?因为我的pip会在这个需求文件中出错,错误是
invalidRequest:Invalid requirement,在“u'[:platfo'”
处解析错误,这很好。它允许我在构建包后获取完整的列表。谢谢!很好。这在大多数情况下都有效,但我想省略其他部分,如[doc],[test]等等。被接受的答案涵盖了这一点。@EddyPronk我的解决方案省略了这些部分,但我同意,被接受的答案也是我会同意的。
from re import match

pattern = r'^[\w\d_-]+'
with open('requires.txt') as file:
    for line in file:
        if match(pattern, line):
            print(line.strip())
$ python2.7 test.py
idna>=2.1
asn1crypto>=0.21.0
six>=1.4.1
cffi>=1.7
enum34
ipaddress