First draft of entry points for metadata 2.0

This commit is contained in:
Nick Coghlan 2013-08-03 01:13:56 +10:00
parent fa09420ca3
commit 311a487701
2 changed files with 305 additions and 16 deletions

View File

@ -86,6 +86,24 @@ identification scheme.
"rationale" section at the end of the document, as it would otherwise be
an irrelevant distraction for future readers.
A Note on Time Frames
=====================
There's a lot of work going on in the Python packaging space at the moment.
In the near term (up until the release of Python 3.4), those efforts will be
focused on the existing metadata standards, both those defined in Python
Enhancement Proposals, and the de facto standards defined by the setuptools
project.
This PEP is about setting out a longer term goal for the ecosystem that
captures those existing capabilities in a format that is easier to work
with. There are still a number of key open questions (mostly related to
source based distribution), and those won't be able to receive proper
attention from the development community until the other near term
concerns have been resolved.
Purpose
=======
@ -223,12 +241,16 @@ or consumes distribution version and dependency metadata.
along with the supporting metadata file formats defined by the
``setuptools`` project.
"Entry points" are a scheme for identifying Python callables or other
objects as strings consisting of a Python module name and a module
attribute name, separated by a colon. For example: ``"test.regrtest:main"``.
"Distro" is used as the preferred term for Linux distributions, to help
avoid confusion with the Python-specific meaning of the term "distribution".
"Distros" is used as the preferred term for Linux distributions, to help
avoid confusion with the Python-specific meaning of the term.
"Dist" is the preferred abbreviation for "distributions" in the sense defined
in this PEP.
"Qualified name" comes from PEP 3155, and refers to the dotted name of an
object relative to its containing module. This is useful for referring
to method definitions on classes, as well as any other attributes of
top level module objects.
Integration and deployment of distributions
@ -255,7 +277,10 @@ Integration and deployment can in turn be broken down into further substeps.
These three steps may all occur directly on the target system. Alternatively
the build step may be separated out by using binary archives provided by the
publisher of the distribution, or by creating the binary archives on a
separate system prior to deployment.
separate system prior to deployment. The advantage of the latter approach
is that it minimizes the dependencies that need to be installed on
deployment targets (as the build dependencies will be needed only on the
build systems).
The published metadata for distributions SHOULD allow integrators, with the
aid of build and integration tools, to:
@ -299,6 +324,25 @@ and publishers, with the aid of build and publication tools, to:
Standard build system
---------------------
.. note::
The standard build system currently described in the PEP is a draft based
on existing practices for projects using distutils or setuptools as their
build system (or other projects, like ``d2to1``, that expose a setup.py
file for backwards compatibility with existing tools)
The specification doesn't currently cover expected argument support for
the commands, which is a limitation that needs to be addressed before the
PEP can be considered ready for acceptance.
It is also possible that the "meta build system" will be separated out
into a distinct PEP in the coming months (similar to the separation of
the versioning and requirement specification standard out to PEP 440).
If a `suitable API can be worked out <Metabuild system>`__, then it may
even be possible to switch to a more declarative API for build system
specification.
Both development and integration of distributions relies on the ability to
build extension modules and perform other operations in a distribution
independent manner.
@ -318,10 +362,6 @@ development and integration activities:
* ``python setup.py bdist_wheel``: create a binary archive from an sdist,
source archive or VCS checkout
Future iterations of the metadata and associated PEPs may aim to replace
these ``distutils``/``setuptools`` dependent commands with build system
independent entry points.
Metadata format
===============
@ -436,6 +476,48 @@ When serialised to a file, the name used for this metadata set SHOULD
be ``pydist-dependencies.json``.
Export metadata
---------------
Distributions may define components that are intended for use by other
distributions (such as plugins). As it can be beneficial to know whether or
not a distribution defines any such exports without needing to parse any
metadata, a suitable subset is defined for serialisation to a separate file
in the ``dist-info`` metadata directory.
The external command metadata consists of the following fields:
* ``metadata_version``
* ``generator``
* ``name``
* ``version``
* ``exports``
When serialised to a file, the name used for this metadata set SHOULD
be ``pydist-exports.json``.
Command metadata
----------------
Distributions may define commands that will be available from the command
line following installation. As it can be beneficial to know whether or not
a distribution has such commands without needing to parse any metadata,
a suitable subset is defined for serialisation to a separate file in the
``dist-info`` metadata directory.
The external command metadata consists of the following fields:
* ``metadata_version``
* ``generator``
* ``name``
* ``version``
* ``commands``
When serialised to a file, the name used for this metadata set SHOULD
be ``pydist-commands.json``.
Included documents
------------------
@ -508,7 +590,7 @@ if any. A manually produced file would omit this field.
Example::
"generator": "setuptools (0.8)"
"generator": "setuptools (0.9)"
Name
@ -1348,6 +1430,141 @@ Example where the supported Python version varies by platform::
"supports_environments": ["python_version >= '2.6' and sys_platform != 'win32'",
"python_version >= '3.3' and sys_platform == 'win32'"]
Installed interfaces
====================
Most Python distributions expose packages and modules for import through
the Python module namespace. Distributions may also expose other
interfaces when installed.
Export specifiers
-----------------
An export specifier is a string using one of the following formats::
module
module:name
module[requires_extra]
module:name[requires_extra]
The meaning of the subfields is as follows:
* ``module``: the module providing the export
* ``name``: if applicable, the qualified name of the export within the module
* ``requires_extra``: indicates the export will only work correctly if the
additional dependencies named in the given extra are available.
Note that installation of extras is not tracked directly: they are merely
a convenient way to refer to a set of dependencies that will be checked for
at runtime.
.. note::
I tried this as a mapping with subfields, and it made the examples below
unreadable. While this PEP is mostly for tool use, readability still
matters to some degree for debugging purposes, and because I expect
snippets of the format to be reused elsewhere.
Modules
-------
A list of module names that the distribution provides for import.
For names that contain dots, the portion of the name before the final dot
MUST appear either in the installed module list or in the namespace package
list.
Note that attempting to import some declared modules may result in an
exception if the appropriate extras are not installed.
Example::
"modules": ["chair", "chair.cushions", "python_sketches.nobody_expects"]
.. note::
Making this a list of export specifiers instead would allow a distribution
to declare when a particular module requires a particular extra in order
to run correctly. On the other hand, there's an argument to be made that
that is the point where it starts to become worthwhile to split out a
separate distribution rather than using extras.
Namespaces
----------
A list of namespace packages that the distribution contributes modules to.
On versions of Python prior to Python 3.3 (which provides native namespace
package support), installation tools SHOULD emit a suitable ``__init__.py``
file to properly initialise the namespace rather than using a distribution
provided file.
Installation tools SHOULD emit a warning and MAY emit an error if a
distribution declares a namespace package that conflicts the name of an
already installed module or vice-versa.
Example::
"namespaces": ["python_sketches"]
Commands
--------
The ``commands`` mapping contains three subfields:
* ``wrap_console``: console wrapper scripts to be generated by the installer
* ``wrap_gui``: GUI wrapper scripts to be generated by the installer
* ``prebuilt``: scripts created by the distribution's build process and
installed directly to the configured scripts directory
``wrap_console`` and ``wrap_gui`` are both mappings of relatively arbitrary
script names to export specifiers. The script names must follow the rules
for distribution names. The export specifiers must refer to
either a package with a __main__ submodule (if no ``name`` subfield is
given in the export specifier) or else to a callable inside the named
module.
Installation tools should generate appropriate wrappers as part of the
installation process.
.. note::
Still needs more detail on what "appropriate wrapper" means.
``prebuilt`` is a list of script paths, relative to the scripts directory in
a wheel file or following installation. They are provided for informational
purpose only - installing them is handled through the normal processes for
files created when building a distribution.
Example::
"commands": {
"wrap_console": [{"wrapwithpython": "chair.run_cli"}],
"wrap_gui": [{"wrapwithpythonw": "chair:run_gui"}],
"prebuilt": ["notawrapper"]
}
Exports
-------
The ``exports`` mapping contains relatively arbitrary subfields, each
defining an export group. Each export group is then a mapping of relatively
arbitrary subfields to export specifiers.
Both export group names and export names must follow the rules for
distribution identifiers. It is suggested that export groups be named
after distributions to help avoid name conflicts.
The meaning of exports within an export group is up to those defining the
export group. One common use case is to advertise plugins for use by other
software.
Install hooks
=============

