我有一个Python程序,它是pip
的一个包装器,用于帮助开发Python包。基本上我面临的问题是如何读取包的 Name 和 Version 等元数据(通常是' .tar.gz'和' ; .whl'档案)没有安装。 distutils
或其他工具可以执行此操作吗?
只需几个注释......代码是为Python 3编写的,但我正在使用各种Python包,例如 sdist , bdist_wheel ,用于Py2和Py3。另外,我只关心我有路径的本地包,而不是PyPi上可用的理论包。
我现在所做的工作很好,但看起来很乱,我想知道是否有更好的工具可以抽象出来。现在我正在阅读存档中的元数据文本文件并手动解析出我需要的字段。如果失败了,我将从包的文件名中删除名称和版本(非常糟糕)。有一个更好的方法吗?以下是我用来解析包 Name 和 Version 的两个函数。
Simeon,感谢您建议使用 wheel 档案中包含的 metadata.json 文件。我不熟悉档案中包含的所有文件,但我希望有一种很好的方法可以解析其中的一些文件。 metadata.json 当然符合轮子的标准。在接受之前,我要打开问题一段时间,看看是否还有其他建议。
无论如何,如果将来有人遇到此问题,我已附上我的更新代码。它可以说是一个更干净的班级,但这就是我现在所拥有的。对于边缘情况来说,它不具有超强的耐用性,所以买家要注意这一切。
import tarfile, zipfile
def getmetapath(afo):
"""
Return path to the metadata file within a tarfile or zipfile object.
tarfile: PKG-INFO
zipfile: metadata.json
"""
if isinstance(afo, tarfile.TarFile):
pkgname = afo.fileobj.name
for path in afo.getnames():
if path.endswith('/PKG-INFO'):
return path
elif isinstance(afo, zipfile.ZipFile):
pkgname = afo.filename
for path in afo.namelist():
if path.endswith('.dist-info/metadata.json'):
return path
try:
raise AttributeError("Unable to identify metadata file for '{0}'".format(pkgname))
except NameError:
raise AttributeError("Unable to identify archive's metadata file")
def getmetafield(pkgpath, field):
"""
Return the value of a field from package metadata file.
Whenever possible, version fields are returned as a version object.
i.e. getmetafield('/path/to/archive-0.3.tar.gz', 'name') ==> 'archive'
"""
wrapper = str
if field.casefold() == 'version':
try:
# attempt to use version object (able to perform comparisons)
from distutils.version import LooseVersion as wrapper
except ImportError:
pass
# package is a tar archive
if pkgpath.endswith('.tar.gz'):
with tarfile.open(pkgpath) as tfo:
with tfo.extractfile(getmetapath(tfo)) as mfo:
metalines = mfo.read().decode().splitlines()
for line in metalines:
if line.startswith(field.capitalize() + ': '):
return wrapper(line.split(': ')[-1])
# package is a wheel (zip) archive
elif pkgpath.endswith('.whl'):
import json
with zipfile.ZipFile(pkgpath) as zfo:
metadata = json.loads(zfo.read(getmetapath(zfo)).decode())
try:
return wrapper(metadata[field.lower()])
except KeyError:
pass
raise Exception("Unable to extract field '{0}' from package '{1}'". \
format(field, pkgpath))
答案 0 :(得分:1)
这种情况并不是很好,这就是创建轮文件的原因。如果您只需要支持wheel文件,那么您可以清理代码,但只要您必须支持<package>-<version>.dist-info
源包,您的方法就会有点混乱。
轮子的文件格式在3中指定,因此您既可以解析某些信息的文件名,也可以读取里面metadata.json
目录的内容。特别是METADATA
和metadata.json
非常有用。事实上,阅读metadata.json
就足够了,这将导致干净的代码无需安装即可访问该信息。
我会重构代码以使用PKG-INFO
并为tar.gz
源包实现尽力而为的方法。长期计划是将所有PKG-INFO
源包转换为轮子并删除当时过时的static
解析代码。
答案 1 :(得分:0)
pkginfo
软件包使这一过程变得更加容易。
from pkginfo import Wheel, SDist
def getmeta(pkgpath):
if pkgpath.endswith('.tar.gz'):
dist = SDist
elif pkgpath.endswith('.whl'):
dist = Wheel
pkg = dist(pkgpath)
print(pkg.name, pkg.license)