确定所需的 Python 模块及其 Python 进程的版本

Posted

技术标签:

【中文标题】确定所需的 Python 模块及其 Python 进程的版本【英文标题】:Figuring the required Python modules and their versions of a Python process 【发布时间】:2021-11-18 23:32:50 【问题描述】:

我有一个跟踪进程系统调用的工具。这样我就知道进程使用的所有文件/区域。我有一个正在执行的 Python 脚本(创建一个进程)。我知道运行期间使用的所有文件,例如脚本本身。我也知道使用的模块的文件。模块安装在/tmp/vendor

根据我发现的/tmp/vendor 中的文件,我试图确定模块名称和模块版本,以便我可以为 pip 创建一个需求文件,然后使用pip install 安装它们(到其他一些目录)。基本上,我希望能够了解 Python 进程的所有模块依赖项。这些模块可能来自不同的领域,但让我们专注于一个 (/tmp/vendor)。我将模块安装到/tmp/vendor 的方式是:

pip install --requirement requirements.txt --target /tmp/vendor

现在我希望能够基于/tmp/vendor 中的文件构建这个requirements.txt 文件。

解决方案可以是动态的或静态的。起初我试图以静态方式解决它 - 检查/tmp/vendor中的文件。我做了一个例子——我安装了requests

pip install requests --target /tmp/vendor

据我了解,它会安装最新版本。在vendor里面我有:

ls -la vendor/
total 52
drwxr-x--- 13 user group 4096 Sep 26 17:37 .
drwxr-x---  8 user group 4096 Sep 26 17:37 ..
drwxr-x---  2 user group 4096 Sep 26 17:37 bin
drwxr-x---  3 user group 4096 Sep 26 17:37 certifi
drwxr-x---  2 user group 4096 Sep 26 17:37 certifi-2021.5.30.dist-info
drwxr-x---  5 user group 4096 Sep 26 17:37 charset_normalizer
drwxr-x---  2 user group 4096 Sep 26 17:37 charset_normalizer-2.0.6.dist-info
drwxr-x---  3 user group 4096 Sep 26 17:37 idna
drwxr-x---  2 user group 4096 Sep 26 17:37 idna-3.2.dist-info
drwxr-x---  3 user group 4096 Sep 26 17:37 requests
drwxr-x---  2 user group 4096 Sep 26 17:37 requests-2.26.0.dist-info
drwxr-x---  6 user group 4096 Sep 26 17:37 urllib3
drwxr-x---  2 user group 4096 Sep 26 17:37 urllib3-1.26.7.dist-info

现在我可以看到它还安装了其他需要的模块,例如urllib3idna。 例如,我的工具发现我正在使用:

/tmp/vendor/requests/utils.py

我还注意到每个模块的格式:

$NAME-(.*).dist-info

组是模块的版本。所以起初我以为我可以解析/tmp/vendor/(.*)/.*并获取模块名称($NAME)然后查找$NAME-(.*).dist-info,但问题是我注意到某些模块没有这个*.dist-info目录所以我无法确定模块的版本,这让我放弃了这种方法。

我还尝试了一些动态方法——我知道使用了哪个 python 版本,我可以运行 python 并尝试加载模块。但我找不到找到模块版本的方法。

总而言之 - 我正在寻找一种可靠的方法来计算我的 Python 进程运行所需的模块。这些模块应该带有它们的版本。所有模块都是使用 pip 安装的,因此应该可以简化任务。怎么办?

【问题讨论】:

我偶然选择了“来自可靠来源的答案”。我实际上正在寻找解决方案的建议。 对于特定的 Python 模块,可以找出它属于哪个分发包(假设它已正确安装并具有正确的元数据):***.com/a/60975978/11138259 -- 你也可以查看@987654322 @ 或类似的工具。 -- 还有这个:docs.python.org/3/library/modulefinder.html 【参考方案1】:

如果安装了模块,您应该能够使用创建 requirements.txt 文件 pip freeze > requirements.txt.创建 venv 以使用它。所有模块都必须使用 pip 安装。 你也可以看看类似的答案: Retrieving the requirements of a Python single script

【讨论】:

但是pip freeze 给了我所有的模块。我只想要使用过的那个。一些获得路径并返回模块名称和版本的机制之王。不是全部。【参考方案2】:

导航到site_packages(或等效)目录后,迭代运行以下命令并将结果收集到字典中:

pkg_resources.require('dep')

