确定所需的 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
现在我可以看到它还安装了其他需要的模块,例如urllib3
和idna
。
例如,我的工具发现我正在使用:
/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 格式)提供所有软件包的列表以及安装在指定路径下的版本。
假设您发现安装了包 foo
和 bar
,那么您可以使用以下命令获取每个包中包含的文件
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
和/或pigar
(pip install pireqs
或pip 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数据库插入数据,并记录插入数据所需的时间