python - 从包中获取 Python 包分发版本
问题描述
您可以使用以下方式获取 python 发行版的版本
import pkg_resources
pkg_resources.get_distribution("distro").version
如果您知道分发名称,那就太好了,但是我需要在运行时动态找出我的分发名称。
# 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"
这适用于应用程序的包名称与分发名称相同的情况(例如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/*
如何以编程方式访问此映射?
解决方案
如果您正在寻找一种既适用于您的开发(未安装或仅在本地调用)版本,又适用于已安装版本的解决方案,请尝试此解决方案。
进口:
import ast
import csv
import inspect
from os import listdir, path
import pkg_resources
实用功能:
def get_first_setup_py(cur_dir):
if 'setup.py' in listdir(cur_dir):
return path.join(cur_dir, 'setup.py')
prev_dir = cur_dir
cur_dir = path.realpath(path.dirname(cur_dir))
if prev_dir == cur_dir:
raise StopIteration()
return get_first_setup_py(cur_dir)
现在使用 Python 的ast
库:
def parse_package_name_from_setup_py(setup_py_file_name):
with open(setup_py_file_name, 'rt') as f:
parsed_setup_py = ast.parse(f.read(), 'setup.py')
# Assumes you have an `if __name__ == '__main__':`, and that it's at the end:
main_body = next(sym for sym in parsed_setup_py.body[::-1]
if isinstance(sym, ast.If)).body
setup_call = next(sym.value
for sym in main_body[::-1]
if isinstance(sym, ast.Expr) and
isinstance(sym.value, ast.Call) and
sym.value.func.id in frozenset(('setup',
'distutils.core.setup',
'setuptools.setup')))
package_version = next(keyword
for keyword in setup_call.keywords
if keyword.arg == 'version'
and isinstance(keyword.value, ast.Name))
# Return the raw string if it is one
if isinstance(package_version.value, ast.Str):
return package_version.s
# Otherwise it's a variable at the top of the `if __name__ == '__main__'` block
elif isinstance(package_version.value, ast.Name):
return next(sym.value.s
for sym in main_body
if isinstance(sym, ast.Assign)
and isinstance(sym.value, ast.Str)
and any(target.id == package_version.value.id
for target in sym.targets)
)
else:
raise NotImplemented('Package version extraction only built for raw strings and '
'variables in the same function that setup() is called')
return "development"
最后通过更改为:替换@Gricey 的答案中的函数:
return parse_package_name_from_setup_py(get_first_setup_py(path.dirname(__file__)))
推荐阅读
- python - 代码在 Jupyter 中有效,但在 Spyder 中出错
- javascript - 当网格生成低于 16 行时,网格拉伸问题
- c++ - 从 C++ 中的输入收集数据时,有没有更有效的方法可以处理变量?
- ponylang - 为什么赋值返回旧值而不是新值?
- c# - C# UWP - 如何获取对象的拖动图像?
- ruby-on-rails - Rails 应用程序、capistrano、nginx 和 puma 502 错误网关
- lua - 如何从 Lua 中的变量调用表?
- asp.net - 无法在 ASP.NET 中执行会话状态
- php - 仅显示具有特定 ID 的数据数据库
- firebase - 如何在 Firebase 存储上使用 hbs 模板和数据库 onCreate 触发器函数中的 nodemailer-express-handlebars?