如何在Python控制台中访问Python包元数据?

28

如果我使用distutils.core 构建了一个Python包,例如通过以下方式:

setup(
    ext_package="foo",
    author="me",
    version="1.0",
    description="foo package",
    packages=["foo",],
)

所有的元数据都去哪里了(它的目的是什么?)以及我如何从Python内部访问它。具体来说,我如何在执行类似于下面的操作后从Python控制台访问作者信息。

>>> import foo
7个回答

22

随着Python3.8的发布,您可能想要使用新的importlib.metadata[1] 模块来解析任何已安装软件包的元数据。

获取作者信息的代码如下:

>>> from importlib import metadata
>>> metadata.metadata('foo')['Author']  # let's say you called your package 'foo'
'Arne'

获取您安装的版本:

>>> from importlib import metadata
>>> metadata.version('foo')
'0.1.0'
比之前你需要做的事情简单得多。

[1]也可作为Python2.7和3.5+的后移版,称为importlib-metadata,感谢@ChrisHunt指出。


5
该功能在importlib-metadata中也提供Python 2.7和3.5+的后向支持。 - Chris Hunt
如果你想知道为什么一个包没有元数据,尽管它可以正常导入:那是因为缺少.egg-info目录。 - undefined

5

访问元数据的一种方法是使用

import pip

package = [pckg for pckg in pip.get_installed_distributions() 
            if pckg.project_name == 'package_name'][0]
#  package var will contain some metadata: version, project_name and others.

或者 pkg_resources
from pkg_resources import get_distribution

pkg = get_distribution('package_name')  # also contains a metadata

很不幸,我现在没有安装 pip,所以无法测试它。使用 pkg_resources.get_distribution 方法似乎也无法针对通过简单的 python setup.py build 安装的扩展进行操作。在这种情况下,我会收到一个 DistributionNotFound: foo 异常。 - dastrobu
1
python setup.py build不会安装。 - merwok
9
使用 pip 在导入时会导致巨大的延迟,并为您的代码增加了相当大的内存占用。它内部只是使用了 pkg_resources 并给您带来了相同的结果。换句话说,在这里使用 pip 是多余和误导性的“解决方案”。此外,该答案实际上并没有得到所需的信息;从一个 Distribution 对象中获取版本、作者和描述等其他字段是非常不明显的。首先,pkg._get_metadata(pkg.PKG_INFO) 可以将元数据文件的各行作为列表返回。 - amcgregor

4
元数据存储在名为<package>-<version>-<py version>.egg-info的文件中。
当您创建模块时,应该有这一行: Writing /usr/lib/python2.7/site-packages/foobar-1.0-py2.7.egg-info 此文件包含元数据:
Metadata-Version: 1.0
Name: Foobar
Version: 1.0
Summary: foobar
Home-page: http://foobar.com/
Author: foobar
Author-email: foobar@foobar.net
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

如果您想访问它,最好的方法是使用pippkg_resources(如Alexander Zhukov所说)。

>>> import pkg_resources
>>> d = pkg_resources.get_distribution('Foobar')
>>> d.version
'1.0'
>>> d.location
'/usr/lib/python2.7/site-packages'

3
读回元数据所需的关键步骤缺失:d._get_metadata(d.PKG_INFO)。光秃秃的 Distributio 对象没有明显的访问信息的方式。 - amcgregor
@amcgregor,你的评论实际上是我错过的信息!这应该成为一个独立回答。 - Joachim Jablon

3

解决方案

虽然我更喜欢使用importlib.metadata,但既然已经有另一个答案展示了如何使用它,我将向您展示另一种选择。

为了说明,我将使用来自PyPI的我的一个软件包genespeak

使用metadata

try:
    from importlib import metadata
except ImportError:  # for Python<3.8
    import importlib_metadata as metadata

print(metadata.name('genespeak')) # genespeak
print(metadata.version('genespeak')) # 0.0.7

使用pkginfo PyPI

我们将使用以下5种方式来访问软件包信息。

from pkginfo import SDist, BDist, Wheel, Installed, Develop

A. 从源分发包中检查软件包信息

通常情况下,您可以使用以下命令创建源分发包:

python setup.py sdist

假设您在路径./dist/genespeak-0.0.7.tar.gz下有一个.tar.gz文件,以下是提取包信息所需的步骤。
from pkginfo import SDist

pkg = SDist("./dist/genespeak-0.0.7.tar.gz")
# Now you can access the metadata fields from 
# PKG-INFO file inside the source file:
# `./dist/genespeak-0.0.7.tar.gz`
print(pkg.name) # genespeak
print(pkg.version) # 0.0.7

B. 从二进制发行版中检查软件包信息

