从包中获取 Python 包分发版本

Gri*_*cey 6 python python-3.x python-packaging

您可以使用以下命令获取 python 发行版的版本

import pkg_resources
pkg_resources.get_distribution("distro").version
Run Code Online (Sandbox Code Playgroud)

如果您知道发行版名称,那就太好了,但是我需要在运行时动态找出我的发行版名称。

# Common framework base app class, extended by each app
class App(object):
    def get_app_version(self) -> str:
        package_name = self.__class__.__module__.split('.')[0]
        try:
            return pkg_resources.get_distribution(package_name).version
        except Exception:
            return "development"
Run Code Online (Sandbox Code Playgroud)

这适用于应用程序包名称与分发名称相同的情况(例如requests)。然而,一旦它们不匹配(例如my-app包含 package my_app),这就会失败。

所以我需要的是发行版和它们的包之间的映射,我确定它一定存在于某个地方,因为 pip 似乎知道在调用卸载时要删除什么:

$ pip uninstall requests
Uninstalling requests-2.21.0:
  Would remove:
    /home/user/.virtualenvs/app/lib/python3.6/site-packages/requests-2.21.0.dist-info/*
    /home/user/.virtualenvs/app/lib/python3.6/site-packages/requests/*
Run Code Online (Sandbox Code Playgroud)

如何以编程方式访问此映射?

Gri*_*cey 0

经过几个小时的探索pkg_resources和阅读pip 卸载的源代码后,我得到了以下结果:

import inspect
import pkg_resources
import csv

class App(object):
    def get_app_version(self) -> str:
        # Iterate through all installed packages and try to find one that has the app's file in it
        app_def_path = inspect.getfile(self.__class__)
        for dist in pkg_resources.working_set:
            try:
                filenames = [
                    os.path.normpath(os.path.join(dist.location, r[0]))
                    for r in csv.reader(dist.get_metadata_lines("RECORD"))
                ]
                if app_def_path in filenames:
                    return dist.version
            except FileNotFoundError:
                # Not pip installed or something
                pass
        return "development"
Run Code Online (Sandbox Code Playgroud)

这将迭代所有已安装的软件包,并且对于每个软件包,迭代其文件列表并尝试将其与当前文件进行匹配,这会将软件包与发行版进行匹配。这并不是很理想,我仍然愿意接受更好的答案。