Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 514 – Python 在 Windows 注册表中的注册

作者:
Steve Dower <steve.dower at python.org>
BDFL-代表:
Paul Moore <p.f.moore at gmail.com>
状态:
活跃
类型:
信息性
创建:
2016 年 2 月 2 日
发布历史:
2016 年 2 月 2 日,2016 年 3 月 1 日,2016 年 7 月 18 日
决议:
Python-Dev 邮件

目录

摘要

本 PEP 定义了 Python 注册表项的模式,以允许第三方安装程序注册其安装,并允许工具和应用程序检测并正确显示用户机器上的所有 Python 环境。本 PEP 不建议对 Python 进行任何实现更改。

除非 Python 环境希望被外部工具自动发现,否则它们不需要注册。由于这仅与 Windows 相关,因此这些工具预计将主要是 GUI 应用程序。但是,控制台应用程序也可以使用注册的信息。本 PEP 涵盖了可能提供的信息,但实际的信息呈现和使用由工具设计者决定。

该模式与官方安装程序至少从 Python 2.5 版本开始使用的注册表值相匹配,并且解析行为与官方 Python 版本的行为相匹配。提供了一些向后兼容规则,以确保工具可以正确检测未注册完整信息的 CPython 版本。

动机

在 Windows 上安装时,官方 Python 安装程序会创建一个注册表项,以便其他应用程序发现和检测。这允许诸如安装程序或 IDE 之类的工具自动检测和显示用户的 Python 安装。例如,PEP 397 py.exe 启动器和 PyCharm 和 Visual Studio 等编辑器已经利用了此信息。

第三方安装程序(例如发行版使用的安装程序)通常会出于相同目的创建相同的键。大多数使用注册表来检测 Python 安装的工具仅检查官方安装程序使用的键。因此,希望被发现的第三方安装会覆盖这些值,这会导致用户“丢失”其原始 Python 安装。

通过描述用于注册表键的布局,该布局允许第三方安装以独特的方式注册自身,以及为工具开发人员提供有关发现所有可用 Python 安装的指南,可以防止这些冲突。我们还借此机会添加一些众所周知的元数据,以便向用户提供更多信息。

定义

“注册表项”等同于注册表的 文件系统路径。每个键都可以包含“子键”(嵌套在键中的键)和“值”(与键关联的命名和类型化的属性)。这些用于在 Windows 上存储设置,就像包含配置文件的目录一样。

HKEY_CURRENT_USER 是当前登录用户的设置根目录,该用户通常可以读取和写入此根目录下的所有设置。

HKEY_LOCAL_MACHINE 是所有用户的设置根目录。通常,任何用户都可以读取这些设置,但只有管理员可以修改它们。通常情况下,HKEY_CURRENT_USER 下的值优先于 HKEY_LOCAL_MACHINE 下的值。

在 64 位 Windows 上,HKEY_LOCAL_MACHINE\Software\Wow6432Node 是一个特殊键,32 位进程会透明地读取和写入它,而不是直接访问 Software 键。

有关 Windows 上注册表重定向的更多文档,请参阅 MSDN 库 [1]

结构

我们认为机器上有一组 Python 环境,每个机器用户的环境可能不同。根据每个环境的安装选项,有三个潜在的注册表位置可以存储这组环境。

HKEY_CURRENT_USER\Software\Python\<Company>\<Tag>
HKEY_LOCAL_MACHINE\Software\Python\<Company>\<Tag>
HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python\<Company>\<Tag>

官方 Python 版本使用 PythonCore 作为公司,使用 sys.winver 的值作为标签。公司 PyLauncher 是保留的。其他注册的环境可以使用任何公司和标签值。本文件后面将给出一些建议。

公司-标签对不区分大小写,并唯一标识每个环境。根据工具的目的和预期用途,有两种建议的解决公司-标签对之间冲突的方法。

列出所有已安装环境的工具可以选择包括这些环境,即使公司-标签对匹配也是如此。它们应确保用户可以轻松识别注册是针对每个用户还是针对每台机器,以及哪个注册具有更高的优先级。

旨在从所有注册的环境中选择单个已安装环境(基于公司-标签对)的工具,例如 py.exe 启动器,应始终选择在 HKEY_CURRENT_USER 中注册的环境,而不是在 HKEY_LOCAL_MACHINE 中注册的匹配环境。

HKEY_LOCAL_MACHINE\Software\PythonHKEY_LOCAL_MACHINE\Software\Wow6432Node\Python 之间的冲突仅会在 64 位和 32 位版本的解释器具有相同的标签时发生。在这种情况下,工具应选择最适合其使用的解释器。

如果工具能够从提供的信息(或缺少的信息)中确定它无法使用注册的环境,则没有义务将其呈现给用户。

