PEP 262 – 已安装 Python 包数据库
- 作者:
- A.M. Kuchling <amk at amk.ca>
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 主题:
- 打包
- 创建日期:
- 2001年7月8日
- 发布历史:
- 2002年3月27日
引言
本 PEP 描述了系统中已安装的 Python 软件数据库的格式。
(在本文件中,“发行版”一词指的是一起开发和分发的代码集合。“发行版”与 Red Hat 或 Debian 包相同,但“包”一词在 Python 术语中已有含义,指的是“一个带有 __init__.py 文件的目录。”)
要求
我们需要一种方法来确定系统中安装了哪些发行版以及这些发行版的哪些版本。我们希望提供类似于 CPAN、APT 或 RPM 的功能。必需支持的用例包括:
- 系统中是否安装了发行版 X?
- 系统中安装了发行版 X 的哪个版本?
- 可以在哪里找到发行版 X 的新版本?(这可以定义为“用户可以访问并找到下载链接的主页”,还是“程序可以找到最新版本的位置?”两者都应该得到支持。)
- 发行版 X 将哪些文件安装到了我的系统中?
- 文件 x/y/z.py 来自哪个发行版?
- x/y/z.py 是否被本地修改过?
- 此软件还需要哪些其他发行版?
- 此发行版提供了哪些 Python 模块?
数据库位置
数据库位于 `
数据库的结构特意保持简单;该目录或其子目录(如果有)中的每个文件都描述一个单独的发行版。Python 软件的二进制打包(如 RPM)可以通过将相应文件安装到 INSTALLDB 目录来更新 Python 的数据库。
扫描子目录的理由是,如果数据库目录包含过多条目,我们可以移动到一个基于目录的索引方案。例如,这可以让我们透明地从 INSTALLDB/Numeric 切换到 INSTALLDB/N/Nu/Numeric 或类似的哈希方案。
数据库内容
INSTALLDB 或其子目录中的每个文件都描述一个单独的发行版,并且具有以下内容:
第一行列出此文件中的节,用空格分隔。当前总是“PKG-INFO FILES REQUIRES PROVIDES”。这是为了未来兼容性;如果我们添加一个新节,例如用于列出文档文件,那么我们将添加一个 DOCS 节并在内容中列出。节之间总是用空行分隔。
使用 Distutils 进行安装的发行版应自动更新数据库。自己进行安装的发行版将不得不使用数据库的 API 来手动添加或更新自己的条目。像 RPM 或 pkgadd 这样的系统包管理器可以直接在 INSTALLDB 目录中创建新文件。
文件中的每个节都用于不同的目的。
PKG-INFO 部分
FILES 部分
发行版安装的每个文件的条目。原始的 .py 文件以及由发行版安装的 .pyc 和 .pyo 等生成文件都包含在此列表中;但是,它们的校验和将不会被存储或检查。
每个文件的条目是单个制表符分隔的行,其中包含以下字段:
- 文件在系统上的完整安装路径。
- 文件的大小。
- 文件的权限。在 Windows 上,此字段始终为“unknown”。
- 文件的所有者和组,用制表符分隔。在 Windows 上,这两个字段都将是“unknown”。
- 文件的 SHA1 摘要,以十六进制编码。对于 *.pyc 文件等生成的文件,此字段必须包含字符串“-”,表示不应验证文件的校验和。
REQUIRES 部分
此节是一个字符串列表,给出此模块发行版正常运行所需的***服务***。此列表包括发行版名称(“python-stdlib”)和模块名称(“rfc822”、“htmllib”、“email”、“email.Charset”)。它将由 `distutils.core.setup()` 函数的额外 `requires` 参数指定。例如:
setup(..., requires=['xml.utils.iso8601',
最终可能会有自动工具扫描所有代码并生成需求列表,但这些工具不太可能处理所有可能的情况;手动指定需求的***方法***将始终是必需的。
PROVIDES 部分
此节是一个字符串列表,给出已安装发行版提供的***服务***。此列表包括发行版名称(“python-stdlib”)和模块名称(“rfc822”、“htmllib”、“email”、“email.Charset”)。
XXX 是否应列出文件?例如 $PREFIX/lib/color-table.txt,以包含数据文件、必需的脚本等。
最终可能会有一个选项让模块开发人员向此节添加自己的字符串。例如,您可以添加“XML 解析器”,而其他模块发行版随后可以将“XML 解析器”列为它们的依赖项之一,表示可以使用多个不同的 XML 解析器。目前此功能不受支持,因为它会引发太多问题:我们需要一个合法的字符串中央注册表,还是允许人们随意放置任何内容?等等,等等……
API 描述
有一个基本类:InstallationDatabase。它的代码位于 distutils/install_db.py 中。(XXX 标准库中的其他位置,或替代模块名称有任何建议吗?)
InstallationDatabase 返回 Distribution 实例,其中包含有关已安装发行版的所有信息。
XXX Distribution 中的几个字段是 distutils.dist.Distribution 中字段的重复。它们可能应该被分解到此处提出的 Distribution 类中,但能否以向后兼容的方式完成?
InstallationDatabase 具有以下接口:
class InstallationDatabase:
def __init__ (self, path=None):
"""InstallationDatabase(path:string)
Read the installation database rooted at the specified path.
If path is None, INSTALLDB is used as the default.
"""
def get_distribution (self, distribution_name):
"""get_distribution(distribution_name:string) : Distribution
Get the object corresponding to a single distribution.
"""
def list_distributions (self):
"""list_distributions() : [Distribution]
Return a list of all distributions installed on the system,
enumerated in no particular order.
"""
def find_distribution (self, path):
"""find_file(path:string) : Distribution
Search and return the distribution containing the file 'path'.
Returns None if the file doesn't belong to any distribution
that the InstallationDatabase knows about.
XXX should this work for directories?
"""
class Distribution:
"""Instance attributes:
name : string
Distribution name
files : {string : (size:int, perms:int, owner:string, group:string,
digest:string)}
Dictionary mapping the path of a file installed by this distribution
to information about the file.
The following fields all come from PEP 241.
version : distutils.version.Version
Version of this distribution
platform : [string]
summary : string
description : string
keywords : string
home_page : string
author : string
author_email : string
license : string
"""
def add_file (self, path):
"""add_file(path:string):None
Record the size, ownership, &c., information for an installed file.
XXX as written, this would stat() the file. Should the size/perms/
checksum all be provided as parameters to this method instead?
"""
def has_file (self, path):
"""has_file(path:string) : Boolean
Returns true if the specified path belongs to a file in this
distribution.
"""
def check_file (self, path):
"""check_file(path:string) : Boolean
Checks whether the file's size, checksum, and ownership match,
returning true if they do.
"""
交付物
数据库 API 的描述,将添加到此 PEP 中。
对 Distutils 的补丁,1) 实现 InstallationDatabase 类,2) 在安装新发行版时更新数据库。3) 添加一个简单的包管理工具,功能将添加到此 PEP 中。(还是应该单独创建一个 PEP?)有关当前补丁,请参阅 [2]。
未解决的问题
PJE 建议安装数据库“可能存在于 sys.path 中的每个目录中,其内容按 sys.path 顺序合并。这将允许主目录或其他替代位置的安装正常工作,并简化 distutils install 命令写入文件的过程。”一个不错的特点:这确实意味着包管理器工具可以考虑用户私自安装的 Python 包。
AMK 疑惑:如果 setup.py 被告知将包安装到不在 sys.path 中的目录,它会做什么?它会向被告知写入的目录写入一个 install-db 目录,还是什么都不做?
包数据库文件本身是否应包含在文件列表中?(PJE 会认为是的,但当然它不能包含自己的校验和。AMK 想不出包含 DB 文件有意义的用例。)
PJE 担心先***写入***包 DB 文件,然后再安装其他文件,这样部分安装失败的可以被回滚,并被识别为损坏。此 PEP 可能需要指定某种算法来识别这种情况。
我们应该保证安装数据库的格式在 Python 版本之间保持兼容,还是可以任意更改?可能我们需要保证兼容性。
被拒绝的建议
而不是为每个发行版使用一个文本文件,可以使用一个大的文本文件或一个 anydbm 文件。这已被拒绝有几个原因。首先,性能可能不是一个极其紧迫的问题,因为数据库仅在安装或删除软件时使用,这是一个相对不频繁的任务。可扩展性也不是问题,因为人们可能安装了数百个 Python 包,但成千上万或数万个似乎不太可能。最后,单个文本文件与 RPM 或 DPKG 等安装程序兼容,因为二进制打包程序可以直接将新的数据库文件放入数据库目录。如果使用一个大的文本文件或一个二进制文件,则必须通过运行 postinstall 脚本来更新 Python 数据库。
在 Windows 上,文件的权限和所有者/组不会被存储。Windows 实际上支持所有权和访问权限,但读取和设置它们需要 win32all 扩展,并且它们在基本的 Windows Python 安装程序中不存在。
参考资料
[1] Michael Muller 的补丁(于 1999 年 12 月 28 日左右发布到 Distutils-SIG)生成了一个已安装文件列表。
致谢
此 PEP 的想法最初来自 Greg Ward、Fred L. Drake Jr.、Thomas Heller、Mats Wichmann、Phillip J. Eby 等人的帖子。
Distutils SIG 的读者对本文档提出了许多修改和重写建议。
版权
本文档已置于公共领域。
来源:https://github.com/python/peps/blob/main/peps/pep-0262.rst