通常,您可以使用以下命令创建二进制发行版文件(.egg):

python setup.py bdist_egg

假设您在路径 ./dist/genespeak-0.0.7-py38.egg 中有一个 .egg 文件,以下是提取软件包信息所需的步骤。
from pkginfo import BDist

pkg = BDist("./dist/genespeak-0.0.7-py38.egg")
# Now you can access the metadata fields from 
# the binary distribution file (*.egg):
# `./dist/genespeak-0.0.7-py38.egg`
print(pkg.name) # genespeak
print(pkg.version) # 0.0.7

C. 从Wheel中检查软件包信息

通常,您会使用以下命令创建二进制分发的wheel文件(.whl):

python setup.py bdist_wheel

假设您有一个位于路径./dist/genespeak-0.0.7-py3-none-any.whl.whl文件,以下是提取包信息所需的步骤。
from pkginfo import Wheel

pkg = Wheel("./dist/genespeak-0.0.7-py3-none-any-whl")
# Now you can access the metadata fields from 
# PKG-INFO file inside the source file:
# `./dist/genespeak-0.0.7-py3-none-any.whl`
print(pkg.name) # genespeak
print(pkg.version) # 0.0.7

D. 从已安装的包中检查包信息

更多详情请参见此处

from pkginfo import Installed
import genespeak

pkg = Installed(genespeak)
# Now you can access the metadata fields from 
# PKG-INFO file inside the source file:
# `./dist/genespeak-0.0.7.tar.gz`
print(pkg.name) # genespeak
print(pkg.version) # 0.0.7

E. 从开发目录检查软件包信息

from pkginfo import Develop

dev = Develop(".")
# Now you can access the metadata fields from 
# PKG-INFO file under `genespeak.egg-info` 
# directory under the project root.
print(dev.name) # genespeak
print(dev.version) # 0.0.7

参考资料


@dastrobu 也许你的问题早已得到解答。然而,另一个选项总是会派上用场。 - CypherX

2
关于仅使用version元数据,我发现大多数工具不包括所有情况,因此使用它们相当不可靠。例如:
  • 内置模块
  • 未安装但已添加到Python路径中的模块(例如由IDE添加)
  • 同一模块的两个版本可用(一个在Python路径中,优先于已安装的版本)
因为我们需要一种可靠的方式来获取任何包、模块或子模块的版本,所以我最终编写了getversion。它非常容易使用:
from getversion import get_module_version
import foo
version, details = get_module_version(foo)

请参阅文档以了解详情:文档。最初的回答。

1
给定以下的setup.py文件:
from distutils.core import setup

setup(
    name         = 'TestApp',
    version      = '0.0.1',
    author       = 'saaj',
    py_modules   = ['app'],
    test_suite   = 'test'
)

如果需要进行一些脚本编写和自动化操作,但不想安装软件包,而 pipeasy_install 甚至 setuptools 都没有提供命令行选项或公共 API 来读取所有元数据(例如 test_suite),那么这里有一个小技巧:

python3 -c "import sys, types; m = types.ModuleType('distutils.core'); \
    m.setup = lambda **kwargs: print(kwargs); \
    sys.modules['distutils.core'] = m; import setup" 

这将打印传递给setup()的关键字参数的dict
{'author': 'saaj', 'version': '0.0.1', 'name': 'TestApp', 
    'test_suite': 'test', 'py_modules': ['app']}

您可以将 lambda 中的 print 替换为任何您需要的输出。如果您的 setup.pysetuptools 导入 setup(),这实际上是推荐的方法,只需在代码片段中将 "distutils.core" 替换为 "setuptools" 即可。
import sys
import types

m = types.ModuleType('distutils.core')
m.setup = lambda **kwargs: print(kwargs)
sys.modules['distutils.core'] = m

import setup  # import you setup.py with mocked setup()

-2
这段内容与编程有关。其含义是,该数据的一个用途是在 Pypi (http://pypi.python.org/) 上显示(如果您要在此处发布软件包)。一种组织结构方式如下:
在您的 foo 模块的顶层:
__author__= "me"
__version__= "1.0"
__description__= "foo package"

in setup.py:

import foo
setup(

    author = foo.__author__,
    version = foo.__version__,
    description = foo.__description__,
    packages = ["foo",],

)

这样,您只需要在一个地方更新元数据,由于数据在包的主模块中定义,因此可以从那里访问。


4
这里存在一个非常重要的进退两难。你的安装脚本现在依赖于要安装的软件包已经被安装 (或者至少已经可以被导入)。这并不是一个好事情。(因为这可能会有危险的实践,所以我会给它一个反对票。) - amcgregor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接