除了向后兼容性部分中讨论的内容外,公司和标签值被认为对工具是透明的,不应该从文本中推断出关于解释器的任何信息。但是,某些工具可能会向用户显示公司和标签值,因此理想情况下标签应该能够帮助用户识别关联的环境。

除非 Python 环境希望被外部工具自动发现,否则它们不需要注册自身。

向后兼容性

Python 3.4 及更早版本在 sys.winver 中没有区分 32 位和 64 位版本。因此,在该方案下不可能并行安装 32 位和 64 位解释器,因为这会导致重复的标签。

为了确保向后兼容性,应用程序应将以下两个注册表项下的环境视为不同的环境,即使标签匹配也是如此

HKEY_LOCAL_MACHINE\Software\Python\PythonCore\<Tag>
HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python\PythonCore\<Tag>

HKEY_CURRENT_USER 下列出的环境可能被视为与以上两个键都不同,这可能导致使用相同标签发现三个环境。或者,工具可以确定每个用户的环境是 64 位还是 32 位,并使其优先于每个机器的环境,从而导致最多发现两个环境。

在 3.5 之前,不可能检测到 64 位和 32 位版本的 Python 的并行安装,即使它们是为当前用户安装的。Python 3.5 及更高版本始终为 64 位和 32 位版本使用不同的标签。

以下部分描述了可能注册的用户可见信息。对于 Python 3.5 及更早版本,这些信息不可用,但为 PythonCore 键指定了备用默认值。

在其他公司名称下注册的环境没有向后兼容性要求,必须使用不同的标签来支持并行安装。使用这些注册的工具不需要区分标签,除了优先考虑用户的设置以外。

公司

键的公司部分旨在将相关环境分组,并确保标签的命名空间恰当。键名应为字母数字,不包含空格,并且可能唯一。例如,商标名称(首选)、主机名或作为最后手段,UUID 都是合适的

HKEY_CURRENT_USER\Software\Python\ExampleCorp
HKEY_CURRENT_USER\Software\Python\www.example.com
HKEY_CURRENT_USER\Software\Python\6C465E66-5A8C-4942-9E6A-D29159480C60

公司名称 PyLauncher 是为 PEP 397 启动器 (py.exe) 保留的。它不遵循此约定,工具应该忽略它。

如果存在名为 DisplayName 的字符串值,则应使用它来向用户标识环境制造商/开发商/发行商。否则,应使用键的名称。(对于 PythonCore,默认显示名称为“Python Software Foundation”。)

