PEP 773 – 适用于 Windows 的 Python 安装管理器
- 作者:
- Steve Dower
- 讨论至:
- Discourse 帖子
- 状态:
- 已接受
- 类型:
- 标准跟踪
- 主题:
- 发布
- 创建日期:
- 2025 年 1 月 21 日
- 发布历史:
- 2024 年 12 月 18 日, 2025 年 1 月 21 日
- 取代:
- 397, 486
- 决议:
- 2025 年 4 月 25 日
摘要
在 Windows 上安装 python.org 的 Python 分发版本非常复杂。目前有三种主要方法,用户体验水平大致相同,但所有这些方法都存在不同的限制,包括无法满足现代使用场景。本 PEP 提出了一种单一 Windows 安装工作流工具的设计,该工具可以满足平台现有安装程序的所有需求,同时避免了它们的大多数限制,并为核心团队提供了在未来多年内管理发布的能力。
使用新的安装程序,在 Windows 上启动默认 Python 的推荐命令将是 python
,而用于管理多个版本(包括启动特定版本)的命令将是 py
。用户还可以选择通过在 PATH
环境变量中添加一个额外条目来拥有全局 python3.x
命令。我们建议从“如何教授此内容”部分开始,以获取最简洁的高级功能概述。
在本 PEP 被接受两年后,现有的 .exe
安装程序和 py
启动器将被弃用,并且不再发布。现有的可嵌入分发版将不再作为下载列出,但将通过安装管理器提供。Windows 应用商店应用将立即被安装管理器取代。
读者须知
本文档是一份详细的*提案*,旨在协助就用新格式替换 Windows 上 Python 现有安装格式做出“批准”或“拒绝”决定。它无意成为具有约束力的规范,也不是用户文档或互操作性规范。实际实现可能会随着需求的变化而随时间变化,这不应被视为对本 PEP 的“违反”。任何用于公共用途的接口和协议将根据标准弃用时间表独立记录和维护。
有关在 Windows 上安装 Python 的文档可在 docs.python.org/using/windows.html 找到。
背景
用户可能有一系列需求,导致他们希望安装 Python 运行时。许多(可能大多数)用户对运行(或许是编写)短脚本感兴趣,例如执行简单任务或帮助教授某个概念的脚本。一些用户正在寻找特定版本以与现有代码或另一个应用程序集成。一些用户则需要一整套不同的解释器版本来执行测试。
在本节中,我们讨论了用户对“安装 Python”的期望,概述了 Windows 现有的安装程序,并指出了这些产品固有的某些缺陷和挑战。
预期
根据大量的轶事经验和对可用定量数据(尽管不一定是公开的)的分析,我们对 Windows 上大多数 Python 用户做出以下断言:
- 大多数用户只想要最新的稳定版本
- 大多数用户希望“一键式”(或更少操作)安装
- 大多数用户不希望使用管理员权限
- 大多数用户将从安装维护更新中受益
- 大多数用户期望安装后
python
命令能正常工作
支持这些断言的主要依据是,用户主动选择的最流行的安装程序是 python.org 上的最新稳定版本和 Windows 应用商店中的最新稳定版本,两者都满足这些要求。
我们对其他重要的用户群体做出了以下假设。这些群体之间可能存在一些重叠,并且至少有一些用户期望所有这些群体都存在。
- 有些用户希望以编程方式安装 Python
- 有些用户希望安装特定版本
- 有些用户希望安装多个版本
- 有些用户希望为机器的所有用户安装
- 有些用户不希望有“开始”菜单快捷方式
- 有些用户希望将其作为项目构建过程的一部分进行安装
- 有些用户希望将其作为项目安装过程的一部分进行安装
- 有些用户打算从不更新他们的安装
- 有些用户期望安装后
py
命令能正常工作
传统安装程序
传统安装程序是可直接从 python.org 下载的可执行文件,它安装 Python 的整个开发工具包。这包括 CPython 解释器、标准库、Python 头文件和导入库、Tcl 和 Tk 的构建、HTML 文件形式的文档、运行时和标准库测试套件、Python 和 IDLE 的“开始”菜单快捷方式、调试符号和二进制文件的调试版本、py.exe
启动器及其文件关联,以及修改用户 PATH
环境变量、启用系统长路径支持、预生成标准库的 .pyc
文件以及安装 pip 的功能。从 3.13 版本开始,它还包括一组实验性的自由线程二进制文件。其中许多组件是可选的。
下载可执行文件后,用户会看到一个“快速安装”选项,该选项会将大部分选项都启用的 Python 安装到他们的用户目录中。我们相信大多数用户会选择此选项。
除了快速安装之外,还有一个选项会将用户带到两页的选项,列出他们不需要安装的组件,以及其他选项,例如安装目录和是否为所有用户安装。
所有这些选项都可以在命令行中指定,并且还有一个选项可以不显示任何 UI 而继续安装。根据反馈和错误报告,所有这些选项至少都被一些用户使用。但是,由于我们不跟踪安装遥测数据,因此我们无法知道哪些选项比其他选项更重要。
在幕后,传统安装程序是一个 Burn 捆绑包,使用 Wix Toolset 安装程序框架生成,包含一个或多个 MSI 文件,每个功能对应一个。微软自己广泛使用此框架,它提供了使用 Windows Installer 的最直接方法。该捆绑包是一个自定义的 C++ 应用程序,基于其模板,允许我们自定义安装程序的整体行为,以精确确定实际应安装哪些 MSI 文件。复制文件、更新注册表和生成快捷方式的过程完全由 Windows Installer 处理。
除了预期的用途之外,我们理解许多用户会(尝试)将传统安装程序用于其他场景,例如未注册的安装和自动化的 CI 系统安装。虽然有更好的替代方案可用,但它们不那么明显,希望未来的设计能使这些场景更容易。
Windows 应用商店
CPython 的 Windows 应用商店包是作为我们正常发布过程的一部分生成的,几乎所有二进制文件都与其他包相同。由于它是一个应用商店包,主要 python.exe
经过增强,能够正确确定其位置,并且包含替代的 pip.exe
和其他快捷方式,以弥补缺少 PATH
环境变量设置的不足。这些在我们的仓库中的 PC\python_uwp.cpp
中实现。
这些软件包通过在 Microsoft Store 应用中搜索 Python 进行安装,该应用将找到自 3.8 版本以来的每个主要版本的结果。然后,用户必须选择一个版本并安装它。这些软件包包括 CPython 解释器、标准库、Tcl/Tk、IDLE 和 pip,并为 python.exe
、python3.exe
、python3.X.exe
、pip[3[.X]].exe
和 idle[3[.X]].exe
创建文件关联、“开始”菜单快捷方式和全局命令。无法修改或不需要修改 PATH
,尽管用户可能需要通过“管理应用执行别名”设置页面来管理其全局快捷方式。
此外,Microsoft 已向全新安装的 Windows 添加了一个默认的 python.exe
命令。这会捕获用户在尚未安装 Python 的机器上尝试启动 Python 的行为。直接启动时,该命令将打开 Microsoft Store 应用,进入包含推荐 Python 应用的页面,通常是最新版本。此应用完全由 Microsoft 控制。根据与 Python 应用相关的遥测数据(该应用*确实*由上游 Python 项目控制),每月大约有 30 万次安装是通过此重定向器进行的,占该版本总安装量的约 90%。
在幕后,应用商店包基于微软的新应用安装技术,称为 APPX 或 MSIX。这些本质上是带有少量元数据的普通 ZIP 文件,只是安装由操作系统处理。它们总是提取到固定位置,所有用户都可以访问但任何人都不能修改,并自动更新到最新版本。用户自己的数据存储在用户配置文件中由操作系统管理的位置,并且可以使用常规操作系统功能进行重置、备份和恢复。
NuGet 包
CPython 的 NuGet 包是作为我们正常发布过程的一部分生成和发布的。其内容与传统安装程序相同。NuGet 包发布到 nuget.org,这是一个通常与 .NET 语言相关的包管理器,但与 Visual Studio 支持的任何项目高度集成。这使得它成为用户希望在常规构建过程中轻量级安装 Python 的一个很好的格式,并且可以简化嵌入场景。
这些包可以使用任何能够使用 NuGet API 的工具进行安装,或者一旦知道包的 URL,也可以直接下载。该包是一个带有某些元数据的普通 ZIP 文件。它包含 CPython 解释器、标准库、开发头文件和导入库,以及 pip。它在安装时不执行任何代码,用户必须自己找到该包才能启动其中包含的 python.exe
。
可嵌入包
CPython 的可嵌入包作为我们正常发布过程的一部分进行制作和发布。它与传统安装程序一起发布到 python.org。内容相同,但是布局已更改,将所有二进制文件存储在顶层,并将标准库打包到 ZIP 文件中。包含一个 ._pth
文件来覆盖 sys.path
,以便只使用发行版中的文件,并忽略环境变量或注册表项。
此包不包含 pip,因为其目的是将其嵌入到更广泛的应用程序中。其他库应在构建时安装,因为分发后,运行时旨在成为其所属应用程序的内部实现细节。
除了其预期用途外,一些用户还试图将此包用作开发工具包而不是运行时包。据信这是因为这些用户更喜欢避免“重量级”安装程序,并认为此包旨在成为“便携式”安装(解压即可运行),这可能是因为它是在 python.org 下载页面上列出的唯一 ZIP 文件选项(这说明了这些页面上清晰度和限制选项的重要性)。希望未来的安装程序设计能够避免或限制这种混淆。
备用分发
虽然不在我们核心团队的职权范围内,但适用于 Windows 的 Python 其他发行版通常采用以项目、工作流或环境为中心的模型来安装运行时。我们指的是,工具首先安装,然后用于创建一个包含运行时以及其他依赖项的工作空间。这些工具的示例包括 conda 和 uv。
关于这些工具,有两点值得注意。首先,它们通常因影响小而受到称赞,因为它们通常不会为运行时安装额外的入口点或文件,这使得安装快速且仅限于单个项目。其次,它们的用户通常很欣赏选择特定运行时版本的便利性,或者,根本不需要选择,因为现有的规范(或约束)可以为他们选择。
这些工具倾向于满足上述第二组期望中的许多,通常将多个任务组合在一个命令中,以减少学习如何使用和组合多个命令的认知负担。
还值得指出的是,核心团队不将这些替代发行版视为任何上游发行版的竞争对手。它们是开源生态系统预期工作方式的基本组成部分。我们自己的发行版对于选择使用它们的用户来说是一种便利,因为并非所有场景都适用于工作流工具或预构建包。
挑战
我们当前的安装程序面临诸多挑战,主要分为两类:用户期望不匹配或无法实现,以及普遍的不可靠性。
传统安装程序的不可靠性最高。Windows Installer 技术非常陈旧,并且实际上已不再开发。虽然其基本功能尚可,但干扰可能来自许多来源,例如病毒扫描程序、其他安装程序、系统配置、管理员策略,甚至与安装程序位于同一目录中的其他文件。除此之外,其大多数高级和有益功能,如更新补丁、增量更新和自动回滚,对 Python 用户来说并不重要。
大多数用户期望都是由传统安装程序*定义*的,因此从定义上来说,它满足了这些期望。一个主要缺陷是它无法创建“非托管”安装——即,只将文件复制到用户系统而不进行注册。如果您已经安装了一次,并且尝试再次安装,您将只能管理(或升级)现有安装。这可能导致安装在更新时移动,从而导致用户遇到问题。
此外,PATH
环境变量无法智能修改——充其量,我们只能在安装路径前或后添加。这通常会导致最新安装的 Python 具有最高优先级。例如,如果用户安装了 Python 3.14,然后安装(或更新)3.13,则 python
命令将从较新版本切换到较旧版本。
由 PEP 397 定义并由 PEP 514 隐含更新的 py.exe
启动器,旨在避免这个问题。它使用自己的逻辑来查找已安装的版本,而不依赖于 PATH
。然而,PEP 514 的逻辑不允许对预发布或实验性构建进行特殊处理,因此 py.exe
通常默认优先选择这些构建,而不是用户预期的非实验版本。
Windows 应用商店包非常可靠,但全局快捷方式除外。这些快捷方式不是通过修改 PATH
来添加其自己的目录,而是创建在一个由操作系统管理的单一目录中,该目录包含所有应用定义的快捷方式。用户可以修改其 PATH
以排除或降低此目录的优先级,从而导致不可靠或不一致的行为,而且历史上我们也曾看到这种情况是由安装程序引起的。例如,从应用商店安装 Python,然后从传统安装程序安装 Python 并启用其 PATH
修改,几乎总是会用后安装的 Python 覆盖应用商店包的 Python。
应用商店包无法满足的用户期望通常是性能和技术方面的。由于启动应用的开销,Python 启动速度较慢。由于应用旨在彼此隔离,因此更难使用隐藏目录(例如 AppData
或 TEMP
)在不同版本的 Python 之间进行通信,因为每个版本都有自己的空间。应用受到比传统应用程序通常禁用更严格的安全要求,例如 DLL 劫持保护,这会导致某些库失败。python3
和 python
快捷方式通过系统设置进行管理,用户界面不是很好(根据 Microsoft 的说法,也不会改进)。如果不管理这些,很容易启动不需要的版本,尽管通常目标只能由用户手动更改,而不能仅仅通过安装另一个应用程序来更改。
NuGet 包和可嵌入发行版都像提取档案文件一样简单可靠地安装,尽管值得注意的是,对于许多 Python 用户来说,这不是一项常见的任务。它们根本不提供任何安装管理,除了删除和重新提取之外,无法可靠地更新。无法满足的用户期望几乎总是由于用户选择了错误的安装程序。这两个包都适用于特殊情况,尽管有文档说明,但普通 ZIP 文件的吸引力导致一些用户失败。
PyManager 概述
PyManager 是我们提议的替换安装工具的内部名称。它将以 MSIX 包的形式在 Windows 应用商店和 python.org 上发布。从这两个来源下载将获得相同的包,并且两者都将支持新版本的自动更新(通过应用商店)。
用户可见的名称将是“Python 安装管理器”,由 Python 软件基金会发布。发布后,我们将要求微软调整他们的 python.exe
存根以打开此新应用。
此应用程序不直接提供 Python 版本,但它提供用户期望可用的全局命令,以及文件关联和“开始”菜单快捷方式。操作系统将提示用户在安装后启动应用程序,这将触发当前 CPython 版本的自动安装,然后启动它。从用户的角度来看,他们拥有与今天相同的初始体验,只是首次启动时多了一个进度条。
该应用程序提供的全局命令必须是静态的并捆绑在应用程序本身中。它们只能在运行时更改其行为,并且不能重定向到不同的可执行文件,除非由用户(并且只能重定向到另一个已安装的应用程序)。因此,PyManager 将提供的命令是 python.exe
、python3.exe
、py.exe
、pymanager.exe
。每个命令都必须能够检查用户系统并选择要启动的正确运行时。此外,py
和 pymanager
将具有管理子命令,以允许添加和删除运行时。
根据 PEP 394 和 Windows 的默认行为,启动 Python 的推荐命令是 python.exe
。由 PyManager 提供时,它将查找现有安装,无论是 PyManager 管理的安装还是使用 PEP 514 查找的安装,或者它将安装最新可用的 CPython 版本并选择该版本。python3.exe
命令的行为类似,但只允许查找来自 python.org 的 3.x 安装。
PyManager 提供的 py.exe
命令将推荐用于大多数管理用途,因为它简洁。例如 py install ...
、py list ...
等。建议的命令将在后面详细介绍。PEP 397 启动器的现有行为将保留,但是,通过 py
启动不会自动安装运行时(默认情况下)。如果请求但未安装,用户将只收到错误。然而,py exec ...
子命令将自动安装并支持与裸 py
相同的选项。
这些命令由操作系统以非常低的优先级添加到用户的 PATH
中。我们在用户机器上创建的每个现有配置都将优先于这些命令,因此这些是错误消息的最后手段。因此,我们通常可以假设用户启动这些命令是因为他们没有配置更强的偏好(例如,已激活环境的用户永远不会启动我们的 python.exe
,因为激活会将不同的一个放在它前面,而想要现有 py.exe
精确行为的用户只需安装它,并且永远不会启动我们的新版本)。
命令 pymanager.exe
用于处理模糊情况。现有 Python 安装和启动器可能会遮蔽 python.exe
和 py.exe
,但在自动化环境中,这可能导致管理脚本不可靠,因此 pymanager
命令不太可能指代除 PyManager 之外的其他内容。它具有所有子命令,并且在未指定命令的情况下启动它将为用户打印帮助信息。
替换其他安装程序
我们的意图是立即停止向 Windows 应用商店发布单个版本,并计划在 Python 3.16 之前弃用并逐步淘汰传统安装程序。可嵌入发行版将保留,但它在 python.org 下载页面上的列表将逐步淘汰,并且只能通过 PyManager 获得。NuGet 包不会有任何更改。
PyManager 将以应用程序包的形式提供,可从 python.org 手动下载,并且双击安装体验通常很流畅。这提供了与当前从我们网站下载的方法等效的方式。它将捆绑一个最近的(未指定)CPython 版本,以便下载可以移动到未连接互联网的机器并在安装后仍然提供 Python 运行时。
某些自动化部署场景不适用于较新的 MSIX 格式,因此 python.org 上也将提供一个简单的 MSI。这将不包含任何选项或用户界面,并且需要管理员权限,这些权限通常可用于此类场景。此外,非标准系统(例如 Wine)上的用户(不支持 MSIX 格式)将需要 MSI。尽管未获得官方支持,但 MSI 将使这些平台能够继续使用 CPython 的上游发行版。对于大多数其他用户,不建议使用 MSI,MSIX 被认为是默认选项。
值得注意的是,MSI 安装无法完全兼容 MSIX,同时拥有这两种安装的用户很可能会遇到困惑或问题。预计只有不支持应用商店应用的用户才会使用 MSI。
我们的发布流程将开始向 python.org 发布普通的 ZIP 包。这些包将从 FTP 页面提供,但不会直接列在常规下载页面上。
当前分发自己构建的 CPython 的第三方工具将欢迎使用我们的工具,但预计将成为需要支持的用户的初始联系点。
项目所有权和开发
PyManager 将在 CPython 仓库旁边的独立仓库中开发和维护,并遵循相同的条款。CPython CLA 将适用,并且所有(且仅)核心开发者将拥有提交权限。
PyManager 的发布独立于 CPython 的发布。版本无需匹配,发布也无需同步。除非另有安排,PyManager 的发布经理是 Windows 的构建经理。
规范
注意
在本文档中,所有命令行选项都将显示为一个或两个连字符。在实现中,所有选项都将支持一个或两个连字符或一个正斜杠,以兼容 Windows 和 UNIX 约定。
执行子命令
py [-V:<TAG>] [interpreter opts] [script.py|-m module|-c code] [script args]
py [-3.*] [interpreter opts] [script.py|-m module|-c code] [script args]
python ...
python3 ...
pymanager exec -V:tag ...
pymanager exec -3.* ...
此子命令用于选择和启动运行时。它是 py
命令的默认操作,也是 python
和 python3
命令支持的唯一操作。为了与这些命令的现有用法保持一致,每个情况下的默认选项略有不同。
此子命令在 py
和 pymanager
上都可用。然而,由于 py
默认提供它,我们不期望用户在那里使用它。我们的意图是,py
、python
和 python3
命令是启动运行时的默认方式,而 pymanager exec
用于高级场景。
-V:tag
命令用于从命令行请求特定的运行时。tag 是一个 Company\\Tag
对,如果未包含斜杠则仅为 Tag
,并按照 PEP 514 的定义使用。-3.*
选项被解释为 -V:PythonCore\\3.*
。此选项仅适用于 py
和 py exec
变体。
如果命令行中未指定标签,并且指定了脚本文件,则将检查脚本是否存在 shebang。如果找到与已识别模式匹配的 shebang,它将提供用于搜索的标签,或者它将覆盖所有其他处理,并且其指定的 exécutable 将在不进一步努力的情况下启动。这是为了处理允许在原本旨在提高可移植性的功能中使用任意 Windows 特定路径的(不幸的)旧版支持。通常,包含 /usr/bin/python3.13
等简单模式的 shebang 是预期的,而那些使用 /usr/bin/env python
的 shebang 可能没有益处,因为环境往往不如我们的搜索可靠。
如果尚未请求标签,将查询 VIRTUAL_ENV
环境变量以查看是否已激活环境。如果已激活,则将成为请求。
如果在此阶段已请求标签,则 python3
命令将验证其是否匹配 PythonCore\\3.*
,如果不匹配则退出并报错。这允许 python3
命令在与其他平台一致的活动环境中使用,但如果环境不包含该命令则不能使用。这适用于 Windows 上大多数现有版本的 Python。(此行为的替代方案是使 python3
在环境处于活动状态时始终报错,因为任何其他行为都会对用户造成不一致。)
如果未请求标签,则将查询默认值。对于 python3
,这是 PythonCore\\3
,但对于所有其他命令,它都从配置中读取(可能涉及环境变量)。如果仍然为空,则允许任何标签。
然后选择并启动与标签匹配的最佳已安装运行时,并使用剩余的命令行。
如果未找到匹配的运行时,py exec
和 pymanager exec
命令将自动安装并启动一个(如果用户配置允许)。py
、python
和 python3
命令将报告错误并退出。但是,在根本没有运行时可用的情况下,包括未从其他安装程序检测到的运行时,第一个运行时将自动安装。这提供了一个有用的首次启动体验,新用户在刚安装 PyManager 后将直接启动最新(或请求的)CPython 版本。在此首次之后,当请求的运行时未找到时,将再次报告错误。
安装子命令
py install [-s|--source <URL>] [-f|--force] [-u|--upgrade] tag [...]
py install [-s|--source <URL>] [-t|--target <DIR>] tag
py install [-s|--source <URL>] [-d|--download <DIR>] tag [...]
py install --refresh
注意
此命令及其后的所有子命令也都在 pymanager
下可用。然而,由于我们打算将 py
作为常用命令,我们只展示它。
此子命令将在一台或多台计算机上安装一个或多个运行时。标签是 Company\\Tag
对(如果未包含斜杠,则仅为 Tag
),用于搜索索引文件。公司名称进行不区分大小写的匹配,优先匹配完整名称而不是前缀,标签使用不区分大小写、数字感知匹配,带点号的数字被视为版本。标签必须匹配列出的“安装用途”标签之一,并且条目列出多个此类标签以处理缩写请求。特殊标签 default
解析为用户配置的默认值(通常为 3
;有关配置设置的详细信息,请参阅稍后的“配置”)。
标签也可以指定为约束,使用 >
、>=
、<
、<=
或 !=
后跟 Company\\Tag
或 Tag
值。在匹配约束时,仅使用主标签元数据进行比较。由于比较是版本感知的,因此诸如 >3.10
之类的约束将选择 3.11 作为最小值,而 >3.10.0
可能会选择 3.10.1。
在某些情况下,针对任意标签的约束行为可能不直观。预计约束将主要用于上游发行版,这些发行版通常使用版本形式的标签,并且主要用于处理 Requires-Python
等其他元数据的情况。用户预计会使用较短的标签来方便,而不是范围。
默认索引文件托管在 python.org 上,包含所有可安装版本的安装信息,包括包 URL 和哈希值。用户可以通过配置文件或 --source
命令行选项指定备用索引,或者管理员通过配置文件指定。请求的标签与索引文件进行匹配,如果找到精确匹配项,则选择该包。在没有精确匹配项的情况下,将使用前缀匹配。在这两种情况下,标签中的数字都按逻辑处理——即 3.1
是 3.1.2
的前缀,但不是 3.10
的前缀。有关如何在索引文件中指定安装标签的详细信息,请参阅下面的“索引 Schema”。
如果现有安装已满足某个标签,则不会安装任何内容。用户必须传递 --upgrade
或 --force
选项才能替换现有安装;前者仅用更新的版本替换,而后者即使是相同版本也会删除并替换。
不提供任何标签而调用命令不会安装任何内容,但会显示帮助文本和错误(与任何无效选项一样)。然而,不带标签传递 --upgrade
将尝试升级所有安装。
传递 --refresh
将重新生成所有安装的所有元数据和快捷方式。这有意地一次应用于所有安装,因为快捷方式优先级依赖于所有安装的一致性(例如,最新的 3.x 版本应该获得 python3.exe
快捷方式,如果用户可以选择只刷新旧版本,这就会变得复杂)。
如果只传递一个标签并传入 --target <DIR>
选项,则该运行时将被解压到指定目录,而不会注册为安装(或生成别名或快捷方式)。这旨在涵盖嵌入情况,或下载用于不兼容平台的文件。使用多个标签和 --target
将导致错误。
如果传入 --download <DIR>
选项,则运行时包将被下载但不安装。它们将作为其源包存储在指定目录中,并创建一个引用这些文件的 index.json
。此索引以后可用于使用 python install --source <index.json> [tag ...]
执行离线安装。
卸载子命令
py uninstall [-y|--yes] [--purge] [tag ...]
此子命令将卸载当前机器上的一个或多个运行时。标签与 install 命令完全相同,包括前缀匹配,但只检查现有安装。除非传入 --yes
选项,否则在卸载每个运行时之前会提示用户。
如果 --purge
选项在没有标签的情况下传递,则(确认后)所有运行时,以及快捷方式和任何缓存文件都将被删除。如果 --purge
与任何标签一起传递,则会产生错误。
卸载 PyManager 不会卸载任何已安装的运行时。出于技术原因,这无法可靠实现(我们无法在卸载时运行任意代码),因此我们特意确保任何已安装的内容将继续工作。重新安装 PyManager 允许恢复对这些安装的管理。在卸载 PyManager 之前运行 py uninstall --purge
将执行完整的卸载。
列表子命令
py list [-f|--format <FMT>] [-1|--one] [--only-managed] [tag ...]
py list [-f|--format <FMT>] [-1|--one] [--online] [--source <URL>] [tag ...]
py [--list|--list-paths|-0|-0p]
此子命令将列出所有或部分匹配指定标签或范围的安装。如果未提供标签,则列出所有安装。PyManager 未管理的运行时(包括活动的虚拟环境)可能会单独列出。
默认格式是用户友好的。其他格式将包括机器可读和单字符串格式(例如,--format=prefix
只打印 sys.prefix
,每行一个)。确切的格式列表留待实现。
如果提供了 --one
,则只列出最佳结果。这是为了帮助 shell 脚本在不启动运行时的情况下找到默认(或合适)的运行时。(请注意,“最佳”定义不严格,但如果结果中包含用户首选的默认环境,则它始终是该环境。)
--only-managed
选项省略了已发现但不由 PyManager 管理的运行时,例如,使用常规 PEP 514 查找找到的运行时。
传递 --source
(或 --online
以隐式传递默认源)将搜索在线索引而不是当前已安装的运行时。此选项位于此处而不是 install
子命令上,因为过滤和格式化选项已在 list
上可用。
py.exe
启动器中的旧版 --list
、--list-paths
、-0
和 -0p
参数也将提供。但是,它们不支持此处列出的新选项,并且仅限于重现现有启动器的输出。在此列表中,非托管安装无法区分。
帮助子命令
py help <COMMAND>
此子命令将显示每个指定命令的帮助文本,如果未指定任何命令,则显示命令列表。指定一个命令等同于 py <COMMAND> --help
。显示子命令列表是 pymanager
命令的默认操作。
添加此命令主要是为了提供一种简单的方法来告诉用户如何查找更多信息:他们可以被告知运行 py help
。这避免了不得不覆盖或扩展 python -?
的输出,否则它会转发到选定的运行时并已打印至少一屏文本。
自动安装后(例如,在未安装任何内容的情况下运行 python
),将显示一条消息,告知用户可以运行 py help
以获取有关如何管理其安装的更多信息。
环境变量
安装应用商店应用时,无法自动更新环境变量,因此不会自动进行任何更新。核心命令在正常运行的机器上应该已经可用。
用户 PyManager 数据目录中的一个目录用于生成别名。如果需要,用户可以自己将此目录添加到其 PATH
中。此目录的内容将由 PyManager 管理,并将包含用于直接启动已安装运行时的可执行文件(例如,对于 Python 3.13 的安装,包含 python3.exe
和 python3.13.exe
)。每当将别名添加到此目录时,将检查 PATH
,如果缺少,将向用户显示包含要添加的路径的消息。
由安装到运行时的包安装的脚本将位于另一个目录中。由于当前设计,我们不认为将它们都安装到单个目录或多个运行时共享的目录中是安全的。但是,未来的开发可能会包括一个命令,用于 PyManager 根据已安装包中的元数据生成自己的入口点。
文件关联
安装 PyManager 时将创建标准文件关联,并将使用 PyManager 的全局 python.exe
别名启动脚本和打包应用程序。这为双击脚本或 .pyz
文件的用户提供了合理的行为。
窗口化可执行文件
对于前面描述的每个全局别名,还存在一个 *w.exe
。这些在不创建或附加控制台窗口的情况下启动,这通常意味着它们将只显示脚本创建的 UI。例如,IDLE 总是使用 pythonw.exe
启动,因为这避免了不必要的原生控制台。
这些命令在其他方面与其控制台对应项行为相同。
配置
PyManager 使用基于 JSON 的配置文件的层次结构进行配置。命令行选项始终覆盖配置文件选项。用户可编辑位置的配置文件可以通过配置或命令行选项禁用。
按优先级升序排列,这些文件将位于:
- 在应用程序包中
- 由仅限管理员的配置指定(见下文)
- 在
base_config
设置中(默认值:无) - 在
user_config
设置中(默认值:%AppData%\\Python\\PyManager.json
) - 在
additional_config
设置中(默认值:%PYTHON_MANAGER_CONFIG%
) - 使用
-c
命令行选项指定
每个配置选项的具体行为留待实现。然而,其他部分讨论了一些预期的选项。
提供应用包配置以允许 PyManager 嵌入到其他应用程序或包中。例如,替代分发可能希望包含 PyManager,但使其从自己的索引中查找安装。应用包配置允许重用我们的构建并覆盖默认设置。
user_config
和 additional_config
设置在较早的配置文件中预先配置,允许它们被仅限管理员的配置或备用根配置覆盖。如果配置文件覆盖了导致文件加载的设置,则该设置将被忽略。base_config
设置类似,但最初为空,旨在通过管理员配置轻松覆盖。
提供仅限管理员的配置,允许管理员使用现有工具(例如组策略或注册表更新)管理其控制下的系统。根据设计,这些控件无法被覆盖,因此管理员可以部署策略以阻止或限制 PyManager 的使用。这些控件对于允许 PyManager 安全地部署到特定环境至关重要,如果没有它们,它将简单地被禁止,这些用户将无法访问 Python。
意图是,主要的仅限管理员配置是一个新 base_config
配置文件的路径,管理员可以将其部署到任何受控位置。这允许网络管理员控制其用户默认 Python 运行时的来源,而无需强制限制他们,或者覆盖其他配置文件来源(命令行选项除外)。
索引 Schema
索引文件可在线或本地获取,并向 PyManager 提供查找、选择、安装和管理任何 Python 运行时所需的所有信息。
索引以 JSON 格式存储。主顶级键是 versions
,其中包含一个对象列表。每个版本对象都有自己的 Schema 版本,并且没有整体文件 Schema 版本。未来的更改可能会添加额外的顶级键,以提供无法安全集成到现有功能中的功能。
版本对象可以在索引文件和存储在每个包归档根目录中的 __install__.json
之间拆分。捆绑文件中的条目将填补索引文件中的任何空白。这旨在允许从索引文件中删除通常很大的 shortcuts
键,但也可能扩展到 alias
、executable
和 executable_args
。从索引中省略其他键可能会导致安装包时出现问题。确保正确行为留待实现——这不是一个互操作性点,因此我们不打算在此处指定详细信息(超出适用的正常兼容性要求)。
第二个顶层键 next
包含一个可选的指向另一个索引的 URL。如果 PyManager 无法在包含的版本中找到合适的包,则可以使用此 URL。目的是允许存档旧索引并在需要时才访问它们,从而在不切断用户访问旧版本的情况下减小初始下载的大小。在搜索合适的安装时,如果找到可行的候选,则不会搜索后面的索引(换句话说,首先咨询的索引应该包含最新版本)。
初始 Schema 如下所示
SCHEMA = {
"versions": [
{
# Should be 1.
"schema": int,
# Unique ID used for install detection/side-by-side.
# Must be valid as a filename.
"id": str,
# Name to display in the UI
"display-name": str,
# Version used to sort packages. Also determines prerelease status.
# Should follow Python's format, but is only compared among releases
# with the same Company.
"sort-version": Version,
# Specifies platforms to consider this package for.
# Initially, 'win32' is the only supported value. Others may be
# defined in the future. This condition is evaluated silently, and
# is not intended to replace platform requests in "install-for".
"platform": [str],
# Company field, used for filtering and displayed as the publisher.
"company": str,
# Default tag, mainly for UI purposes.
# It should also be specified in 'install-for' and 'run-for'.
"tag": str,
# List of tags to install this package for. This does not have to be
# unique across all installs; the first match will be selected.
# For example, the 3.10.5 package may list '3', '3.10' and
# '3.10.5' so that any of those may be specified to install it.
# Matches are number aware, so that 3.1 is not a prefix of 3.10.
"install-for": [str],
# List of tags to run this package for. Does not have to be unique
# across all installs; the first match will be selected. The target
# is the executable path relative to the root of the archive.
# Explicit args (optional) are inserted before user args.
"run-for": [{"tag": str, "target": str, "args": [str], "windowed": int}, ...],
# List of global CLI aliases to create for this package. Does not
# have to be unique across all installs; the first match will be
# created.
"alias": [{"name": str, "target": str, "windowed": int}, ...],
# List of shortcuts to create for this package. Additional keys on
# each instance are allowed based on the value of 'kind'.
# Initially, 'kind' supports the following values:
# * 'pep514' - other keys define registry values to set
# * 'start' - generate shortcuts in the user's Start Menu
# * 'uninstall' - generate an Add/Remove Programs entry
"shortcuts": [{"kind": str, ...}, ...]
# Default executable path, relative to the root of the archive.
# Usually the values from 'run-for' will be used instead, and this
# is mainly for display purposes.
"executable": str,
# Default executable args
"executable_args": [str],
# URL to download the package archive from
"url": str,
# Optional set of hashes to validate the download. Hashes are stored
# as hex digests. Any hash supported by hashlib without OpenSSL is
# permitted.
"hash": {
"<hash_name>": str,
}
}
],
# URL (or relative path) to the next index file
"next": str,
}
Shebang 处理
为了与为 sh 样式的 shell 设计的脚本有限兼容,PyManager 将检查脚本中的 shebang 行。指定 Python 命令的 shebang 行将用于(当命令行未覆盖时)为脚本选择合适的运行时。
与目前 py.exe
启动器中的支持不同,我们建议将此功能简化为仅支持命令与已安装全局别名匹配的 Python 命令,任何不匹配的都将被视为任意可执行路径。实际上,对于非刻意情况,这预计会产生相同(或更好)的结果,但在用户依赖现有启动器未指定边缘情况行为的情况下,可能会导致细微的变化。
要检测的特定模式留待实现,但应与现有启动器大致兼容。
基本原理
“更改” python.exe 命令
有人可能会争辩说,PyManager 提供的全局 python.exe
别名“不是真正的 Python”,因此应该使用不同的名称。虽然这确实是严格意义上的事实,但我们认为它应该被使用的原因有三。
首先,每天有数千用户在干净的机器终端上输入 python
后,通过应用商店页面进行安装。由于这种重定向的实现方式,如果他们安装的应用不提供 python
命令,则重定向将保持不变。为了确保用户不会一直回到应用商店,我们需要提供此命令。(python3
也是如此。)
其次,“不是真正的”别名的替代方案不是“真正的”别名。什么都没有。我们无法用遵循用户偏好或安装的别名替换全局静态别名,因此替代方案将是什么都不提供,并且 python
在所有情况下都是一个错误。这更糟糕,在我们看来,对 Python 的声誉是积极有害的。
第三,尽管 python
别名的底层实现比默认的 Programs/python.c
更复杂,但使用体验是相同的。该别名仅在没有其他明确偏好(即 PATH
上没有其他内容)时启动,它尊重任何间接偏好(例如通过配置或 shebang),并且它启动用户机器上最合适的 Python 版本。这比任何替代方案都更接近全局 python
命令的预期行为。
有人指出 Gentoo 也分发了一个智能的 python
命令,以更好地服务于他们的用户,而不是简单的符号链接。
替换 py.exe
py.exe
启动器旨在提供 PyManager 将复制的某些功能——特别是启动已安装运行时的能力。尽管其历史悠久,但该启动器似乎并未成为大多数用户的首选方法,许多用户更喜欢对 PATH
环境变量进行全局修改。然而,该命令本身已变得依赖,并且应尽可能长时间地保留。这通过两种方式实现。
首先,我们随 PyManager 安装我们自己的 py.exe
别名,该别名提供相同的功能,以及 PyManager 特定的功能。这旨在随着时间的推移成为 py.exe
的默认/首选安装。
其次,我们为每次安装生成 PEP 514 元数据(如果请求),这允许旧版 py.exe
继续与 PyManager 管理的安装正常工作。
由于现有 py.exe
启动器如何配置自身,以及 PyManager 的 MSIX 包的限制,PyManager 的 py
别名无法覆盖启动器。因此,安装启动器的用户总是会发现 py
解析为启动器。最终,解决此问题以支持 PyManager 的唯一方法是卸载启动器,这可以通过标准“已安装应用”控制面板完成。
我们建议更新现有启动器(该启动器将继续随传统安装程序发布),以检测新子命令的尝试使用,并向用户提供有用的消息而不是当前的错误。这些警告还可以检测用户有意启动无扩展名文件的情况,并保留该行为,从而为无法对其设置进行其他更改的用户提供可行的替代方案。
与 venv 的交互
由标准库 venv
模块实现的已激活虚拟环境,将修改用户的 PATH
环境变量,以确保 venv 启动器优先于其他可执行文件。因此,当 venv 被激活时,PyManager 只能通过其除 python
之外的别名启动。当检测到活动的虚拟环境时,它将被视为用户的默认运行时(卸载除外),这确保了其他命令也将按预期运行。
这意味着工作虚拟环境将像今天一样运行,无需 PyManager 的额外支持。
向后兼容性
通常,不同次要版本(3.x
到 3.y
)之间的安装过程不提供兼容性保证,因此“必须使用不同的安装程序”不被视为兼容性中断。Python 版本的安装仅受此更改的影响,即安装方法修改了它们的行为。通常,大多数安装将更接近于用户自己从源代码构建的行为。
话虽如此,当用户转向新的安装过程时,一些更改将影响某些用户。本节概述了我们所知道的尽可能多的这些更改,没有特定的顺序,并且很可能构成迁移指南的基础。
脚本化下载
编写脚本以生成旧安装程序下载文件名的用户会发现这些脚本已损坏。这些 URL 从未保证稳定或可预测,因此我们除了道歉并建议用户使用我们自己的工具进行下载之外,别无他法。
传统安装程序的弃用期允许这些用户有时间了解即将到来的变化。在可能的情况下,我们将在传统安装程序中添加弃用警告。
脚本化安装
编写脚本以特定选项执行我们的安装程序的用户必须更改其脚本。首先,大多数选项已被删除,剩下的选项则采用了新的拼写。由于在没有人为干预(即,有人必须更改命令)的情况下,不可能达到将旧安装程序的选项传递给新安装程序的状态,因此这被认为是可以接受的更改。
传统安装程序的弃用期允许这些用户有时间了解即将到来的变化。在可能的情况下,我们将在传统安装程序中添加弃用警告。
旧运行时已安装
已安装现有运行时的用户会发现它们由 PyManager 及其别名选择,前提是注册未损坏。
已安装运行时之间的优先级顺序已更改为仅在明确请求时才包含预发布版本(例如,-V:3
将匹配 3.14.0 而不是 3.15.0a1,但 -V:3.15
将匹配 3.15.0a1),并正确排序标签上的文本后缀(例如,3.14t 现在*低于*3.14 的优先级)。
虽然可以在可能影响用户的情况下提供警告,但此类警告会被认为非常嘈杂(例如,每次启动 python
时都会收到消息,因为您安装了未选择的预发布版本),并且不必要地使选择逻辑复杂化。此更改将仅记录。
旧 py.exe
启动器已安装
未手动卸载旧的 py.exe
启动器的用户会发现他们现有的和新的 Python 安装都已找到,尽管在版本匹配的情况下,现有安装将优先于新安装(而新的 py
将选择新安装)。
他们还会发现 py list
等命令不起作用。这里的解决方案是使用 Windows 设置卸载启动器。
无法检测到用户意外保留了旧的 py
安装,也无法为他们删除它。此更改将仅记录。
误配置的 venv 已激活
激活已损坏或配置错误的虚拟环境(缺少 python.exe
或未将其添加到 PATH
)的用户可能会收到与以前不同的错误。
PyManager 的全局 python
别名将被找到并执行,从而抑制任何系统“未找到”错误。由于它未能找到环境的实际运行时,因此它将失败,尽管代码和消息可能不同。
由于此场景需要一个已损坏的系统,此更改将仅记录。
旧版本可用性
PyManager 首次发布之前的 Python 版本可以回填到 python.org 索引中,可以基于新重新打包的存档,也可以使用 NuGet 中几乎等效的包(后者不包括 Tcl/Tk,这使得它们对于某些用户来说显著不兼容,但这对于特别旧的版本来说可能没问题)。
截至本 PEP 获得批准,计划是为 Python 3.10.0 及更高版本的所有版本(包括补丁版本,但不包括预发布版本)创建包,以便所有非 EOL 版本都可以使用 PyManager 完全安装。Python 3.5.0 及更高版本(不包括预发布版本)将直接引用 NuGet 上的包,使其以简化形式轻松可用。这两种方法都不需要重新构建现有版本,也不需要对这些版本的源代码进行任何更改。
管理员安装
为所有用户安装 Python 不再可能,因为 PyManager 只会安装到用户自己的目录中。目前没有提出任何场景表明每机器安装符合我们上游分发的意图,因此我们根本不会提供此选项。希望获得此功能的第三方鼓励提供他们自己的分发版本。
PyManager 只能为所有用户安装,尽管 MSIX 不需要管理员权限即可安装,并且可以由管理员进行广泛配置,包括限制用户可以安装的实际运行时。此外,PyManager 支持本地提取以进行捆绑,因此嵌入式应用程序可以轻松生成自己的布局,如果他们愿意,可以为所有用户安装。
由于此场景无论是否进行任何更改都需要管理员干预,因此这只会记录。
构建时安装
使用可嵌入发行版的用户可能需要更改发现包 URL 的新方法,但建议是使用 PyManager 发现和安装。由于安装程序的更改,预计不会有任何差异,并且可嵌入发行版包将与今天相同。
未对 NuGet 包提出任何更改。
单架构安装程序
当前的提案只提供 Intel 64 位版本的 PyManager。这将阻止使用纯 32 位操作系统或 CPU 的用户安装 PyManager。由于这在当今机器中所占比例极小,并且在开发人员机器(PyManager 的目标受众)中所占比例更小,我们不担心排除这些用户。
Windows ARM64 机器通过高效仿真支持运行为 Intel 64 位构建的二进制文件。CPython 32 位版本仍将可用,因为它们在与 32 位可执行文件集成方面具有重要作用,而这不允许替换 64 位二进制文件。由于 PyManager 作为独立可执行文件运行,因此这不是管理器必需的功能。
测试套件和调试符号
现有安装程序可选地允许安装 Python 标准库测试套件和可选安装调试符号。这些都不是大多数用户所必需的,但对于某些用户来说很方便。初步测试表明,省略测试套件和调试符号可以节省约 60% 的压缩包大小(从 46MB 到 18MB)。
因此,PyManager 安装的“默认”CPython 包将不包括测试套件或调试符号。但是,将有第二组包包含这些附加功能,分组在 PythonTest
下(与默认的 PythonCore
相对)。例如,py install 3.13
将安装默认版本,而 py install PythonTest\\3.13
将安装带有附加文件的第二个运行时(可以使用 py -V:PythonTest\\3.13
启动,如果未安装等效的 PythonCore
版本,则只需 py -V:3.13
即可)。
调试二进制文件不再分发,所有其他可选功能都默认包含。
全局 pip 命令
与当前的 Windows 应用商店安装不同,不包括全局 pip
命令(传统安装程序也不包括全局 pip
命令,除非选择修改 PATH
和安装 pip 的选项;第一个选项默认关闭)。这会影响全局安装包,这已经不被推荐,但对激活的虚拟环境没有影响。
现有的建议仍然是运行 python -m pip
或 py -V:<TAG> -m pip
来启动 pip。
安全隐患
在本节中,我们比较了安装程序本身与现有安装程序的安全隐患。Python 安装在机器上的隐患以及恶意用户执行安装程序的能力不在讨论范围之内。
安装程序引入的典型风险是,提升权限的安装可能会对系统进行更改,从而允许低权限用户以后影响高权限用户,例如,通过不恰当地设置共享文件夹上的访问控制。PyManager 仅以与用户相同的权限级别运行,因此不会引入任何权限提升路径。
使用前面描述的 MSI 进行安装可能会引入额外的风险,因为使用了较旧的安装程序技术。典型用户被引导到 MSIX 或 Windows 应用商店安装路径,这些路径是安全的,并且假设 MSI 用户能够确保其安装过程的安全性(例如,通过正确引用其启动安装程序的命令并确保初始系统配置是合适的)。
一旦 PyManager 安装在机器上,恶意用户很可能会使用它来安装 Python。前面“配置”中描述的仅限管理员的配置旨在控制这些场景。但最终,能够运行 PyManager 的攻击者能够做用户可以做的任何事情,只有完整的应用程序白名单方法才能阻止使用 Python。
通过 HTTPS 获取包受到连接安全的保护。我们使用支持公共和企业证书以及经过身份验证的代理服务器的本机 Windows 下载机制。提要可能包含可下载包的哈希值,这些哈希值将经过验证,但 PyManager 中没有内置的第三方托管验证。这与今天的模型一致。
PyManager 的运行时安装可由当前用户完全访问和修改。这等同于使用传统安装程序或 NuGet 包的典型安装,但比使用传统安装程序的商店安装或每机器安装更容易受到篡改。无法完全保护安装免受安装它的用户的影响。重新安装或更新安装会执行全新安装,这将恢复自原始安装以来可能发生的任何篡改。
PyManager 在安装运行时时生成的别名设计为使用经过签名、未修改的可执行文件,该文件使用相邻的数据文件启动正确的目标。这很容易被滥用以将启动器指向启动替代方案,但是,解决此问题的唯一方法是牺牲对可执行文件本身的信任,使其易于替换而不是数据。这种风险已经存在,并且等同于替换用户可能启动的脚本或标准库的任何部分。重要的是,由于别名在用户之间不共享,因此没有沿此路径的权限提升。
PyManager 没有执行每机器安装的机制。这对于某些用户来说可能是有用的功能,因为它允许安装完全不可由普通用户修改(虚拟环境和用户站点文件夹除外)。管理员可以使用 PyManager 和其他操作系统命令手动模仿此类功能,但这不被视为关键工作流程。建议的替代方案是管理员提供 PyManager 并覆盖其配置。
对现有 PEP 的影响
此提案将通过将相同的功能定义为具有相同名称的新工具的一部分,从而有效地取代 PEP 397(“Python launcher for Windows”)和 PEP 486(“Make the Python Launcher aware of virtual environments”)。两者都已被视为最终版本,并且启动器由其文档和正常兼容性流程定义。新功能基于当前实现,而不是原始 PEP 文本。
此提案对 PEP 394(“The “python” Command on Unix-Like Systems”)没有影响,并且被认为与它一致,为 Windows 设计了一种方法,允许向所有平台上的用户提供类似的指导。
此提案对 PEP 514(“Python registration in the Windows registry”)没有影响,实际上通过更灵活的系统来注册我们自己的运行时,提高了我们遵循它的能力。遵循 PEP 514 的工具将找到选择使用注册的任何运行时,无论它们是如何安装的。
如何教授此内容
基本用法
此提案的一个核心目标是“在终端中输入‘python’”对于最基本的情况来说是足够的指令。由于 Microsoft 添加的重定向器,遵循此指令至少会产生一些有用的结果,而有了 PyManager,我们可以确保“有用的结果”意味着用户正在运行最新版本。
为了解释实际发生的情况,我们建议使用以下文字作为介绍
Python installs on Windows are managed using an installer tool. After it has
been installed, you can run ``python`` to launch the interpreter, and it will
choose the best version already installed, available online, or referenced by
the script you are launching (if any). If you have a preference for a
particular version, you can specify it with ``py -V:<version>`` followed
by the rest of your command.
To install a version of Python without running any command, use ``py install
<version>``. You can see all of your installs with ``py list`` and remove them
with ``py uninstall <version>``. Run ``py help`` to see all the options that
are available.
Because each version of Python will be shared by all your projects, we
recommend using virtual environments. This will usually be created for a
particular Python version by running ``py -V:<version> -m venv .venv``, and
activated with ``.venv\Scripts\Activate``. Now, rather than the install
manager, ``python`` or ``py`` will always launch your virtual environment, and
any packages you install are only available while this environment is active.
To get access to the manager again, you can ``deactivate`` the environment, or
use ``py <command>``.
许多 Python 项目在其自己的 README 文件中包含了如何启动其项目的信息。历史上,由于用户可用的选项范围广泛,此类信息一直很复杂。我们建议,在安装管理器发布后,此类指导可以按以下方式编写
To install and use our application, first install Python following the
guidance for your operating system at https://docs.pythonlang.cn/using/. Then,
create a virtual environment and use 'pip' to install.
``python3 -m venv .venv``
``source .venv/bin/activate`` or ``.venv\Scripts\Activate`` (on Windows)
``python -m pip install OurAwesomePackage``
...
如果说明不包含有关虚拟环境的信息,则可以显示 python
或 python3
命令,并且在 Windows 上,两者都将按预期为使用安装管理器的用户运行。
目前引用 Windows py
的说明可以继续这样做,因为安装管理器提供了实际上等效的命令。希望提供 Windows 特定说明的项目,例如通过使用 -V:
或 --install
选项安装正确版本,也应链接到 文档 作为确保安装管理器已安装的指导。
卸载
在用户可能考虑删除安装管理器之前,彻底卸载是一个重要的议题。由于在删除管理器时并非所有安装部分都可以自动清理,我们选择不删除大部分。因此,虽然默认的 python
和 py
命令会消失,但所有已安装的运行时仍然存在且可用。
我们建议这样的解释
Before you uninstall the Python install manager, you'll want to uninstall any
runtimes that you added. This can be done easily with the "purge" option:
``py uninstall --purge``
This will remove all installs and any shortcuts that would otherwise be left
behind. If you already removed the manager, you can reinstall it and run the
above uninstall command again to clean up. Individual runtimes can be
uninstalled by specifying the tag instead of ``--purge``. Tags can be found
by looking at ``python list``.
配置
配置文件是一个常见的特性,它将被记录,但不需要向普通用户教授。同样,高级部署选项,例如系统管理员或希望其用户使用首选索引的组织可能使用的选项,最好在参考资料中介绍。
自定义索引
我们建议只有在指示用户安装专用运行时或发行版时才需要引入索引。预计寻求提供索引的管理员会积极在文档中查找相关信息。
为了解释如何以及何时使用替代索引,我们建议使用以下文本
Our distribution can be installed on Windows using the Python Install Manager
(include link) by referencing our index:
``py install --source <your index URL here> latest``
This index contains all our versions. Use ``py list --source <URL>`` to
see everything that is available.
参考实现
参考实现可在作者的存储库中找到,预编译的 MSIX 包可在发布页面下找到。此示例包含一个捆绑索引,而不是托管索引,并引用了一系列现有 NuGet 包以允许安装测试。
参考文档也可在同一存储库中找到。
被拒绝的想法
使 PyManager 在所有平台可用
虽然我们并非天生反对这个想法,但它依赖于更多组件的协调才能实现。
首先,就目前而言,参考实现具有许多特定于 Windows 的知识。需要收集和实现其他平台的等效知识,以及非 Windows 平台的任何额外行为。
其次,我们需要一个预构建的、可重定位的二进制文件来源,可以将其提取到系统中。虽然此类来源确实存在,但由于我们在供应链中的位置,我们无法合理地使用它们(它们应该使用我们)。对于 Windows,我们自己的二进制文件已经满足这些标准,因此我们可以不加修改地重新打包它们。
第三,当前的实现依赖于捆绑的 Python 运行时,出于显而易见的原因,它必须与任何用户干扰隔离。这也需要上述可重定位的二进制文件,我们目前只为 Windows 拥有。
由于使其在其他平台正常运行需要额外的步骤,并且事实上没有必要替换这些平台的现有安装程序,我们认为此想法超出本 PEP 的范围。将来可能会继续进行(并且最有可能这样做的贡献者已经表示他们正在研究它并且能够使用一致的接口)。
包含预装有管理器的运行时
该提案是让 PyManager 包含一个完整的 Python 运行时,以便其 python.exe
别名可以直接引用它,而不是动态解析到最佳可用版本。
为了稳定性和更新,运行时发布与管理器完全独立非常重要。更新管理器应该可以在不影响任何现有运行时安装的情况下进行,同样,不应该要求更新管理器以获取更新的运行时。
假设我们要在管理器中包含 Python 3.14.0,这样就不需要安装它,那么后来用 3.15.0 替换它将是一个重大更改。由于我们只有一个管理器的安装,这将导致最新的安装获得最旧的运行时。
这也忽略了 PyManager 的 python
别名是一个未指定版本的情况——当用户启动此别名时,是因为他们不关心他们获得了什么版本,因此没有指定一个。在这种情况下,我们应该选择最佳可用版本,并允许他们根据需要稳定它(无论是通过 shebang 还是活动环境)。
包含运行时而非管理器
这是我们正在努力改变的现状。如果您读到这里仍然选择提出这个论点,请回去重新开始。
使用内置模块而非子命令
针对使用 py list
或 py install
等命令的两种替代方案是使用专用模块,像 py -m list
和 py -m install
这样调用,或者使用像 py -m manage list
这样调用的单个专用模块。这个想法被否决,因为它试图将现有语义用于无法通过这些语义可靠实现的场景,因此需要一个更难解释、理解和维护的特殊情况。
这个想法被否决的主要原因在于两个原本理想的语义之间的相互作用:首先,默认的 py
命令应该像直接启动一样启动最新可用的运行时;其次,-m
的行为在某些情况下不应被视为特殊情况。如果放弃第一部分,我们将自由修改命令使其按用户预期的方式运行——如果我们同意完全破坏兼容性,没有人会提出兼容性问题。然而,如果放弃第二个约束,用户将承担确保混淆的负担。(我们不建议放弃任何一个——毕竟这是一个被拒绝的想法——但这有助于说明有哪些选项。)
首先,由于其中一个子命令旨在安装您的第一个运行时,我们不能将 python -m [manage] install
视为通过默认运行时运行——根本没有默认运行时!它本质上需要特殊情况处理才能读取命令并通过不同的程序执行它。
此外,Python 允许其他选项在 -m
之前或与 -m
混合,这需要特殊情况支持。
最后,-m
选项的语义包括在初始 sys.path
中搜索匹配的模块名称。这比裸名称的搜索范围要广得多。py -m install
将乐意执行 install.py
、install.pyc
、install.pyd
、install\\__init__.py
等,在检查文件系统、环境、注册表以及其中包含的任何传递路径后。与 py install
相比,后者只在当前工作目录中查找名为 install
的文件,-m
行为更有可能已被实际场景所依赖。(例如,Django 项目通常有一个 manage.py
脚本,这意味着 py -m manage
总是会表现不正确。)
将 py -m install
更改为不像 -m
那样行为,而是执行内部命令,这比更改 py install
更容易破坏用户。因此,这个想法被拒绝。
使用新的命令行选项而非子命令
子命令的一个合理替代方案是用前导标点符号指定它们的名称,就像一个选项而不是子命令。例如,这可能看起来像 py /install ...
而不是 py install
,或者 py --list
。因为其中一些对于正常的 CPython 解释器目前是错误的,所以可以添加它们而无需担心向后兼容性。
然而,值得注意的是,Windows 典型的斜杠前缀格式在 CPython 中不是错误。因此,Windows 用户无法直接转移现有知识,必须学习一种新的指定选项的方法。由于我们正在提议一个 Windows 特定工具,这是一个糟糕的开始。此外,那些熟悉 Unix 风格命令行的人会认识到选项被错误地用作命令。
我们希望创建一个干净的界面,而以包含明显缺陷或学习挑战的设计开头与该目标背道而驰。现代工具普遍将子命令用于这些目的,因此使用不同的想法被拒绝。
转而改进当前的传统安装程序
与其创建一个新的安装机制,我们可以投入精力维护当前的安装程序。然而,现阶段,我们当前的安装程序完全基于已淘汰的技术。Windows 不再开发 Windows Installer 服务,Wix 也不再改进我们使用的工具集版本。迁移到更新的 Wix Toolset 需要大量工作,最终仍然使我们受限于旧技术。
如前所述,Windows Installer 提供的最有益的功能并未用于 CPython,并且通常造成的麻烦多于它解决的任何问题(例如,由于自动收集的文件版本信息而导致的意外降级)。
Burn 捆绑包(我们安装程序逻辑的主要来源)的实现是 C++ 编写的,并集成到一个少数核心开发人员熟悉框架中。这使得维护具有挑战性,并且不是一个好的长期立场。将注册无安装等所需功能迁移到 Burn 捆绑包中是不可能的(除非编写端到端重新实现并将其作为事后考虑集成)。
我们认为,维护当前的传统安装程序至少与实现新安装程序一样费力,并且不会为核心团队或我们的用户带来有意义的好处。因此,这个想法被拒绝。
完全删除应用商店包
删除应用商店软件包将减少用户在选择 Python 运行时时面临的选项数量。除了可靠性和安全性之外,传统安装程序作为替代品是完全足够的。将生态系统部分迁移到更安全的设置(例如不依赖 DLL 劫持)的工作已经基本完成,但仍有一些软件包仅在安全性较低的配置下工作,将所有用户都迁回这些配置将确保这些软件包的用户不会面临他们今天面临的问题。
然而,大多数商店软件包用户似乎没有抱怨。据传闻,他们通常对商店安装完全满意,尤其赞赏安装的便捷性和可靠性。(就个人而言,这位作者自 Python 3.8 以来一直专门使用商店软件包,没有遇到任何阻碍性问题。)
最主要的问题是由配置错误的 PATH
变量和 Microsoft 安装的默认 python.exe
重定向器引起的。换句话说,与我们自己的软件包完全无关(尽管有时与我们其他安装程序中无法解决的问题有关)。为了通过此路径成功安装的大量用户,我们认为诊断和协助受影响用户的负担是值得的,并认为简单地放弃商店软件包的想法被拒绝。
话虽如此,当 PyManager 发布到商店时,我们计划将商店中所有现有的运行时下架,以确保用户能找到管理器。这只影响新安装,任何以前安装过特定版本的人(即使在另一台机器上,如果他们已登录)将能够继续使用和安装这些版本。
依赖 WinGet 或类似工具
WinGet、Chocolatey 和其他类似工具并非我们所要求的安装程序。它们使用自己的元数据存储库来下载、验证和运行安装程序。如果没有我们自己的安装程序,它们就无法运行任何东西,因此无法使用。
它们的元数据可能不支持安装 PyManager,然后运行它来安装特定的运行时。如果出现这种情况,它们可能需要直接研究使用我们的二进制包。
目前,这些安装工具都没有得到 CPython 的官方支持,因此我们没有义务让它们工作。
将每个版本都做成 Windows 应用商店包
可以像我们目前一样将每个版本发布到 Windows 应用商店,但将其取消列出并依赖安装程序(可能是 PyManager、WinGet 或其他可以安装应用商店包的工具)。这将避免压倒用户的风险,同时大大简化我们自己的包管理职责。
这种方法将给任何有权访问商店发布界面的贡献者带来巨大的负担,因为更新软件包是手动操作。此外,它将使每个 Python 运行时都面临前面概述的技术限制。因此,这个想法被拒绝。
将每个版本都制作成 MSIX 包而不是 ZIP,即使这避免了商店发布界面,仍然会给用户带来技术限制。它也被拒绝。
只发布普通的 ZIP 文件
发布纯 ZIP 文件是计划的一部分,但它不会在可视列表中列出(例如,在 python.org 下载页面上,尽管它们将在 FTP 视图中可见)。另一种方法是发布和列出这些包,并期望用户下载并手动解压和配置它们。
鉴于我们看到的工作流程,我们相信大多数用户根本不想配置 Python 安装。他们不仅不想选择安装位置,也不想选择版本,甚至不想搜索下载提供商或说明。然而,他们确实希望能够稍后找到安装、启动、更新或删除它,或者列出所有已知安装。
还值得注意的是,ZIP 文件将比目前下载页面上列出的文件更多,因此文件列表将变得更长。选择正确的下载对于用户来说已经具有挑战性(那些绕过主要“下载”按钮并查看所有可用版本和文件列表的用户),我们不希望使其更具挑战性。
索引协议和下载列表将提供给希望使用它的工具,或者愿意通过 JSON 查找 URL 的用户。安装命令上的 --target
选项也提供了一个简单的下载和解压操作,允许用户获得与 ZIP 文件相同的体验。而 --download
选项可以为用户提供仍然打包的 ZIP 文件。
仅将 PyManager 发布到一个地方
无论是 Windows 应用商店还是 python.org,都可以只发布到一个位置。
然而,用户强烈期望能够从 python.org 下载一些东西。如果我们要完全取消任何选项,我们无疑会伤害我们的用户。如果没有 python.org 上可用的 MSIX,用户就无法将包传输到另一台机器,也无法完全编写管理器的初始安装脚本。
许多用户依赖 Windows 应用商店应用来安装软件包,而 Windows 中内置的重定向器只能打开到商店页面。因此,删除商店应用相当于每月拒绝数十万次安装。
这两个构建实际上是相同的。我们提供给商店的 MSIX 与提供给 python.org 的唯一区别是包签名:我们自己签署 python.org 包,而商店包作为发布过程的一部分进行签署。否则,生产和发布这两个包没有额外的成本。
内联脚本元数据
PEP 723 引入了内联脚本元数据,这是一种结构化注释,旨在供第三方工具解释,然后在正确的环境中启动 Python 脚本。摘自该 PEP 的一个示例
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
PyManager 没有集成支持安装依赖项,并且不建议添加任何支持。因此,我们无法完全实现此元数据的处理,并且我们认为部分处理比没有处理更糟糕,因此我们选择不实现任何处理。
用户可以直接将约束指定为选项,例如,py -V:>=3.11 my-script.py
以获得选择行为。
我们还可以检测元数据并警告所选运行时是否不符合其要求,但这不属于初始提案的一部分。
版权
本文档置于公共领域或 CC0-1.0-Universal 许可证下,以更宽松者为准。
来源:https://github.com/python/peps/blob/main/peps/pep-0773.rst