【发布时间】:2015-05-12 10:27:15
【问题描述】:
我有一个 Python 程序,它有点像 pip 的包装器,我用它来协助开发 Python 包。基本上我面临的问题是如何在不安装的情况下读取包的元数据,例如 Name 和 Version(通常是 '.tar.gz' 和 '.whl' 档案) . distutils 或其他工具可以做到这一点吗?
只是一些注释...代码是为 Python 3 编写的,但我正在使用各种 Python 包,例如 Py2 的 sdist、bdist_wheel和 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))
【问题讨论】:
标签: python python-3.x pip packaging distutils