其中dep 是在site_packages(或等效)目录中看到的依赖项。这将为您提供一个依赖关系字典,可以从中重建 requirements.txt。

例如,虚拟环境的site_package包含以下目录:

black
cairo
click
...

现在,以下生成版本信息:

import pkg_resources
pkg_resources.require("black")
print(dep.key : dep.version for dep in pkg_resources.require("black"))

这会导致:

'black': '21.9b0', 'click': '8.0.1', 'mypy-extensions': '0.4.3', 'regex': '2021.8.28', 'platformdirs': '2.3.0', 'tomli': '1.2.1', 'typing-extensions': '3.10.0.2', 'pathspec': '0.9.0'

注意 类似的方法可能是

import pkg_resources
dep.key : dep.version for dep in pkg_resources.working_set

但是,这将产生来自sys.path 的所有内容,而不仅仅是site_packages 中存在的依赖项

【讨论】:

【参考方案3】:

您应该首先获取相关路径下所有已安装软件包的列表

pip list --format json --path /tmp/vendor

这将为您(以 JSON 格式)提供所有软件包的列表以及安装在指定路径下的版本。

假设您发现安装了包 foobar,那么您可以使用以下命令获取每个包中包含的文件

pip show --files foo bar

很遗憾,此命令的输出不能以 json 格式提供,但它遵循某种我认为可以很好解析的格式。

这样你最终会得到一个文件列表,你知道每个文件来自哪个包。

请注意,python 将 *.py 文件编译为 *.pyc,因此您的进程监视器可能会为您提供 *.pyc 文件,这些文件当然不在列表中。但是您可以在进行查找之前将扩展名从 *.pyc 更改为 *.py。

【讨论】:

【参考方案4】:

使用importlib.metadata

这是如今的首选方式,因为importlib.metadata 自 Python 3.8 起已成为标准库的一部分;对于旧版本,有一个向后移植 importlib-metdata

from importlib import metadata as m

dists = m.distributions(path=['/tmp/vendor'])
for d in dists:
    print('Found package', d.metadata['Name'], '==', d.metadata['Version'])

旧版:使用pkg_resources

这已被importlib.metadata 长期取代,仅出于完整性考虑而列出。

import pkg_resources

dists = pkg_resources.find_distributions('/tmp/vendor')
for d in dists:
    print('Found package', d.project_name, '==', d.version)

有关find_distributions() 上的文档,请参阅GETTING OR CREATING DISTRIBUTIONS。

【讨论】:

非常感谢,如果我知道模块的位置,pkg_resources 工作得很好。有没有办法根据一个文件来计算 python 包?例如,对于requests,查看文件/tmp/vendor/requests/api.py。我试图使用find_distributions,但它没有用。也没有找到pkg_resources 的方法,但也许我错过了一个。你知道怎么做吗? 除了扫描所有文件的父目录,在第一个非空结果处停止(如for parent in pathlib.Path(module.__file__).parents: yield from pkg_resources.find_distributions(parent) 等)之外,没有可靠的方法可以做到这一点。这是因为文件本身不提供任何分发元数据;如果我mkdir requests && touch requests/api.py,分发元数据应该从哪里来?您必须以一种或另一种方式自己找到用户站点目录。【参考方案5】:

你能在那台机器上安装库吗? 如果您可以访问源代码(并且如果我正确理解了问题),您可以尝试使用几个库来检查要求。 检查pipreqs 和/或pigarpip install pireqspip install pigar

编辑: 使用pigar,我在扫描文件夹时得到以下输出:

 > pigar -c /path/of/code/folder
 [...]
 ===============================
  PACKAGE    | CURRENT | LATEST
  -----------+---------+-------
  Pillow     | 8.3.2   | 8.3.2 
  matplotlib | 3.3.3   | 3.4.3
  numpy      | 1.19.4  | 1.21.2
  pyserial   | 3.5     | 3.5
 ===============================

【讨论】:

以上是关于确定所需的 Python 模块及其 Python 进程的版本的主要内容,如果未能解决你的问题,请参考以下文章

使用 cx_freeze 构建的 Exe 会为所需的包产生“找不到模块”错误

python 对MySQL数据库插入数据,并记录插入数据所需的时间

使用 Python 请求测量网站加载时间

如何确定特定 win32 api 调用所需的 windows 库?

Python循环不返回所需的值

在 Python 中存储整数所需的字节数 [重复]