如果存在名为 SupportUrl 的字符串值,则可以显示它或以其他方式使用它来将用户引导到与环境相关的网站。(对于 PythonCore,默认支持 URL 为“https://pythonlang.cn/”。)

完整的示例可能如下所示

HKEY_CURRENT_USER\Software\Python\ExampleCorp
    (Default) = (value not set)
    DisplayName = "Example Corp"
    SupportUrl = "http://www.example.com"

标签

键的标签部分旨在唯一标识单个公司提供的环境。键名应为字母数字,不包含空格,并且在所有安装中保持稳定。例如,Python 语言版本、UUID 或部分/完整哈希是合适的,而基于安装目录或机器当前状态的标签可能不合适。例如

HKEY_CURRENT_USER\Software\Python\ExampleCorp\examplepy
HKEY_CURRENT_USER\Software\Python\ExampleCorp\3.6
HKEY_CURRENT_USER\Software\Python\ExampleCorp\6C465E66

预计某些工具将要求用户在命令行中键入标签,并且公司可能是可选的,前提是标签在所有 Python 安装中是唯一的。建议使用简短、人类可读且易于键入的标签,并且如果可能,选择一个可能在所有其他公司中都是唯一的标签。

如果存在名为 DisplayName 的字符串值,则应使用它来向用户标识环境。否则,应使用键的名称。(对于 PythonCore,默认值为“Python ”加上标签。)

如果存在名为 SupportUrl 的字符串值,则可以显示它或以其他方式使用它来将用户引导到与环境相关的网站。(对于 PythonCore,默认值为“https://pythonlang.cn/”。)

如果存在名为 Version 的字符串值,则应使用它来标识环境的版本。这与环境实现的 Python 版本无关。(对于 PythonCore,默认值为标签的前三个字符。)

如果存在名为 SysVersion 的字符串值,则它必须采用 x.yx.y.z 格式,与解释器中 sys.version_info 返回的版本匹配。如果省略,则 Python 版本未知。(对于 PythonCore,默认值为标签的前三个字符。)

如果存在名为 SysArchitecture 的字符串值,则它必须与 platform.architecture() 返回的元组的第一个元素匹配。通常,这将是“32bit”或“64bit”。如果省略,则体系结构未知。(对于 PythonCore,当在 HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python *或* 32 位操作系统上的任何位置注册时,体系结构为“32bit”,当在 64 位机器上的 HKEY_LOCAL_MACHINE\Software\Python 下注册时,体系结构为“64bit”,当在 HKEY_CURRENT_USER 下注册时,体系结构未知。)

请注意,这些值中的每一个都是推荐的,但不是必需的。省略 SysVersionSysArchitecture 可能会阻止某些工具正确支持环境。一个完整的示例可能如下所示

HKEY_CURRENT_USER\Software\Python\ExampleCorp\examplepy
    (Default) = (value not set)
    DisplayName = "Example Py Distro 3"
    SupportUrl = "http://www.example.com/distro-3"
    Version = "3.0.12345.0"
    SysVersion = "3.6.0"
    SysArchitecture = "64bit"

安装路径

在环境键下方,必须创建 InstallPath 键。此键始终命名为 InstallPath,默认值必须与 sys.prefix 匹配。

HKEY_CURRENT_USER\Software\Python\ExampleCorp\3.6\InstallPath
    (Default) = "C:\ExampleCorpPy36"

如果存在名为 ExecutablePath 的字符串值,则它必须是 python.exe(或等效文件)可执行文件的完整路径。如果省略,则环境不可执行。(对于 PythonCore,默认值为 (Default) 值引用的目录中的 python.exe 文件。)

如果存在名为 ExecutableArguments 的字符串值,则工具应将该值用作执行 ExecutablePath 时的第一个参数。工具可以在这些参数之后添加其他参数,并将合理地预期标准 Python 命令行选项可用。

如果存在名为 WindowedExecutablePath 的字符串值,则它必须是 pythonw.exe(或等效文件)可执行文件的路径。如果省略,则默认值为 ExecutablePath 的值,如果省略,则环境不可执行。(对于 PythonCore,默认值为 (Default) 值引用的目录中的 pythonw.exe 文件。)

如果存在名为 WindowedExecutableArguments 的字符串值,则工具应将该值用作执行 WindowedExecutablePath 时的第一个参数。工具可以在这些参数之后添加其他参数,并将合理地预期标准 Python 命令行选项可用。

完整的示例可能如下所示

HKEY_CURRENT_USER\Software\Python\ExampleCorp\examplepy\InstallPath
    (Default) = "C:\ExampleDistro30"
    ExecutablePath = "C:\ExampleDistro30\ex_python.exe"
    ExecutableArguments = "--arg1"
    WindowedExecutablePath = "C:\ExampleDistro30\ex_pythonw.exe"
    WindowedExecutableArguments = "--arg1"

帮助

在环境键下方,可以创建一个 Help 键。如果存在,此键始终命名为 Help,并且没有默认值。

每个 Help 子键都指定与环境关联的文档文件、工具或 URL。子键可以具有任何名称,默认值为适合传递给 os.startfile 或等效函数的字符串。

如果存在名为 DisplayName 的字符串值,则应使用它来标识帮助文件供用户使用。否则,应使用键名。

完整的示例可能如下所示

HKEY_CURRENT_USER\Software\Python\ExampleCorp\6C465E66\Help
    Python\
        (Default) = "C:\ExampleDistro30\python36.chm"
        DisplayName = "Python Documentation"
    Extras\
        (Default) = "http://www.example.com/tutorial"
        DisplayName = "Example Distro Online Tutorial"

其他键

公司-标签对下的所有其他子键可供私用。

传统的官方 CPython 版本在该空间中使用某些键来确定 Python 标准库和其他已安装模块的位置。此行为主要保留以实现向后兼容性。但是,由于读取这些值的代码嵌入到解释器中,因此如果使用未修改的解释器,第三方发行版可能会受到写入 PythonCore 的值的影响。

示例代码

此示例代码枚举注册表并显示可用于启动环境的公司-标签对以及目标可执行文件。它只显示标签的最优目标。对 PythonCore 的向后兼容处理被省略,但在后面的示例中展示。

# Display most-preferred environments.
# Assumes a 64-bit operating system
# Does not correctly handle PythonCore compatibility

import winreg

def enum_keys(key):
    i = 0
    while True:
        try:
            yield winreg.EnumKey(key, i)
        except OSError:
            break
        i += 1

def get_value(key, value_name):
    try:
        return winreg.QueryValue(key, value_name)
    except FileNotFoundError:
        return None

seen = set()
for hive, key, flags in [
    (winreg.HKEY_CURRENT_USER, r'Software\Python', 0),
    (winreg.HKEY_LOCAL_MACHINE, r'Software\Python', winreg.KEY_WOW64_64KEY),
    (winreg.HKEY_LOCAL_MACHINE, r'Software\Python', winreg.KEY_WOW64_32KEY),
]:
    with winreg.OpenKeyEx(hive, key, access=winreg.KEY_READ | flags) as root_key:
        for company in enum_keys(root_key):
            if company == 'PyLauncher':
                continue

            with winreg.OpenKey(root_key, company) as company_key:
                for tag in enum_keys(company_key):
                    if (company, tag) in seen:
                        if company == 'PythonCore':
                            # TODO: Backwards compatibility handling
                            pass
                        continue
                    seen.add((company, tag))

                    try:
                        with winreg.OpenKey(company_key, tag + r'\InstallPath') as ip_key:
                            exec_path = get_value(ip_key, 'ExecutablePath')
                            exec_args = get_value(ip_key, 'ExecutableArguments')
                            if company == 'PythonCore' and not exec_path:
                                # TODO: Backwards compatibility handling
                                pass
                    except OSError:
                        exec_path, exec_args = None, None

                    if exec_path:
                        print('{}\\{} - {} {}'.format(company, tag, exec_path, exec_args or ''))
                    else:
                        print('{}\\{} - (not executable)'.format(company, tag))

此示例仅扫描当前用户的 PythonCore 条目。如果数据丢失,将使用前面 PEP 中描述的默认值。请注意,这些默认值仅供 PythonCore 使用;其他注册没有默认值。

# Only lists per-user PythonCore registrations
# Uses fallback values as described in PEP 514

import os
import winreg

def enum_keys(key):
    i = 0
    while True:
        try:
            yield winreg.EnumKey(key, i)
        except OSError:
            break
        i += 1

def get_value(key, value_name):
    try:
        return winreg.QueryValue(key, value_name)
    except FileNotFoundError:
        return None

with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Python\PythonCore") as company_key:
    print('Company:', get_value(company_key, 'DisplayName') or 'Python Software Foundation')
    print('Support:', get_value(company_key, 'SupportUrl') or 'https://pythonlang.cn/')
    print()

    for tag in enum_keys(company_key):
        with winreg.OpenKey(company_key, tag) as tag_key:
            print('PythonCore\\' + tag)
            print('Name:', get_value(tag_key, 'DisplayName') or ('Python ' + tag))
            print('Support:', get_value(tag_key, 'SupportUrl') or 'https://pythonlang.cn/')
            print('Version:', get_value(tag_key, 'Version') or tag[:3])
            print('SysVersion:', get_value(tag_key, 'SysVersion') or tag[:3])
            # Architecture is unknown because we are in HKCU
            # Tools may use alternate approaches to determine architecture when
            # the registration does not specify it.
            print('SysArchitecture:', get_value(tag_key, 'SysArchitecture') or '(unknown)')

        try:
            ip_key = winreg.OpenKey(company_key, tag + '\\InstallPath')
        except FileNotFoundError:
            pass
        else:
            with ip_key:
                ip = get_value(ip_key, None)
                exe = get_value(ip_key, 'ExecutablePath') or os.path.join(ip, 'python.exe')
                exew = get_value(ip_key, 'WindowedExecutablePath') or os.path.join(ip, 'python.exe')
                print('InstallPath:', ip)
                print('ExecutablePath:', exe)
                print('WindowedExecutablePath:', exew)
        print()

此示例显示了 64 位 Python 3.6.0 的仅限于我的安装将创建的注册表的子集。还可能创建其他键。

HKEY_CURRENT_USER\Software\Python\PythonCore
    (Default) = (value not set)
    DisplayName = "Python Software Foundation"
    SupportUrl = "https://pythonlang.cn/"

HKEY_CURRENT_USER\Software\Python\PythonCore\3.6
    (Default) = (value not set)
    DisplayName = "Python 3.6 (64-bit)"
    SupportUrl = "https://pythonlang.cn/"
    Version = "3.6.0"
    SysVersion = "3.6"
    SysArchitecture = "64bit"

HKEY_CURRENT_USER\Software\Python\PythonCore\3.6\Help\Main Python Documentation
    (Default) = "C:\Users\Me\AppData\Local\Programs\Python\Python36\Doc\python360.chm"
    DisplayName = "Python 3.6.0 Documentation"

HKEY_CURRENT_USER\Software\Python\PythonCore\3.6\InstallPath
    (Default) = "C:\Users\Me\AppData\Local\Programs\Python\Python36\"
    ExecutablePath = "C:\Users\Me\AppData\Local\Programs\Python\Python36\python.exe"
    WindowedExecutablePath = "C:\Users\Me\AppData\Local\Programs\Python\Python36\pythonw.exe"

参考资料


来源: https://github.com/python/peps/blob/main/peps/pep-0514.rst

最后修改时间: 2023-09-09 17:39:29 GMT