如何查找Python包的依赖关系

223

如何以编程方式获取Python包的依赖项列表?

标准的setup.py文档中有这些信息,但我找不到一个简单的方法来从Python或命令行中访问它。

理想情况下,我正在寻找类似于:

$ pip install somepackage --only-list-deps
kombu>=3.0.8
billiard>=3.3.0.13
boto>=2.26
或:
>>> import package_deps
>>> package = package_deps.find('somepackage')
>>> print package.dependencies
['kombu>=3.0.8', 'billiard>=3.3.0.13', 'boto>=2.26']

请注意,我不是在谈论导入一个包并找到所有已引用的模块。虽然这可能会找到大多数依赖包,但它无法找到所需的最小版本号。这只存储在setup.py文件中。


1
这里有很多答案展示了pip被导入到程序中使用。pip的文档强烈建议不要使用pip进行这种用法。如果这些解决方案中的任何一个被用于重要内容,这是需要铭记在心的事情。 - Jordan Mackie
11个回答

196

除了pip show [包名]命令之外,还有pipdeptree

只需执行

$ pip install pipdeptree

然后运行

$ pipdeptree

它会以树形结构显示您的依赖项,例如:

flake8==2.5.0
  - mccabe [required: >=0.2.1,<0.4, installed: 0.3.1]
  - pep8 [required: !=1.6.0,>=1.5.7,!=1.6.1,!=1.6.2, installed: 1.5.7]
  - pyflakes [required: >=0.8.1,<1.1, installed: 1.0.0]
ipdb==0.8
  - ipython [required: >=0.10, installed: 1.1.0]

该项目位于https://github.com/naiquevin/pipdeptree,您还可以在此找到使用信息。


17
pipdeptree可以展示所有已安装的软件包的依赖关系,而不仅仅是某个给定软件包的依赖关系。虽然你可以筛选其--json输出,但它仍然取决于这些软件包已经被安装。 - sschuberth
3
真的,但当你想知道为什么安装了不在你的“requirements.txt”中的包时,这个答案仍然很有用 :) - beruic
6
另外,您可以使用-p选项来仅选择几个要探索其依赖关系的软件包。 - Zaccharie Ramzi
4
优化requirements.txt时,pipdeptree非常有用。$ pipdeptree | grep -P '^\w+'
这将只输出顶级包。更多信息在这里
- NFT Master
1
@DeepamGupta 只需要在虚拟环境中使用 pipdeptree 即可。 - beruic
显示剩余3条评论

114
尝试在pip中使用show命令,例如:show
$ pip show tornado
---
Name: tornado
Version: 4.1
Location: *****
Requires: certifi, backports.ssl-match-hostname

更新(使用指定版本检索依赖项):

from pip._vendor import pkg_resources


_package_name = 'somepackage'
_package = pkg_resources.working_set.by_key[_package_name]

print([str(r) for r in _package.requires()])  # retrieve deps from setup.py

Output: ['kombu>=3.0.8', 
         'billiard>=3.3.0.13', 
         'boto>=2.26']

3
这告诉你软件包的版本,而不是它的依赖关系;它们只是被列出来了。 - jonrsharpe
请查看“要求”部分。 - Alex Lisovoy
5
可以,但是它没有显示“所需的最低版本号”,正如问题提出者要求的那样。 - jonrsharpe
2
一些奇怪的事情,$ pip3 show beautifulsoup4 对我来说显示了空的 Requires: -- beautifulsoup4 不依赖于任何东西吗? - xealits
1
@xealits,我刚刚尝试在虚拟环境中安装beautifulsoup4,看起来它似乎不依赖于任何东西。 - PythonJin
8
@PythonJin,是的,显然它只使用了标准包...我只是有点惊讶。做得好,beautifulsoup4 - xealits

42

