PEP-514: Integrates feedback from python-dev. (#59)
* PEP-514: Integrates feedback from python-dev. * PEP-514: Clarified motivation and improved a few examples. * Clarifies some text and PythonCore defaults. Adds ExecutableArguments and WindowedExecutableArguments for fixed launch arguments. Adds first (of multiple) examples * Adds extra examples. * Minor clarification for resolving architecture for PythonCore entries.
This commit is contained in:
parent
fef91bba87
commit
c9f126a62e
337
pep-0514.txt
337
pep-0514.txt
|
@ -13,16 +13,22 @@ Abstract
|
|||
========
|
||||
|
||||
This PEP defines a schema for the Python registry key to allow third-party
|
||||
installers to register their installation, and to allow applications to detect
|
||||
and correctly display all Python environments on a user's machine. No
|
||||
installers to register their installation, and to allow tools and applications
|
||||
to detect and correctly display all Python environments on a user's machine. No
|
||||
implementation changes to Python are proposed with this PEP.
|
||||
|
||||
Python environments are not required to be registered unless they want to be
|
||||
automatically discoverable by external tools.
|
||||
automatically discoverable by external tools. As this relates to Windows only,
|
||||
these tools are expected to be predominantly GUI applications. However, console
|
||||
applications may also make use of the registered information. This PEP covers
|
||||
the information that may be made available, but the actual presentation and use
|
||||
of this information is left to the tool designers.
|
||||
|
||||
The schema matches the registry values that have been used by the official
|
||||
installer since at least Python 2.5, and the resolution behaviour matches the
|
||||
behaviour of the official Python releases.
|
||||
behaviour of the official Python releases. Some backwards compatibility rules
|
||||
are provided to ensure tools can correctly detect versions of CPython that do
|
||||
not register full information.
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
@ -30,25 +36,28 @@ Motivation
|
|||
When installed on Windows, the official Python installer creates a registry key
|
||||
for discovery and detection by other applications. This allows tools such as
|
||||
installers or IDEs to automatically detect and display a user's Python
|
||||
installations.
|
||||
installations. For example, the PEP 397 ``py.exe`` launcher and editors such as
|
||||
PyCharm and Visual Studio already make use of this information.
|
||||
|
||||
Third-party installers, such as those used by distributions, typically create
|
||||
identical keys for the same purpose. Most tools that use the registry to detect
|
||||
Python installations only inspect the keys used by the official installer. As a
|
||||
result, third-party installations that wish to be discoverable will overwrite
|
||||
these values, resulting in users "losing" their Python installation.
|
||||
these values, often causing users to "lose" their original Python installation.
|
||||
|
||||
By describing a layout for registry keys that allows third-party installations
|
||||
to register themselves uniquely, as well as providing tool developers guidance
|
||||
for discovering all available Python installations, these collisions should be
|
||||
prevented.
|
||||
prevented. We also take the opportunity to add some well-known metadata so that
|
||||
more information can be presented to users.
|
||||
|
||||
Definitions
|
||||
===========
|
||||
|
||||
A "registry key" is the equivalent of a file-system path into the registry. Each
|
||||
key may contain "subkeys" (keys nested within keys) and "values" (named and
|
||||
typed attributes attached to a key).
|
||||
typed attributes attached to a key). These are used on Windows to store settings
|
||||
in much the same way that directories containing configuration files would work.
|
||||
|
||||
``HKEY_CURRENT_USER`` is the root of settings for the currently logged-in user,
|
||||
and this user can generally read and write all settings under this root.
|
||||
|
@ -62,6 +71,9 @@ On 64-bit Windows, ``HKEY_LOCAL_MACHINE\Software\Wow6432Node`` is a special key
|
|||
that 32-bit processes transparently read and write to rather than accessing the
|
||||
``Software`` key directly.
|
||||
|
||||
Further documentation regarding registry redirection on Windows is available
|
||||
from the MSDN Library [1]_.
|
||||
|
||||
Structure
|
||||
=========
|
||||
|
||||
|
@ -74,21 +86,39 @@ the installation options of each environment::
|
|||
HKEY_LOCAL_MACHINE\Software\Python\<Company>\<Tag>
|
||||
HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python\<Company>\<Tag>
|
||||
|
||||
Environments are uniquely identified by their Company-Tag pair, with two options
|
||||
for conflict resolution: include everything, or give priority to user
|
||||
preferences.
|
||||
|
||||
Tools that include every installed environment, even where the Company-Tag pairs
|
||||
match, should ensure users can easily identify whether the registration was
|
||||
per-user or per-machine.
|
||||
|
||||
When tools are selecting a single installed environment from all registered
|
||||
environments, the intent is that user preferences from ``HKEY_CURRENT_USER``
|
||||
will override matching Company-Tag pairs in ``HKEY_LOCAL_MACHINE``.
|
||||
|
||||
Official Python releases use ``PythonCore`` for Company, and the value of
|
||||
``sys.winver`` for Tag. Other registered environments may use any values for
|
||||
Company and Tag. Recommendations are made in the following sections.
|
||||
``sys.winver`` for Tag. The Company ``PyLauncher`` is reserved. Other registered
|
||||
environments may use any values for Company and Tag. Recommendations are made
|
||||
later in this document.
|
||||
|
||||
Company-Tag pairs are case-insensitive, and uniquely identify each environment.
|
||||
Depending on the purpose and intended use of a tool, there are two suggested
|
||||
approaches for resolving conflicts between Company-Tag pairs.
|
||||
|
||||
Tools that list every installed environment may choose to include those
|
||||
even where the Company-Tag pairs match. They should ensure users can easily
|
||||
identify whether the registration was per-user or per-machine, and which
|
||||
registration has the higher priority.
|
||||
|
||||
Tools that aim to select a single installed environment from all registered
|
||||
environments based on the Company-Tag pair, such as the ``py.exe`` launcher,
|
||||
should always select the environment registered in ``HKEY_CURRENT_USER`` when
|
||||
than the matching one in ``HKEY_LOCAL_MACHINE``.
|
||||
|
||||
Conflicts between ``HKEY_LOCAL_MACHINE\Software\Python`` and
|
||||
``HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python`` should only occur when both
|
||||
64-bit and 32-bit versions of an interpreter have the same Tag. In this case,
|
||||
the tool should select whichever is more appropriate for its use.
|
||||
|
||||
If a tool is able to determine from the provided information (or lack thereof)
|
||||
that it cannot use a registered environment, there is no obligation to present
|
||||
it to users.
|
||||
|
||||
Except as discussed in the section on backwards compatibility, Company and Tag
|
||||
values are considered opaque to tools, and no information about the interpreter
|
||||
should be inferred from the text. However, some tools may display the Company
|
||||
and Tag values to users, so ideally the Tag will be able to help users identify
|
||||
the associated environment.
|
||||
|
||||
Python environments are not required to register themselves unless they want to
|
||||
be automatically discoverable by external tools.
|
||||
|
@ -118,9 +148,14 @@ It is not possible to detect side-by-side installations of both 64-bit and
|
|||
current user. Python 3.5 and later always uses different Tags for 64-bit and
|
||||
32-bit versions.
|
||||
|
||||
Environments registered under other Company names must use distinct Tags to
|
||||
support side-by-side installations. Tools consuming these registrations are
|
||||
not required to disambiguate tags other than by preferring the user's setting.
|
||||
The following section describe user-visible information that may be registered.
|
||||
For Python 3.5 and earlier, none of this information is available, but
|
||||
alternative defaults are specified for the ``PythonCore`` key.
|
||||
|
||||
Environments registered under other Company names have no backward compatibility
|
||||
requirements and must use distinct Tags to support side-by-side installations.
|
||||
Tools consuming these registrations are not required to disambiguate tags other
|
||||
than by preferring the user's setting.
|
||||
|
||||
Company
|
||||
-------
|
||||
|
@ -128,21 +163,23 @@ Company
|
|||
The Company part of the key is intended to group related environments and to
|
||||
ensure that Tags are namespaced appropriately. The key name should be
|
||||
alphanumeric without spaces and likely to be unique. For example, a trademarked
|
||||
name, a UUID, or a hostname would be appropriate::
|
||||
name (preferred), a hostname, or as a last resort, a UUID would be appropriate::
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp
|
||||
HKEY_CURRENT_USER\Software\Python\6C465E66-5A8C-4942-9E6A-D29159480C60
|
||||
HKEY_CURRENT_USER\Software\Python\www.example.com
|
||||
HKEY_CURRENT_USER\Software\Python\6C465E66-5A8C-4942-9E6A-D29159480C60
|
||||
|
||||
The company name ``PyLauncher`` is reserved for the PEP 397 launcher
|
||||
(``py.exe``). It does not follow this convention and should be ignored by tools.
|
||||
|
||||
If a string value named ``DisplayName`` exists, it should be used to identify
|
||||
the environment category to users. Otherwise, the name of the key should be
|
||||
used.
|
||||
the environment manufacturer/developer/destributor to users. Otherwise, the name
|
||||
of the key should be used. (For ``PythonCore``, the default display name is
|
||||
"Python Software Foundation".)
|
||||
|
||||
If a string value named ``SupportUrl`` exists, it may be displayed or otherwise
|
||||
used to direct users to a web site related to the environment.
|
||||
used to direct users to a web site related to the environment. (For
|
||||
``PythonCore``, the default support URL is "http://www.python.org/".)
|
||||
|
||||
A complete example may look like::
|
||||
|
||||
|
@ -157,36 +194,58 @@ Tag
|
|||
The Tag part of the key is intended to uniquely identify an environment within
|
||||
those provided by a single company. The key name should be alphanumeric without
|
||||
spaces and stable across installations. For example, the Python language
|
||||
version, a UUID or a partial/complete hash would be appropriate; an integer
|
||||
counter that increases for each new environment may not::
|
||||
version, a UUID or a partial/complete hash would be appropriate, while a Tag
|
||||
based on the install directory or some aspect of the current machine may not.
|
||||
For example::
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\examplepy
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\3.6
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\6C465E66
|
||||
|
||||
It is expected that some tools will require users to type the Tag into a command
|
||||
line, and that the Company may be optional provided the Tag is unique across all
|
||||
Python installations. Short, human-readable and easy to type Tags are
|
||||
recommended, and if possible, select a value likely to be unique across all
|
||||
other Companies.
|
||||
|
||||
If a string value named ``DisplayName`` exists, it should be used to identify
|
||||
the environment to users. Otherwise, the name of the key should be used.
|
||||
the environment to users. Otherwise, the name of the key should be used. (For
|
||||
``PythonCore``, the default is "Python " followed by the Tag.)
|
||||
|
||||
If a string value named ``SupportUrl`` exists, it may be displayed or otherwise
|
||||
used to direct users to a web site related to the environment.
|
||||
used to direct users to a web site related to the environment. (For
|
||||
``PythonCore``, the default is "http://www.python.org/".)
|
||||
|
||||
If a string value named ``Version`` exists, it should be used to identify the
|
||||
version of the environment. This is independent from the version of Python
|
||||
implemented by the environment.
|
||||
implemented by the environment. (For ``PythonCore``, the default is the first
|
||||
three characters of the Tag.)
|
||||
|
||||
If a string value named ``SysVersion`` exists, it must be in ``x.y`` or
|
||||
``x.y.z`` format matching the version returned by ``sys.version_info`` in the
|
||||
interpreter. Otherwise, if the Tag matches this format it is used. If not, the
|
||||
Python version is unknown.
|
||||
interpreter. If omitted, the Python version is unknown. (For ``PythonCore``,
|
||||
the default is the first three characters of the Tag.)
|
||||
|
||||
Note that each of these values is recommended, but optional. A complete example
|
||||
may look like this::
|
||||
If a string value named ``SysArchitecture`` exists, it must match the first
|
||||
element of the tuple returned by ``platform.architecture()``. Typically, this
|
||||
will be "32bit" or "64bit". If omitted, the architecture is unknown. (For
|
||||
``PythonCore``, the architecture is "32bit" when registered under
|
||||
``HKEY_LOCAL_MACHINE\Software\Wow6432Node\Python`` *or* anywhere on a 32-bit
|
||||
operating system, "64bit" when registered under
|
||||
``HKEY_LOCAL_MACHINE\Software\Python`` on a 64-bit machine, and unknown when
|
||||
registered under ``HKEY_CURRENT_USER``.)
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\6C465E66
|
||||
Note that each of these values is recommended, but optional. Omitting
|
||||
``SysVersion`` or ``SysArchitecture`` may prevent some tools from correctly
|
||||
supporting the environment. A complete example may look like this::
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\examplepy
|
||||
(Default) = (value not set)
|
||||
DisplayName = "Distro 3"
|
||||
DisplayName = "Example Py Distro 3"
|
||||
SupportUrl = "http://www.example.com/distro-3"
|
||||
Version = "3.0.12345.0"
|
||||
SysVersion = "3.6.0"
|
||||
SysArchitecture = "64bit"
|
||||
|
||||
InstallPath
|
||||
-----------
|
||||
|
@ -197,22 +256,35 @@ always named ``InstallPath``, and the default value must match ``sys.prefix``::
|
|||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\3.6\InstallPath
|
||||
(Default) = "C:\ExampleCorpPy36"
|
||||
|
||||
If a string value named ``ExecutablePath`` exists, it must be a path to the
|
||||
``python.exe`` (or equivalent) executable. Otherwise, the interpreter executable
|
||||
is assumed to be called ``python.exe`` and exist in the directory referenced by
|
||||
the default value.
|
||||
If a string value named ``ExecutablePath`` exists, it must be the full path to
|
||||
the ``python.exe`` (or equivalent) executable. If omitted, the environment is
|
||||
not executable. (For ``PythonCore``, the default is the ``python.exe`` file in
|
||||
the directory referenced by the ``(Default)`` value.)
|
||||
|
||||
If a string value named ``ExecutableArguments`` exists, tools should use the
|
||||
value as the first arguments when executing ``ExecutablePath``. Tools may add
|
||||
other arguments following these, and will reasonably expect standard Python
|
||||
command line options to be available.
|
||||
|
||||
If a string value named ``WindowedExecutablePath`` exists, it must be a path to
|
||||
the ``pythonw.exe`` (or equivalent) executable. Otherwise, the windowed
|
||||
interpreter executable is assumed to be called ``pythonw.exe`` and exist in the
|
||||
directory referenced by the default value.
|
||||
the ``pythonw.exe`` (or equivalent) executable. If omitted, the default is the
|
||||
value of ``ExecutablePath``, and if that is omitted the environment is not
|
||||
executable. (For ``PythonCore``, the default is the ``pythonw.exe`` file in the
|
||||
directory referenced by the ``(Default)`` value.)
|
||||
|
||||
If a string value named ``WindowedExecutableArguments`` exists, tools should use
|
||||
the value as the first arguments when executing ``WindowedExecutablePath``.
|
||||
Tools may add other arguments following these, and will reasonably expect
|
||||
standard Python command line options to be available.
|
||||
|
||||
A complete example may look like::
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\ExampleCorp\6C465E66\InstallPath
|
||||
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
|
||||
----
|
||||
|
@ -240,13 +312,166 @@ A complete example may look like::
|
|||
Other Keys
|
||||
----------
|
||||
|
||||
Some other registry keys are used for defining or inferring search paths under
|
||||
certain conditions. A third-party installation is permitted to define these keys
|
||||
under their Company-Tag key, however, the interpreter must be modified and
|
||||
rebuilt in order to read these values. Alternatively, the interpreter may be
|
||||
modified to not use any registry keys for determining search paths. Making such
|
||||
changes is a decision for the third party; this PEP makes no recommendation
|
||||
either way.
|
||||
All other subkeys under a Company-Tag pair are available for private use.
|
||||
|
||||
Official CPython releases have traditionally used certain keys in this space to
|
||||
determine the location of the Python standard library and other installed
|
||||
modules. This behaviour is retained primarily for backward compatibility.
|
||||
However, as the code that reads these values is embedded into the interpreter,
|
||||
third-party distributions may be affected by values written into ``PythonCore``
|
||||
if using an unmodified interpreter.
|
||||
|
||||
Sample Code
|
||||
===========
|
||||
|
||||
This sample code enumerates the registry and displays the available Company-Tag
|
||||
pairs that could be used to launch an environment and the target executable. It
|
||||
only shows the most-preferred target for the tag. Backwards-compatible handling
|
||||
of ``PythonCore`` is omitted but shown in a later example::
|
||||
|
||||
# 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 comany 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))
|
||||
|
||||
This example only scans ``PythonCore`` entries for the current user. Where data
|
||||
is missing, the defaults as described earlier in the PEP are substituted. Note
|
||||
that these defaults are only for use under ``PythonCore``; other registrations
|
||||
do not have any default values::
|
||||
|
||||
# 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 'http://www.python.org/')
|
||||
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 'http://www.python.org/')
|
||||
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()
|
||||
|
||||
This example shows a subset of the registration that will be created by a
|
||||
just-for-me install of 64-bit Python 3.6.0. Other keys may also be created::
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\PythonCore
|
||||
(Default) = (value not set)
|
||||
DisplayName = "Python Software Foundation"
|
||||
SupportUrl = "http://www.python.org/"
|
||||
|
||||
HKEY_CURRENT_USER\Software\Python\PythonCore\3.6
|
||||
(Default) = (value not set)
|
||||
DisplayName = "Python 3.6 (64-bit)"
|
||||
SupportUrl = "http://www.python.org/"
|
||||
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"
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] Registry Redirector (Windows)
|
||||
(https://msdn.microsoft.com/en-us/library/windows/desktop/aa384232.aspx)
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue