首页 > 解决方案 > Python3(pip):查找哪个包提供了特定的模块

问题描述

不要混淆,关于安装包、如何导入生成的模块以及列出哪些包可用的问题有很多。但是,如果您没有 requirements.txt 或 pipenv,似乎没有等效于 pip 的“--what-provides”选项。这个问题与上一个问题类似,但要求提供父包,而不是附加元数据。也就是说,这些其他问题并没有得到很多关注或许多公认的答案 - 例如。如何找到给定模块的 python 包元数据信息。于是奋勇向前……

例如,有两个包(仅举几例)将安装一个名为“serial”的模块 - 即“pyserial”和“serial”。因此,假设安装了其中一个包,我们可以使用 pip list 找到它:

python3 -m pip list | grep serial

但是,如果包的名称与模块的名称不匹配,或者您只是想找出要安装的包,在旧服务器或开发机器上工作,就会出现问题。

您可以检查导入模块的路径 - 这可以为您提供线索。但是继续这个例子......

>>> import serial
>>> print(serial.__file__)
/usr/lib/python3.6/site-packages/serial/__init__.py

它位于“串行”目录中,但实际上只安装了 pyserial,而不是串行:

> python3 -m pip list | grep serial
pyserial                 3.4

我最接近的方法是通过“pipreqs ./”生成一个 requirements.txt,它可能会在依赖的子文件上失败(就像我一样),或者通过 pipenv 反向检查依赖关系(这会带来一整套新问题一起完成所有设置):

> pipenv graph --reverse
cymysql==0.9.15
ftptool==0.7.1
netifaces==0.10.9
pip==20.2.2
PyQt5-sip==12.8.1
    - PyQt5==5.15.0 [requires: PyQt5-sip>=12.8,<13]
setuptools==50.3.0
wheel==0.35.1

有谁知道我错过了一个简单的解决方案来查找 pip 包提供特定模块的命令?

标签: python-3.xpippython-3.6

解决方案


我相信类似以下的东西应该有效:

#!/usr/bin/env python3

import importlib.util
import pathlib

import importlib_metadata

def get_distribution(file_name):
    result = None
    for distribution in importlib_metadata.distributions():
        try:
            relative = (
                pathlib.Path(file_name)
                .relative_to(distribution.locate_file(''))
            )
        except ValueError:
            pass
        else:
            if distribution.files and relative in distribution.files:
                result = distribution
                break
    return result

def alpha():
    file_name = importlib.util.find_spec('serial').origin
    distribution = get_distribution(file_name)
    print("alpha", distribution.metadata['Name'])

def bravo():
    import serial
    file_name = serial.__file__
    distribution = get_distribution(file_name)
    print("bravo", distribution.metadata['Name'])

if __name__ == '__main__':
    alpha()
    bravo()

这只是一个代码示例,展示了如何获取特定模块所属的已安装项目的元数据。

重要的是get_distribution函数,它以文件名作为参数。它可以是模块或包数据的文件名。如果该文件名属于安装在环境中的项目(pip install例如通过),importlib.metadata.Distribution则返回该对象。


更新(2021 年 2 月):

packages_distributions()由于以下新添加的功能,看起来这可能会变得更容易importlib_metadata


推荐阅读