关于在Windows计算机上发现和测试的方法的简要概述:

  1. 解析 PyPI 的 json 文件:https://pypi.org/pypi/<package>/<version>/json#7
  2. 检查 /site-packages/<package-version>.dist-info/METADATA#13,需要预安装)
  3. pipdeptree 包(#3#8,需要预安装)
  4. 使用 setuptools 包的 import pkg_resources#4,需要预安装)
  5. 检查 https://libraries.io/#5
  6. 编写一个脚本,包含 import pippip._vendor.pkg_resources:已弃用(#6
  7. conda search [package_name] --info
    • 可以列出其他软件包:vc2015_runtimepython_abilibflang 等等。
    • 请注意,此方法可以根据构建和通道列出不同的软件包。
      例如:conda search "Django==3.2" --info -c conda-forge
  8. pip show <package>#1#2,需要预安装)
  9. Johnnydep 包(#10):在许多情况下测试会卡住。
  10. pip install --no-install <package>:已弃用(#11
  11. pip install --download <package>:已弃用(#12
  12. conda info [package_name]:已弃用(#9
django 3.2 pyhd3eb1b0_0
-----------------------
file name   : django-3.2-pyhd3eb1b0_0.conda
...
timestamp   : 2021-04-06 20:19:41 UTC
dependencies:
  - asgiref
  - psycopg2
  - python
  - pytz
  - sqlparse

django 3.2 pyhd8ed1ab_0
-----------------------
file name   : django-3.2-pyhd8ed1ab_0.tar.bz2
...
timestamp   : 2021-04-07 21:15:25 UTC
dependencies:
  - asgiref >=3.3.2,<4
  - python >=3.6
  - pytz
  - sqlparse >=0.2.2

需要注意的一点是每种方法都可能提供不同的结果。
例如,requests/setup.py identifies 需要 chardetidnaurllib3certifi。此外,还可能需要额外的包,如 pyOpenSSLcryptographysocksPySockswin-inet-pton
  • 方法1和2与之相符。
  • 方法8只列出所有依赖项(按下“探索依赖项”按钮会卡住)。
  • 方法12只列出 chardetidnaurllib3certifi
  • 如果在Linux docker中使用pip安装requests,则方法5和7不列出任何依赖项。
  • 如果在Windows机器的conda环境中安装requests,则方法5、7、9和10会列出chardetidnaurllib3certifi

非常好的帖子!总结一下,唯一不被弃用、不需要预先安装且可靠的选项是#1(解析pypi.org的JSON)和#8(libraries.io)。 - wisbucky
libraries.io的“探索依赖”按钮现在对我起作用了。例如,https://libraries.io/conda/pandas/2.0.3/tree 返回了结果。 - wisbucky

18
标准库自Python 3.8版本起提供了importlib.metadata.requires()函数:
In [1]: from importlib.metadata import requires

In [2]: requires('pytype')
Out[2]:
['attrs (>=21.2.0)',
 'importlab (>=0.6.1)',
 'libcst',
 'ninja (>=1.10.0.post2)',
 'pyyaml (>=3.11)',
 'six',
 'tabulate',
 'toml',
 'typed-ast (>=1.4.3)',
 'dataclasses ; python_version < "3.7"']

对于较旧版本的Python,可以使用importlib-metadata。有关信息请参阅Python文档中的相关章节:发行版要求

如果您需要解析requires()返回的字符串,则强烈建议使用packaging库,因为其packaging.requirements模块是PEP 508的参考实现。以下是一个包含复杂依赖说明的示例:

In [3]: from packaging.requirements import Requirement

In [4]: spec = 'requests [security,test] (>=2.21.0) ; implementation_name == "cpython"'

In [5]: r = Requirement(spec)

In [6]: r.name
Out[6]: 'requests'

In [7]: r.specifier
Out[7]: <SpecifierSet('>=2.21.0')>

In [8]: r.extras
Out[8]: {'security', 'test'}

In [9]: r.marker
Out[9]: <Marker('implementation_name == "cpython"')>

14

可以使用https://libraries.io/。那是一个很好的地方,在使用pip安装前探索依赖关系。

例如,输入google-cloud-storage并搜索,你就可以找到该库的页面(https://libraries.io/rubygems/google-cloud-storage)。从“Releases”中选择您要探索其依赖项的版本(默认情况下为最新版本),在“Dependencies”下,您可以找到依赖项列表和其支持的版本。


很棒,它可以显示特定版本软件包的依赖树 https://libraries.io/pypi/channels/2.4.0/tree - Harry Moreno

13

这里有不少答案展示了将pip导入程序来使用。 pip的文档强烈建议不要这样使用pip

你可以直接导入pkg_resources并使用相同的逻辑(实际上这是pip文档中针对想要以编程方式查看软件包元信息的用户提供的建议之一), 而不是通过pip的导入访问pkg_resources

import pkg_resources

_package_name = 'yourpackagename'
  
def get_dependencies_with_semver_string():
    package = pkg_resources.working_set.by_key[_package_name]
    return [str(r) for r in package.requires()]
如果您无法确定包名称,请使用pkg_resources.working_set返回的WorkingSet实例实现__iter__,以便打印所有包名称,并希望您能在其中找到您要找的包名称 :)

例如:

import pkg_resources

def print_all_in_working_set():
    ws = pkg_resources.working_set
    for package_metadata in ws:
        print(package_metadata)

这适用于Python 2和3(尽管您需要调整Python 2的打印语句)。


5

有一个很好的工具johnnydep。它的好处在于无需安装要检查的软件包。

pip install johnnydep

使用方法如下:

johnnydep deepspeech-tflite

4

这是一个过时的答案,对于现代PIP版本应该避免使用,并留在这里以供参考旧版PIP版本。 Alex的回答很好(+1)。在Python中:

pip._vendor.pkg_resources.working_set.by_key['twisted'].requires()

应该返回类似以下的内容。
[Requirement.parse('zope.interface>=3.6.0')]

此处的twisted是一个软件包的名称,您可以在字典中找到它:

pip._vendor.pkg_resources.WorkingSet().entry_keys

列举所有内容:

dict = pip._vendor.pkg_resources.WorkingSet().entry_keys
for key in dict:
    for name in dict[key]:
        req =pip._vendor.pkg_resources.working_set.by_key[name].requires()
        print('pkg {} from {} requires {}'.format(name,
                                                  key,
                                                  req))

应该会给你这样的列表:
pkg pyobjc-framework-syncservices from /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC requires [Requirement.parse('pyobjc-core>=2.5.1'), Requirement.parse('pyobjc-framework-Cocoa>=2.5.1'), Requirement.parse('pyobjc-framework-CoreData>=2.5.1')]

最近的版本有什么变化吗?在pip版本19.1.1中似乎不存在_vendor属性(编辑:好吧,在最新的Python版本中,它似乎已经移动到了pkg_resources包中!) - Prahlad Yeri
是的,情况已经改变了,我将考虑更新此内容或删除它,以支持下面的建议。 - cgseller
从我的角度来看,Alex的回答只是在某种程度上更好(pip show部分很好,其他部分不好)。您可以使用pip showpipdeptree或查看Jordan Mackie的回答,直接使用setuptoolspkg_resources - sinoroc

3

看起来有一种简单的方法:

pip install --dry-run somepackage


(注意:上面的内容已经被翻译好了,无需再次翻译)

3
请注意,在模拟运行期间将下载所需的依赖项。 - J. Choi

2

根据这篇文章中的指导,尝试在Python中执行以下操作:

import pip 
installed_packages = pip.get_installed_distributions()
installed_packages_list = sorted(["%s==%s" % (i.key, i.version)
     for i in installed_packages]) 
print(installed_packages_list)

它将显示为:
['behave==1.2.4', 'enum34==1.0', 'flask==0.10.1', 'itsdangerous==0.24', 
 'jinja2==2.7.2', 'jsonschema==2.3.0', 'markupsafe==0.23', 'nose==1.3.3', 
 'parse-type==0.3.4', 'parse==1.6.4', 'prettytable==0.7.2', 'requests==2.3.0',
 'six==1.6.1', 'vioozer-metadata==0.1', 'vioozer-users-server==0.1', 
 'werkzeug==0.9.4']

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接