【问题标题】:How do you find python package metadata information given a module给定模块,如何找到 python 包元数据信息
【发布时间】:2020-07-13 10:49:27
【问题描述】:

我正在尝试检索给定模块名称的 python 包的元数据信息。

我可以使用 importlib-metadata 来检索信息,但在某些情况下,顶级模块名称与包名称不同。

示例:

>>> importlib_metadata.metadata('zmq')['License']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\Users\xxxxx\AppData\Local\Programs\Python\Python37\Lib\site-packages\importlib_metadata\__init__.py", line 499, in metadata
    return Distribution.from_name(distribution_name).metadata
  File "c:\Users\xxxxx\AppData\Local\Programs\Python\Python37\Lib\site-packages\importlib_metadata\__init__.py", line 187, in from_name
    raise PackageNotFoundError(name)
importlib_metadata.PackageNotFoundError: zmq


>>> importlib_metadata.metadata('pyzmq')['License']
'LGPL+BSD'

【问题讨论】:

标签: python python-importlib


【解决方案1】:

我相信类似以下的方法应该有效:

#!/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 relative in distribution.files:
                result = distribution
    return result

def alpha():
    file_name = importlib.util.find_spec('easy_install').origin
    distribution = get_distribution(file_name)
    print("alpha", distribution.metadata['Name'])

def bravo():
    file_name = importlib_metadata.__file__
    distribution = get_distribution(file_name)
    print("bravo", distribution.metadata['Name'])

if __name__ == '__main__':
    alpha()
    bravo()

更新(2021 年 2 月):

由于importlib_metadata 中新添加的packages_distributions() 函数,这看起来会变得更容易:

【讨论】:

  • 这假设顶级模块的路径是包的子目录。 zmq 和 pyzmq-.dist-info 都是 site-packages 的子目录。
  • @nsk 我不明白这个问题。作为测试,我刚刚安装了pyzmq,上面的代码似乎运行良好。
  • @nsk 这回答了你的问题还是需要澄清一些?
  • 不错的解决方案。我唯一要补充的是有一个is_relative_to 方法可以稍微简化一些事情。
  • 我说得太早了:不幸的是,因为很多包裹都只是鸡蛋,例如/usr/local/lib/python3.9/site-packages/,这失败了。
【解决方案2】:

我相信,这里有一个功能可以满足您的需求。它的效率不是很高,因为它必须枚举所有已安装的包分发并读取每个包的顶级模块列表——然而,我相信这是最好的。 (当然,你也可以缓存一个顶级模块名到包名的字典映射。)

from importlib.metadata import Distribution, distributions
from pathlib import Path
from typing import *

def get_pkg_distribution(top_level_module: str) -> Optional[Distribution]:
    pkg_path = Path(__file__).parent
    for dist in distributions():
        package_namespaces = (dist.read_text("top_level.txt") or "").splitlines()
        if top_level_module in package_namespaces:
            return dist
    return None

# Get the package metadata for the current package. Note, `__package__` is actually the name of the top-level module!
pkg_metadata = dict(get_pkg_distribution(__package__).metadata.items())
__version__ = pkg_metadata["Version"]

【讨论】:

  • 这个解决方案似乎和我的一样有缺点。因为它依赖于文件名。最好依赖全限定模块名,获取顶层包/模块名,然后与importlib-metadata.readthedocs.io/en/stable/…进行比较
  • 其实我的解决方案和packages_distributions的实现非常相似,刚刚看了那个库的源代码!我相信__package 应该得到顶级模块的全称?虽然不是 100% 肯定。对我有用吗。
  • 它是否适用于以 egg 或类似方式安装的代码?
  • 我上次尝试的时候好像是这样……但我以后可以好好调查一下,也许吧。
  • 是的,如果您设法测试,请告诉我。我真的很想知道如何从鸡蛋中获得有效的__file__
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-14
  • 2021-09-25
  • 2015-09-17
  • 2011-01-15
  • 1970-01-01
  • 2021-12-06
相关资源
最近更新 更多