1. Carl Meyer
  2. sample-distutils2-project


Éric Araujo  committed 09fdec5

Turn the setup.cfg into shiny reST full-length proposal

  • Participants
  • Parent commits 6a9c17b
  • Branches default

Comments (0)

Files changed (6)

File .hgignore

View file

File Makefile

View file
+# Makefile for Sphinx documentation
+# You can set these variables from the command line.
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+	-rm -rf $(BUILDDIR)/*
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/StaticMetadataDiscussion.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/StaticMetadataDiscussion.qhc"
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

File conf.py

View file
+# -*- coding: utf-8 -*-
+# Static Metadata Discussion build configuration file.
+# This file has been minimized, run sphinx-quickstart in an empty dir
+# to get a new file with all options, default values and comments.
+extensions = []
+templates_path = ['_templates']
+source_suffix = '.rst'
+master_doc = 'index'
+project = u'Static Metadata Discussion'
+copyright = u'2010, Carl Meyer, Éric Araujo and others'
+version = '0.1'
+release = version
+exclude_trees = ['_build']
+add_function_parentheses = False # a reference is not a call dammit
+pygments_style = 'sphinx'
+html_theme = 'default'
+html_theme_options = {'collapsiblesidebar': True} # requires Sphinx 1.0
+html_title = project
+htmlhelp_basename = 'StaticMetadataDiscussion'

File index.rst

View file
+.. doc master file; should at least contain the root `toctree` directive.
+Static Metadata Discussion
+.. toctree::
+   new-config-file

File new-config-file.rst

View file
+Accepted and Proposed Changes to Configuration Files
+:Author: Éric Araujo
+:Credits: Carl Meyer, folks at PyCon 2010, people in #distutils
+:License: PSF
+One goal of Distutils2 is to put all the information required to build and
+install a distribution into a static configuration file (:file:`setup.cfg`)
+instead of Python code (:file:`setup.py`). This information (i.e. the arguments
+to ``distutils.core.setup``) is split into metadata_, files_ and
+`customization hooks`_.
+In the olden days, Distutils configuration files were used only to give
+options to commands. They were designed to be extensible: Third-party tools
+extending Distutils could tell their users to add a section in the
+distribution’s :file:`setup.cfg` file or in their user config file to give
+specific options to new commands or tools (e.g. testing tools). There is a
+simple API to get these options merged from all configuration files (which
+will be even simpler in Distutils2).
+.. i.e. d2.config.get_options('section') instead of
+   distribution.metadata.get_option_dict('section')
+Moving the distribution configuration from a script to a static file makes it
+easier for tools to get the information without having to run code (the
+:file:`setup.py` script). It will also allow a variety of tools written in any
+language to work from the same information.
+The new sections are different from usual sections that give options to
+commands because they make no sense in system or user configuration files.
+While specifying ``install`` or ``sdist`` options in a system or user
+configuration file is useful, options like author name or scripts to include
+in a distribution have to be in the project’s config file only.
+In another discussion, it may be good to think about configuration files
+precedence rules; e.g. if a user specifies an installation directory in their
+own config file, why is the distribution’s file able to override that choice?
+.. TODO take ideas from http://wiki.python.org/moin/Distutils/StaticMetadata
+        + add more comments there
+.. TODO document encoding for the config file (UTF-8/mbcs on win32)
+.. highlight:: cfg
+The first kind of :func:`setup` arguments that should be supported in
+:file:`setup.cfg` are the ones that give metadata. Fields are defined in
+:PEP:`345` and progress is tracked on `Python #8252`_. These fields are
+  [metadata]
+  name = RestingParrot
+  version = 0.6.4
+  author = Carl Meyer
+  author-email= carl@oddbird.net
+  summary = A sample project demonstrating distutils2 packaging
+  classifiers =
+    Development Status :: 4 - Beta
+    Environment :: Console (Text Based)
+    Environment :: X11 Applications :: GTK; sys.version < '3'
+    License :: OSI Approved :: MIT License
+    Programming Language :: Python
+    Programming Language :: Python :: 2
+    Programming Language :: Python :: 3
+  requires-dist =
+    PetShoppe
+    MichaelPalin (> 1.1)
+    pywin32; sys.platform == 'win32'
+    pysqlite2; python_version < '2.5'
+    inotify (0.0.1); sys.platform == 'linux2'
+  requires-external = libxml2
+  provides-dist = distutils2-sample-project (0.2)
+                  unittest2-sample-project
+  project-url =
+    Main repository, http://bitbucket.org/carljm/sample-distutils2-project
+    Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project
+Multi-value fields use newline-separated values, since the values themselves
+may contain spaces. The first (or only) value may be on the same line as the
+key or on the following one.
+The `config file parser`_ automatically supports case variation and
+underscores in place of hyphen in field names. Our own documentation should be
+consistent and use only lower case and hyphens, for simplicity and
+The `PEP 345 environment markers`_ used here will be passed to the
+``DistributionMetadata`` instance without processing, as intended: The class
+knows which fields are allowed to use a marker and how to interpret them.
+.. TODO have DM raise an exception if fields use a marker but should not
+   (PEP 345 only allows requires-*, *-dist and classifier) + report that in
+   the check command
+Some fields don’t have a specified format or can be improved. Proposals
+Avoid Metadata-Version
+This field does not have to be in the file, since the ``DistributionMetadata``
+class detects the right version from the fields that are present.
+.. TODO make the detection better, see email from Alexis
+Use CSV for Keywords and Requires-Python
+:PEP:`345` only says that the ``Keywords`` field is “a list”; the example
+uses space-delimited values, but distutils and distutils2 print out
+comma-separated values, which allows having keywords with spaces in them
+(e.g. “version control”). The ``Requires-Python`` field is described as
+comma-separated values.
+Since keywords and supported versions are typically much shorter than
+classifiers or dependencies, I propose that :file:`setup.cfg` use a
+comma-separated list of values, with leading and trailing spaces removed for
+user convenience (i.e.
+``keywords = version control, packaging, testing, unit testing`` will give the
+list ``['version control', 'packaging', 'testing', 'unit testing']``).
+More examples::
+  requires-python = 2.6
+  requires-python = >=2.4, <=3.0
+Alternatively, if it is deemed confusing to have two ways of giving
+multi-value fields, the field can be newline-separated like other fields
+already defined. Consistency would win other convenience.
+Merge author and author-email
+Merge name and email in a single field for author (and maintainer)::
+  author = Carl Meyer <carl@oddbird.net>
+  maintainer = Éric Araujo <merwok@netwok.org>
+It is a common format, easy to parse (we do not support any valid :RFC:`2822`
+email field, just specifically ``name <email>``). :PEP:`345` :file:`METADATA`
+files separate author name and email, but for user-written :file:`setup.cfg`
+this format is nicer.
+Get description from a file
+Use the contents of a file as value for ``description``. Long descriptions
+typically contain blank lines, which are stripped by our `config file parser`_
+in Python < 3.2, so including the long description directly in the config file
+is a non-starter. People often already have the description in a
+:file:`README` file or equivalent, which they can edit and check with
+reST-enabled tools. Thus this proposal replaces a very common idiom in setup
+scripts, prevents duplication and desynchronisation, and avoids the need for
+me to touch regular expressions to tweak the parser in unholy ways. The value
+is a path relative to the directory containing the :file:`setup.cfg` (``..``
+disallowed). Examples::
+  description = README
+  description = lib/python/unicorn/README.rst
+The value can be a list of files, to be concatenated in order and used as
+description. Now that Distutils2 and PyPI allow uploading documentation and
+adding arbitrary links in a project page, the need for overly long
+``description`` values is reduced, but some users would still want to
+concatenate e.g. :file:`README` and :file:`NEWS`. Thus, this field should
+allow a newline-separated list of files (it allows paths with spaces and
+complies with already-defined way of giving multi-value fields).
+Files listed in that field are automatically added to distributions.
+Get values from hooks
+Some users would like to specify callback functions instead of writing some
+values, to avoid repetition. Let’s take ``version`` as example. It is very
+common to have it stored as a tag in version control, and there are a number of
+helper functions to get this information. We could have this kind of field::
+  [metadata]
+  get-version = _buildhelper.get_hg_version
+Tarek strongly disagrees with this proposal, since it conflicts with the point
+of having static metadata. If the metadata is fully defined by a file format,
+then any tool in any language can follow the specification [#]_ to implement a
+parser and do useful things with the values, without depending on Distutils2,
+setup scripts or Python at all. People who really cannot write the version
+number in :file:`setup.cfg` for some reason can still use a :file:`setup.py`
+and benefit from fixes and features in Distutils2, but they won’t be static
+.. [#] Not this work-in-progress document, but a better one evolved from it.
+Furthermore, the version example is not a strong argument. When doing a
+release, updating the version number in :file:`setup.cfg` is but a minor and
+quick step. Documentation needs to be checked, translations built, the version
+number has to be updated in :file:`README`, :file:`NEWS` or :file:`CHANGES`,
+source code, so using a hook to set version in :file:`setup.cfg` would remove
+only one tidbit of work.
+Other fields may be duplicated in documentation files and :file:`setup.cfg`,
+i.e. author, summary and project URIs, but this duplication has a very small
+cost. Dependencies, classifiers and keywords are only in :file:`setup.cfg`, so
+wouldn’t benefit from hooks at all.
+In conclusion, I suggest to explore other solutions: Use a version control
+hook to edit :file:`setup.cfg` when a special tag is created, or use a shell
+function to create the tag from the value in :file:`setup.cfg`. People who
+love automation typically write a small script or makefile to do all
+operations related to a release (adjust version numbers in relevant files, run
+lint tools, run i18n tools, etc.), then check if the result look good (not
+always trusting automated tools is sane), commit, tag, push, send
+announcements, register the release in catalogs and so on.
+Fix misnamed fields
+The things listed in ``requires-dist`` and friends have a name and a version
+(optional) but no particular distribution format, therefore they’re not
+distributions but releases (or arguably projects). Editing accepted PEPs is
+hard, but it’s better to do it now before our tools and terminology are used
+in the wild. This item is listed only for completeness, not to call a vote;
+Alexis is the owner of this request, and the field names in :file:`setup.cfg`
+will follow the latest version of :PEP:`345`.
+Most other :func:`setup` arguments have to do with files. Arguments list
+Python modules, extension modules (written in C or C++), packages, scripts,
+files related to a package, other files. As specified in `Python #8253`_, a
+new section is introduced, ``files``; nothing else is already defined.
+Listing modules and packages
+For the new format, it is proposed that the three kinds of modules (Python
+modules, extension modules and packages) be merged into a single option.
+Given this file structure:
+.. code-block:: sh
+  $ tree
+  .
+  ├── haven.py
+  ├── pirate.c
+  ├── setup.cfg
+  └── ship
+      ├── cabins
+      │   ├── captain.py
+      │   └── __init__.py
+      ├── hull.c
+      └── __init__.py
+This configuration is enough to include the two modules, the package, its
+subpackage and all submodules in the distribution and have them processed by
+the relevant commands (``build_*``, ``sdist``, etc.)::
+  [files]
+  modules = haven
+            pirate
+            ship
+(See below_ for the rationale to separate with newlines instead of arbitrary
+.. _below: `replacing conditionals`_
+Distutils2 code needs to sort this list into the three lists used by
+``Distribution``, following these simple rules:
+#. If the name is listed is the `extension modules`_ section, an instance of
+   ``distutils2.extension.Extension`` is created and added to
+   ``distribution.ext_modules``;
+#. If the name corresponds to an existing directory which contains an
+   ``__init__.py`` file, it is added to ``distribution.packages``;
+#. The name is added to ``distribution.py_modules``.
+It is not possible to have a module and a package with the same name. In
+addition to being documented, this restriction could also be a runtime
+.. i.e. in d2.config.get_files('modules') + check command
+Package addition is recursive; real-world use of
+:func:`setuptools.find_packages` shows that this is a useful feature. There is
+a way to control it::
+  modules = ship
+  exclude-modules = ship.hull
+Additionally, a boolean option could control all-or-nothing recursion::
+  modules = ship
+  recursive-modules = 0
+It is not clear that this would solve problems, e.g. for the mx project which
+is developped in one tree but packaged as separate PyPI projects; maybe it’s
+best not to define this option right now and try converting complex projects
+before revising this proposal.
+Calling packages “modules” may be confusing to some people, e.g. beginners,
+even if it’s technically correct. Other proposed names include “source” (too
+vague) and “importables” (unused in the documentation and ugly).
+Alternatively, modules and packages could be listed separately. Since it
+appears that people tend to use either one or the other in their projects,
+there would be no cognitive overload in defining two fields instead of one::
+  modules = haven
+            pirate
+  packages = ship
+  exclude-packages = ship.hull
+This may also prevent future problems when namespace packages are supported
+by Python, or maybe not; I have to read :PEP:`382` closely to get a better
+undestanding of file layout and possible detection (esp. recursion) issues.
+Replacing package_dir
+Instead of replacing the ``package_dir`` argument with a field of the same
+name, it is proposed to merge it with the ``packages`` (or ``modules``)
+  packages =
+    ship
+    src:parrot
+    src2:thing
+  exclude-packages =
+    src:parrot.tests
+This translates this file structure:
+.. code-block:: sh
+  $ tree .
+  ├── ship
+  │   └── __init__.py
+  ├── src
+  │   └── parrot
+  │       ├── __init__.py
+  │       └── tests
+  │           └── __init__.py
+  └── src2
+      └── thing
+          └── __init__.py
+Note that these semantics are different from
+``setup(packages=['thing'], package_dir={'thing': 'src2'}``: In the new
+proposal, ``src2:thing`` does not mean that the :file:`src2` directory is to
+be renamed :file:`thing` in the build directory, but that the directory
+:file:`src2` contains another directory named :file:`thing` (with its
+:file:`__init__.py` file and other submodules). The changed semantics are more
+Using ``:`` as a separator forbids using it in directory name, which would not
+be very sane anyway. It also allows putting packages in a deep subdirectory::
+  packages = client/bindings/python:parrotlib
+This source directory syntax is also available for modules (in case the
+proposal to merge them with packages is rejected)::
+  modules = ham/lib/python:ham
+            cheese/lib/python:cheese
+.. TODO kill use of “root package” in the docs and use a better term
+        (say “top-level modules”)
+If a study of setup scripts in projects distributed on the Cheeseshop reveals
+that an overwhelming majority uses only one ``package_dir`` or none, this
+alternate, simpler proposal would be enough::
+  packages = parrot
+  exclude-packages = parrot.tests
+  package-dir = src
+Replacing conditionals
+We can define the ``packages`` or ``modules`` field to be newline-separated
+and accept `PEP 345 environment markers`_ to support source distributions that
+contain e.g. code for both 2.x and 3.x, like httplib2_ does::
+  packages =
+    python2:httplib2; sys.version < '3'
+    python3:httplib2; sys.version > '2'
+Since there is no ``else``, each condition has to be written twice (once in
+normal form, once in reverse, which can be tricky and/or tedious), and the
+values available as ``EXPR`` in environment markers (Python version, OS name,
+ etc.) do not provide all that is required. One example from Mercurial that
+is not trivial to reverse (or maybe I’m just bad at boolean logic):
+.. code-block:: python
+  if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
+      pymodules.append('mercurial.pure.osutil')
+Example that can’t be translated:
+.. code-block:: python
+  if sys.platform == 'linux2' and os.uname()[2] > '2.6':
+    # The inotify extension is only usable with Linux 2.6 kernels.
+    ...
+For such cases, the solution seems to use a `pre-build hook`_ to edit the
+lists of modules and packages. For trivial cases, environment markers provide
+a solution that does not require writing any code, so they’re still useful in
+the ``files`` section.
+Extension modules
+A new section is proposed to describe extension modules, to replace
+instantiation of :class:`Extension` objects with the right options in
+First proposal::
+  [extensions]
+  pirate = pirate.c
+  ship.hull = ship/hull.c
+The format is ``module = source files [arguments to the compiler]``. More
+involved example from SDL (converted from older ``Setup`` format, deprecated
+in Distutils and removed in Distutils2, see
+  [extensions]
+  _camera = src/_camera.c src/camera_v4l2.c src/camera_v4l.c $SDL $DEBUG
+  _numericsurfarray = src/_numericsurfarray.c $SDL $DEBUG
+  font = src/font.c $SDL $FONT $DEBUG
+  scrap = src/scrap.c $SDL $SCRAP $DEBUG
+This example introduces variables, which can be any string. A simple proposal
+for the assignment syntax::
+  $DEBUG =
+  $GFX = src/SDL_gfx/SDL_gfxPrimitives.c
+  $SDL = -I/usr/include/SDL -D_REENTRANT -lSDL
+  $FONT = -lSDL_ttf
+  $SCRAP = -lX11
+In other words, every key starting with a dollar sign is a variable that will
+be usable in the regular fields of the same section.
+If useful, expanding already defined variables in other variable definitions
+could be allowed.
+If useful, environment markers could be allowed in fields describing extension
+modules. Same thing for variables.
+This format effectively supersedes the old ``Setup`` format, supported by
+:func:`distutils.extension.read_setup_file` (removed in Distutils2). Python
+itself still uses a file in this format called :file:`Setup.dist` but uses its
+own helper to consume it, so not supporting it in Distutils2 is okay.
+An alternate proposal is a direct conversion of the :class:`Extension` class
+rather than ``Setup`` files::
+  [files]
+  modules = pirate
+  [extension: pirate]
+  sources = pirate.c
+  headers = Python.h pirate.h
+  optional = 1
+Field names are the same as valid :class:`Extension` arguments, values are
+simple adaptations (string arguments are single values, string lists are
+multi-value fields (whitespace-separated or newline-separated, to be decided),
+booleans are :mod:`ConfigParser` booleans).
+Variables from the other proposal could be added; environment markers could
+Listing scripts
+The Python list naturally translates to a multi-value field::
+  [files]
+  scripts =
+    detect-witch
+    scripts/find-coconuts
+    bin/taunt
+Paths are relative, with ``..`` component forbidden. Using multi-line instead
+of whitespace or comma-separated allows directory names with spaces and also
+environment markers::
+  scripts =
+    unit2.py; sys.platform == 'win32'
+    unit2; sys.platform != 'win32'
+For this precise use case, distutils could add the ``.py`` so-called file
+extension on win32 itself (see `Python #870479`_). Environment markers could
+still be useful for other uses, or we could not use them and let people define
+a `pre-build hook`_ for such cases.
+Proposals like fixing ``sys.path`` for scripts run from an uninstalled source
+or supporting :mod:`pkg_resources`-like ``console_scripts`` are regular feature
+requests outside of the scope of this document which can be implemented with
+new fields or extended syntax for existing fields.
+People have wanted a way to install into ``$exec_prefix/sbin`` instead of
+``$exec_prefix/bin``. There is no proposal about that now, although it would
+not be hard to define, since it is believed that the future resources_ tagging
+will supersede the scripts section and address this feature request in a
+generic way.
+Additional files
+While the PEP on resources_ is not implemented, :file:`setup.cfg` will grow
+new fields that merely translate the :file:`setup.py` form without any added
+semantics or features. Example::
+  [files]
+  package_data =
+    cheese = data/templates/*
+Semantics for the paths are described in the `documentation for package_data
+Environment markers are not supported. The same rules apply for other data
+  [files]
+  data_files =
+    bitmaps = bm/b1.gif, bm/b2.gif
+    config = cfg/data.cfg
+    /etc/init.d = init-script
+Comma-separated values allow paths with spaces.
+.. XXX check whether package_data={'': 'something'} works
+Replacing MANIFEST.in
+The `design document`_ for resources_ states the goal to remove redundant
+listing of files in favor of :file:`setup.cfg`. Files listed as modules,
+packages, scripts, source files for extension modules (maybe tricky), package
+data, extra data files or description file will automatically be included in
+the manifest object used by the distribution. Before resources_ tagging is
+implemented, we could either still support the :file:`MANIFEST.in` file or
+move its contents to :file:`setup.cfg`, retaining strict syntactic and
+semantic compatibility::
+  [files]
+  sdist_extra =
+    include THANKS HACKING
+    recursive-include examples *.txt *.py
+    prune examples/sample?/build
+.. TODO check if paths with spaces work/should work
+.. TODO check if MANIFEST is obsoleted by PEP 376 RECORD file
+   I think not, since it’s not included in sdists/bdists
+Psychic Mode
+This proposal takes advantage from convention over configuration, following
+the lead of DistUtilsExtra.auto_. The idea is to automatically detect specific
+file patterns. Examples taken from its documentation (somewhat edited):
+* Python modules (:file:`{*}.py`, only in root directory)
+* Python packages (all directories with :file:`__init__.py`)
+* Scripts (:file:`bin/{*}`, :file:`scripts/{*}` and :file:`./{projectname}`)
+* Auxiliary data files (`data/{*}`)
+* Automatic :file:`MANIFEST` (everything except swap and backup files,
+  :file:`{*.py[co]}`, and version control directories); this may already be
+  done in distutils 2.7/3.1, and we’re not sure we want to keep this file
+  anyway.
+This mode would be opt-in, e.g. ::
+  [global]
+  autodetect = 1
+Other ideas not considered include less used or platform-specific file
+formats, which could be autodetected after the resources_ proposal is
+implemented. Automatic ``Requires-Dist`` and ``Provides-Dist`` from
+``import`` statements seem brittle and are not considered. Automatic
+:file:`POTFILES.in` depends on consensus that i18n-related tasks are in the
+scope of Distutils, which is not reached right now.
+Alternative: Require that people run ``distutils2.mkpkg`` to trigger detection
+and have the configuration file created or updated. Then they can check that
+the update is right thanks to ``$vcs diff setup.cfg`` or ``editor setup.cfg``.
+A PEP needs to be written. See the `design document`_.
+Customization hooks
+(This is not related to `pre/post-command hooks`_, which will probably be set
+in the relevant command sections or in a new one.)
+The third kind of :func:`setup` arguments are customization hooks.
+  Specify a class to use instead of :class:`distutils2.dist.Distribution`
+  Mapping of command names to classes, to replace existing commands or provide
+  new ones, e.g.
+  .. code-block:: python
+    setup(..., cmdclass={'build_py': build_py_2to3, 'test': TestCommand})
+  Usually ``sys.argv[0]``, used to generate error messages with the correct
+  script name in case it it not :file:`setup.py`.
+  List of arguments to use instead of ``sys.argv[1:]``
+The last two arguments are not needed in :file:`setup.cfg`, whereas the first
+two have are useful and can use this simple syntax::
+  [global]
+  distclass = shop.cheese.HamDistribution
+  cmdclass =
+    build_py = distutils2.build_py.build_py_2to3; sys.version > '3'
+    test = lib:_buildhelper.TestCommand
+As you can see, environment markers and `source directory`_ specifiers are
+allowed. The fields are located in the ``global`` section, alongside
+Additional proposal: Give the field a clearer name. ``command-classes`` or
+``commands`` (and change it in the Python code too). If there is a good reason
+to keep it short, it should at least be a plural form, i.e. ``cmdclasses``.
+.. TODO understand and integrate Tarek’s thing about declaring commands
+Annex: Making things simple for users
+mkpkg (to be given a better name) will help people do the right thing, e.g.
+use a compliant version number, fill the ``license`` field only when there is
+no suitable Trove classifier for the chosen license, in other words give
+useful hints for people that don’t read PEPs or documentation.
+mkpkg is an interactive mini-program that will generate a :file:`setup.cfg`
+file thanks to questions asked to the user (what is the project name, its
+version, etc.). As much as possible, the program will propose answers (e.g.
+using the :func:`find_package` function to get the list of packages) so that
+the user just has to press :kbd:`Enter` to validate, or write the correct
+value and validate.
+Some values could be specified in the user configuration file, e.g. the author
+name or a template for the project homepage (e.g.
+``http://example.org/hacking/projects/{name}``). It’s tempting to reuse the
+``metadata`` section name but it may be confusing, since it would mean that
+the same section is used by two commands depending on the origin file, which
+is not an expectation in the Distutils model (which I will already change to
+introduce sections that are not allowed at all config file levels). It’s
+cleaner if the program uses its own section in the user config file.
+For people wanting to upgrade progressively, Distutils2 includes a
+lib2to3-based converter to rewrite imports (:mod:`distutils2` provides a
+:func:`setup` function with a signature compatible with the one from
+:mod:`distutils` and a :func:`find_packages` function similar to the one from
+:mod:`setuptools`), to allow projects to keep using a setup script while they
+transition their practices and installation documentation.
+Annex: Implementation details
+The `config file parser`_ strips leading and trailing whitespace for free, we
+just have to handle the case of the first line being empty (in the line
+``spam =\nham``, the config file format considers there is an empty line after
+the equals). Handling that case is as simple as
+Helper functions to split the `source directory`_ specifier, resolve a dotted
+name and split an environment marker will be provided in :mod:`util` and
+:mod:`config` for use by third-party tools. :mod:`config` will also provide
+higler-level functions to get a :class:`DistributionMetadata` instance from a
+:file:`setup.cfg` file, a list of Python modules, a list of Python packages
+filtered according to the environment, access a config section defined by a
+Distutils2 extension, and so on.
+.. XXX use a special section name prefix for those extensions?
+Annex: Mapping from arguments to fields
+============================ ==============================
+Argument in :file:`setup.py` Field in :file:`setup.cfg`
+============================ ==============================
+description                  summary
+long_description             description (changed meaning)
+author                       author
+author_email                 merged with author
+maintainer                   maintainer
+maintainer_email             merged with maintainer
+url                          home-page
+N/A                          project-url
+every other metadata field   unchanged
+packages                     packages or modules
+py_modules                   modules
+ext_modules                  modules (+ ``[extensions]``)
+ext_package                  unsupported
+distclass                    distclass
+cmdclass                     cmdclasses
+script_name                  N/A
+script_args                  N/A
+options                      fields under ``[{command}]``
+============================ ==============================
+.. Footnotes and References
+   """"""""""""""""""""""""
+.. _source directory: `replacing package_dir`_
+.. _pre-build hook: `pre/post-command hooks`_
+.. _PEP 345 environment markers:
+   http://www.python.org/dev/peps/pep-0345/#environment-markers
+.. _design document:
+   http://hg.python.org/distutils2/file/tip/docs/design/wiki.rst
+.. _Python #8252: http://bugs.python.org/issue8252
+.. _Python #8253: http://bugs.python.org/issue8253
+.. _Python #870479: http://bugs.python.org/issue870479
+.. _pre/post-command hooks: http://bugs.python.org/issue8312
+.. _config file parser:
+   http://docs.python.org/library/configparser#ConfigParser.RawConfigParser
+.. _command-packages:
+   http://docs.python.org/distutils/extending#integrating-new-commands
+.. _httplib2: http://code.google.com/p/httplib2/source/browse/setup.py
+.. _DistUtilsExtra.auto:
+   http://bazaar.launchpad.net/~python-distutils-extra-hackers/
+   python-distutils-extra/debian/annotate/head:/doc/README   

File setup.cfg

View file
+# Distutils2-compatible configuration file. See new-config-file for more
+# details, proposals and rationales about each section.
 # these fields are specified in PEP 345
 name = RestingParrot
 version = 0.6.4
+# merged author name + email is pending approval
 author = Carl Meyer <carl@oddbird.net>
 summary = A sample project demonstrating distutils2 packaging
+# this is under discussion
+description = README.rst
 license = MIT
-# multi-line values are separated with arbitrary whitespace; the config
-# file parsers strips leading and trailing whitespace for free, we just
-# have to handle the case of the first line being empty, like here:
-# (handling it is just value.strip().splitlines())
 classifiers =
   Development Status :: 4 - Beta
   Environment :: Console (Text Based)
-# this field however is split on newlines only, not arbitrary whitespace,
-# separated, since there are spaces between name and version predicate,
-# name and marker, and sometimes even in the project name (hi twisted!)
 requires-dist =
   MichaelPalin (> 1.1)
-  pywin32; sys_platform == 'win32'
-  pysqlite2; python_version < 2.5
-  inotify (0.0.1); sys_platform == 'linux2'
+  pywin32; sys.platform == 'win32'
+  pysqlite2; python_version < '2.5'
+  inotify (0.0.1); sys.platform == 'linux2'
 requires-external = libxml2
-# this is under discussion
-#  proposal: path relative to location of setup.cfg, probably disallow '..',
-#              allow multiple files (but encourage keeping it short)
-description = README.rst
 # specifying packages and modules not yet covered by any PEP, TBD
 # proposal: