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



在安装Python包、如何导入生成的模块以及列出可用的包方面,有很多问题。但是,如果您没有pip样式的requirements.txt文件或PipenvPipfile,则似乎没有等效于pip--what-provides选项。这个问题与前面的问题类似,但要求提供父包,而不是额外的元数据。也就是说,这些其他问题并没有得到太多关注,也没有得到很多公认的答案——例如,在给定模块的情况下,你如何找到python包的元数据信息。所以勇往直前。。。

例如,有两个包(仅举几个例子(将安装名为serial的模块,即pyserialserial

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 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包提供的特定模块的简单解决方案?

使用importlib.metadata(或importlib-metadata(中的packages_distributions()函数。例如,在serial是">导入包":

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

这应该返回一个包含pyserial的列表分发包";(应用于pip-install的名称(。

参考

  • https://importlib-metadata.readthedocs.io/en/stable/using.html#package-分配
  • https://github.com/python/importlib_metadata/pull/287/files

对于较旧的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对象。

Edit 2023/01/31:现在通过importlib_metadata库解决了这个问题。参见"提供映射";Python包";至";分发包";,特别是";注2";处理这个确切的问题。因此,请参阅@sinoroc的评论,您可以找到具有以下内容的包(例如,包"pyserial"提供模块"serial"(:

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

在@sinoroc大量发表的答案的基础上,我提出了以下代码(包含了前面提到的importlib.util.find_spec方法,但在返回的路径中对RECORD文件进行了基于bash的搜索(。我也尝试实现@sinoroc的版本,但没有成功。这两种方法都包含在演示中。

以";python3 python_ find-module-package.py-m[此处为模块名称]-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)

相关内容

  • 没有找到相关文章

最新更新