PEP: 390 Title: Static metadata for Distutils Version: $Revision$
Last-Modified: $Date$ Author: Tarek Ziadé <tarek@ziade.org>
BDFL-Delegate: Nick Coghlan Discussions-To: <distutils-sig@python.org>
Status: Rejected Type: Standards Track Content-Type: text/x-rst Created:
10-October-2009 Python-Version: 2.7 and 3.2 Post-History: Resolution:
https://mail.python.org/pipermail/distutils-sig/2013-April/020597.html

Abstract
========

This PEP describes a new section and a new format for the `setup.cfg`
file, that allows describing the Metadata of a package without using
`setup.py`.

Rejection Notice
================

As distutils2 is no longer going to be incorporated into the standard
library, this PEP was rejected by Nick Coghlan in late April, 2013.

A replacement PEP based on PEP 426 (metadata 2.0) will be created that
defines the minimum amount of information needed to generate an sdist
archive given a source tarball or VCS checkout.

Rationale
=========

Today, if you want to list all the Metadata of a distribution (see PEP
314) that is not installed, you need to use the `setup.py` command line
interface.

So, basically, you download it, and run::

\$ python setup.py --name Distribute

\$ python setup.py --version 0.6.4

Where `name` and `version` are metadata fields. This is working fine but
as soon as the developers add more code in `setup.py`, this feature
might break or in worst cases might do unwanted things on the target
system.

Moreover, when an OS packager wants to get the metadata of a
distribution he is re-packaging, he might encounter some problems to
understand the `setup.py` file he's working with.

So the rationale of this PEP is to provide a way to declare the metadata
in a static configuration file alongside `setup.py` that doesn't require
any third party code to run.

Adding a `metadata` section in `setup.cfg`
==========================================

The first thing we want to introduce is a `[metadata]` section, in the
`setup.cfg` file, that may contain any field from the Metadata::

\[metadata\] name = Distribute version = 0.6.4

The `setup.cfg` file is used to avoid adding yet another configuration
file to work with in Distutils.

This file is already read by Distutils when a command is executed, and
if the `metadata` section is found, it will be used to fill the metadata
fields. If an option that corresponds to a Metadata field is given to
`setup()`, it will override the value that was possibly present in
`setup.cfg`.

Notice that `setup.py` is still used and can be required to define some
options that are not part of the Metadata fields. For instance, the
`sdist` command can use options like `packages` or `scripts`.

Multi-lines values
==================

Some Metadata fields can have multiple values. To keep `setup.cfg`
compatible with `ConfigParser` and the RFC 822 `LONG HEADER FIELDS` (see
section 3.1.1), these are expressed with `,`-separated values::

    requires = pywin32, bar > 1.0, foo

When this variable is read, the values are parsed and transformed into a
list: `['pywin32', 'bar > 1.0', 'foo']`.

Context-dependant sections
==========================

The `metadata` section will also be able to use context-dependant
sections.

A context-dependant section is a section with a condition about the
execution environment. Here's some examples::

\[metadata\] name = Distribute version = 0.6.4

\[metadata:sys\_platform == 'win32'\] requires = pywin32, bar \> 1.0
obsoletes = pywin31

\[metadata:os\_machine == 'i386'\] requires = foo

\[metadata:python\_version == '2.4' or python\_version == '2.5'\]
requires = bar

\[metadata:'linux' in sys\_platform\] requires = baz

Every `[metadata:condition]` section will be used only if the condition
is met when the file is read. The background motivation for these
context-dependant sections is to be able to define requirements that
varies depending on the platform the distribution might be installed on.
(see PEP 314).

The micro-language behind this is the simplest possible: it compares
only strings, with the `==` and `in` operators (and their opposites),
and with the ability to combine expressions. It makes it also easy to
understand to non-pythoneers.

The pseudo-grammar is ::

    EXPR [in|==|!=|not in] EXPR [or|and] ...

where `EXPR` belongs to any of those:

-   python\_version = '%s.%s' % (sys.version\_info\[0\],
    sys.version\_info\[1\])
-   os\_name = os.name
-   sys\_platform = sys.platform
-   platform\_version = platform.version()
-   platform\_machine = platform.machine()
-   a free string, like `2.4`, or `win32`

Notice that `in` is restricted to strings, meaning that it is not
possible to use other sequences like tuples or lists on the right side.

Distutils will provide a function that is able to generate the metadata
of a distribution, given a `setup.cfg` file, for the execution
environment::

> > > from distutils.util import local\_metadata
> > > local\_metadata('setup.cfg') <DistributionMetadata instance>

This means that a vanilla Python will be able to read the metadata of a
package without running any third party code.

Notice that this feature is not restricted to the `metadata` namespace.
Consequently, any other section can be extended with such
context-dependant sections.

Impact on PKG-INFO generation and PEP 314
=========================================

When `PKG-INFO` is generated by Distutils, every field that relies on a
condition will have that condition written at the end of the line, after
a `;` separator::

    Metadata-Version: 1.2
    Name: distribute
    Version: 0.6.4
    ...
    Requires: pywin32, bar > 1.0; sys_platform == 'win32'
    Requires: foo; os_machine == 'i386'
    Requires: bar; python_version == '2.4' or python_version == '2.5'
    Requires: baz; 'linux' in sys_platform
    Obsoletes = pywin31; sys_platform == 'win32'
    ...
    Classifier: Development Status :: 5 - Production/Stable
    Classifier: Intended Audience :: Developers
    Classifier: License :: OSI Approved :: Python Software Foundation License

Notice that this file can be opened with the `DistributionMetadata`
class. This class will be able to use the micro-language using the
execution environment.

Let's run in on a `Python 2.5 i386 Linux`::

    >>> from distutils.dist import DistributionMetadata
    >>> metadata = DistributionMetadata('PKG_INFO')
    >>> metadata.get_requires()
    ['foo', 'bar', 'baz']

The execution environment can be overridden in case we want to get the
metadata for another environment::

    >>> env = {'python_version': '2.4',
    ...        'os_name': 'nt',
    ...        'sys_platform': 'win32',
    ...        'platform_version': 'MVCC++ 6.0'
    ...        'platform_machine': 'i386'}
    ...
    >>> metadata = DistributionMetadata('PKG_INFO', environment=env)
    >>> metadata.get_requires()
    ['bar > 1.0', 'foo', 'bar']

PEP 314 is changed accordingly, meaning that each field will be able to
have that extra condition marker.

Compatibility
=============

This change is based on a new metadata `1.2` format meaning that
Distutils will be able to distinguish old PKG-INFO files from new ones.

The `setup.cfg` file change will stay `ConfigParser`-compatible and will
not break existing `setup.cfg` files.

Limitations
===========

We are not providing `<` and `>` operators at this time, and
`python_version` is a regular string. This implies using `or` operators
when a section needs to be restricted to a couple of Python versions.
Although, if PEP 386 is accepted, `python_version` could be changed
internally into something comparable with strings, and `<` and `>`
operators introduced.

Last, if a distribution is unable to set all metadata fields in
`setup.cfg`, that's fine, the fields will be set to `UNKNOWN` when
`local_metadata` is called. Getting `UNKNOWN` values will mean that it
might be necessary to run the `setup.py` command line interface to get
the whole set of metadata.

Acknowledgments
===============

The Distutils-SIG.

Copyright
=========

This document has been placed in the public domain.

 .. Local Variables: mode: indented-text indent-tabs-mode: nil
sentence-end-double-space: t fill-column: 70 coding: utf-8 End:
