为了账号安全,请及时绑定邮箱和手机立即绑定

查找哪个Python包提供了特定的导入模块

查找哪个Python包提供了特定的导入模块

泛舟湖上清波郎朗 2023-07-27 14:01:29
不要感到困惑,有大量关于安装 Python 包、如何导入生成的模块以及列出可用包的问题。但是,如果您没有 pip 样式文件或 Pipenv Pipfile ,则--what-provides似乎没有与pip等效的选项。此问题与上一个问题类似,但要求提供父包,而不是其他元数据。也就是说,这些其他问题并没有得到很多关注或许多接受的答案 - 例如。如何查找给定模块的 python 包元数据信息。就这样砥砺前行&hellip;&hellip;requirements.txt举例来说,有两个包(仅举几例)将安装名为的模块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它位于一个serial目录中,但实际上只安装了pyserial ,而不是serial:> python3 -m pip list | grep serialpyserial                 3.4我能想到的最接近的是生成一个可能在依赖子文件上失败的requirements.txtvia (就像我所做的那样),或者通过Pipenvpipreqs ./反向检查依赖关系(这会带来一整套新问题来完成所有设置):> pipenv graph --reversecymysql==0.9.15ftptool==0.7.1netifaces==0.10.9pip==20.2.2PyQt5-sip==12.8.1    - PyQt5==5.15.0 [requires: PyQt5-sip>=12.8,<13]setuptools==50.3.0wheel==0.35.1有谁知道我错过了一个简单的解决方案来查找 pip 包提供特定模块的命令?
查看完整描述

2 回答

?
互换的青春

TA贡献1797条经验 获得超6个赞

使用( 或)packages_distributions()中的函数。例如,在您的情况下, “导入包”的名称是:importlib.metadataimportlib-metadataserial

import importlib.metadata  # or: `import importlib_metadata`
importlib.metadata.packages_distributions()['serial']

这应该返回一个包含 的列表,它是“分发包pyserial”的名称(应该用于pip -install 的名称)。

对于较旧的 Python 版本和/或较旧版本的importlib-metadata...

我相信类似以下的内容应该有效:

#!/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返回该对象。


查看完整回答
反对 回复 2023-07-27
?
狐的传说

TA贡献1804条经验 获得超3个赞

此问题现已通过 importlib_metadata 库解决。请参阅提供从“Python 包”到“分发包”的映射,特别是“注释 2”处理这个确切的问题。因此,请参阅@sinoroc的评论,您可以使用如下内容找到该包(例如,提供模块“serial”的包“pyserial”):


>>> import importlib_metadata

>>> print(importlib_metadata.packages_distributions()['serial'])

['pyserial']

我提出了以下代码(合并了提到的 importlib.util.find_spec 方法,但对返回路径中的 RECORD 文件进行基于 bash 的搜索)。


作为“python3 python_find-module-package.py -m [module-name-here] -d”运行,这也将打印调试。关闭“-d”开关即可仅返回包名称(和错误)。


TLDR:github 上的代码。


#!/usr/bin/python3


import sys

import os.path

import importlib.util

import importlib_metadata

import pathlib

import subprocess

import argparse

parser = argparse.ArgumentParser()

parser.add_argument("-m", "--module", help="Find matching package for the specified Python module",

                            type=str)

#parser.add_argument("-u", "--username", help="Database username",

#                           type=str)

#parser.add_argument("-p", "--password", help="Database password",

#                           type=str)

parser.add_argument("-d", "--debug", help="Debug messages are enabled",

                            action="store_true")

args = parser.parse_args()


TESTMODULE='serial'


def debugPrint (message="Nothing"):

    if args.debug:

        print ("[DEBUG] %s" % str(message))


class application ():

    def __init__(self, argsPassed):

        self.argsPassed = argsPassed

        debugPrint("Got these arguments:\n%s" % (argsPassed))


    def run (self):

        #debugPrint("Running with args:\n%s" % (self.argsPassed))

        try:

            if self.argsPassed.module is not None:

                self.moduleName=self.argsPassed.module  #i.e. the module that you're trying to match with a package.

            else:

                self.moduleName=TESTMODULE

                print("[WARN] No module name supplied - defaulting to %s!" % (TESTMODULE))

            self.location=importlib.util.find_spec(self.moduleName).origin

            debugPrint(self.location)

        except:

            print("[ERROR] Parsing module name!")

            exit(1)


        try:

            self.getPackage()

        except Exception as e:

            print ("[ERROR] getPackage failed: %s" % str(e))


        try:

            distResult=self.getDistribution(self.location)

            self.packageStrDist=distResult.metadata['Name']

            print(self.packageStrDist)

        except Exception as e:

            print ("[ERROR] getDistribution failed: %s" % str(e))


        debugPrint("Parent package for \"%s\" is: \"%s\"" % (self.moduleName, self.packageStr))

        return self.packageStr


    def getPackage (self):

        locationStr=self.location.split("site-packages/",1)[1]

        debugPrint(locationStr)

        #serial/__init__.py

        locationDir=self.location.split(locationStr,1)[0]

        debugPrint(locationDir)

        #/usr/lib/python3.6/site-packages

        cmd='find \"' + locationDir + '\" -type f -iname \'RECORD\' -printf \'\"%p\"\\n\' | xargs grep \"' + locationStr + '\" -l -Z'

        debugPrint(cmd)

        #find "/usr/lib/python3.6/site-packages" -type f -iname 'RECORD' -printf '"%p"\n' | xargs grep "serial/__init__.py" -l -Z


        #return_code = os.system(cmd)

        #return_code = subprocess.run([cmd], stdout=subprocess.PIPE, universal_newlines=True, shell=False)

        #findResultAll = return_code.stdout

        findResultAll = subprocess.check_output(cmd, shell=True)    # Returns stdout as byte array, null terminated.

        findResult = str(findResultAll.decode('ascii').strip().strip('\x00'))

        debugPrint(findResult)

        #/usr/lib/python3.6/site-packages/pyserial-3.4.dist-info/RECORD


        findDir = os.path.split(findResult)

        self.packageStr=findDir[0].replace(locationDir,"")

        debugPrint(self.packageStr)


    def getDistribution(self, fileName=TESTMODULE):

        result = None

        for distribution in importlib_metadata.distributions():

            try:

                relative = (pathlib.Path(fileName).relative_to(distribution.locate_file('')))

            #except ValueError:

            #except AttributeError:

            except:

                pass

            else:

                if relative in distribution.files:

                    result = distribution

        return result


if __name__ == '__main__':

    result=1

    try:

        prog = application(args)

        result = prog.run()

    except Exception as E:

        print ("[ERROR] Prog Exception: %s" % str(E))

    finally:

        sys.exit(result)

    

# exit the program if we haven't already

print ("Shouldn't get here.")

sys.exit(result)



查看完整回答
反对 回复 2023-07-27
  • 2 回答
  • 0 关注
  • 133 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信