View File

@ -136,6 +136,32 @@
"$ref": "#/definitions/provides_declaration"
}
},
"modules": {
"description": "A list of modules and/or packages available for import after installing this distribution.",
"type": "array",
"items": {
"type": "string",
"$ref": "#/definitions/dotted_name"
}
},
"namespaces": {
"description": "A list of namespace packages this distribution contributes to",
"type": "array",
"items": {
"type": "string",
"$ref": "#/definitions/dotted_name"
}
},
"commands": {
"description": "Command line interfaces provided by this distribution",
"type": "object",
"$ref": "#/definitions/commands"
},
"exports": {
"description": "Other exported interfaces provided by this distribution",
"type": "object",
"$ref": "#/definitions/exports"
},
"obsoleted_by": {
"description": "A string that indicates that this project is no longer being developed. The named project provides a substitute or replacement.",
"type": "string",
@ -155,11 +181,11 @@
"properties": {
"postinstall": {
"type": "string",
"$ref": "#/definitions/entry_point"
"$ref": "#/definitions/export_specifier"
},
"preuninstall": {
"type": "string",
"$ref": "#/definitions/entry_point"
"$ref": "#/definitions/export_specifier"
}
}
},
@ -221,6 +247,45 @@
"required": ["requires"],
"additionalProperties": false
},
"commands": {
"type": "object",
"properties": {
"wrap_console": {
"type": "object",
"$ref": "#/definitions/export_map"
},
"wrap_gui": {
"type": "object",
"$ref": "#/definitions/export_map"
},
"prebuilt": {
"type": "array",
"items": {
"type": "string",
"$ref": "#/definitions/relative_path"
}
}
},
"additionalProperties": false
},
"exports": {
"type": "object",
"patternProperties": {
"^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$": {
"type": "object",
"$ref": "#/definitions/export_map"
}
},
"additionalProperties": false
},
"export_map": {
"type": "object",
"patternProperties": {
"^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$": {
"type": "string",
"$ref": "#/definitions/export_specifier"
}
},
"valid_name": {
"type": "string",
"pattern": "^[0-9A-Za-z]([0-9A-Za-z_.-]*[0-9A-Za-z])?$"
@ -234,14 +299,21 @@
"environment_marker": {
"type": "string"
},
"entry_point": {
"type": "string"
},
"document_name": {
"type": "string"
},
"extra_name" : {
"type": "string"
},
"relative_path" : {
"type": "string"
},
"export_specifier": {
"type": "string",
},
"dotted_name" : {
"type": "string",
"pattern": "^[A-Za-z]([0-9A-Za-z_])*([.][A-Za-z]([0-9A-Za-z_])*)*$"
}
}
}