Palladion Software
user icon Guest

zc.buildout vs. "plain" setuptools

A brief proposal on how and when one might prefer to use "standard" setuptools features, rather than relying on zc.bulidout.

Created by tseaver. Last modified 2008-04-23 20:32:58.

Overview

The Zope development community has largely adopted zc.buildout as a de facto standard for how it builds and maintains Zope-related software. This post attempts to outline some reasons why zc.buildout may not be a "sufficient" tool for all cases, and proposes some additional practices which may help alleviate the issues described.

Strengths of zc.buildout

The zc.buildout framework allows for creating a repeatable environment using one or more "recipes" (which serve as plug-ins for the fraemwork) and a configuration file, which defines policies for a given environment. There are recipes available for a wide variety of tasks, ranging from building and testing a single Python package, to building a Python application using any number of such packages, to building arbitrary applications from source (e.g., using "configure && make && make install").

After bootstrapping, a developer can ensure that her environment is in a known state by running (or re-running) the bin/buildout command created by the bootstrap script. This command parses the buildout.cfg file, and uses it to build a number of "parts", each configured via a section in the configuration file, and driven by a recipe.

As the environment grows more complex, the find-grained control provided by zc.buildout becomes much more helpful.

Weaknesses of zc.buildout

At the simpler end of the spectrum, however (building and testing a single, pure-Python package), the advantages are not so clear. In particular:

Recommended Practices

Rather than advocating that the Zope community abandon zc.buildout, for the "simple package" case, I would like to propose some practices which mitigate some of the downsides.

  1. Keep metadata inside the setup.py script.

    Resist the temptation to exploit the additional expressiveness of the configuration language provided by zc.buildout when packaging reusable librarires.

  2. Keep setup.py as declarative as possible.

    Avoid "tricky" things inside the script: use the "standard" mechanisms as much as possible. For instance, use pkg_resources.find_packages, rather than roll your own file-finding logic.

  3. Make metadata in `setup.py` introspectable.

    In particular, move the various kinds dependency information out into globals, if the zc.buildout framework cannot introspect it directly.

    For instance, because zc.buildout does not support the tests_require keyword directly, we should perhaps adopt a conventional name (e.g., TESTS_REQUIRE) at "module scope" in the setup.py script, which contains the testing dependencies to be passed to setup(). zc.buildout could then use such a name to configure its test runner scripts.

  4. Ensure that the package works in the abasence of zc.buildout.

    Before releasing the package to the wider Python world, ensure that the "standard" distutils / setuptools commands (build, develop, install, 'test'[1]) work with the package in an environment which does not have zc.buildout installed.

    The virtualenv package provides an easy means of creating such environments.

  5. Distribute the sdist tarball.

    Unless your package has C extensions, you should plan to distribute only the source distribution (the one created by setup.py sdist). This tarball will contain all the files in your package, including tests and documentation, as opposed to the various binary formats. The binary formats often have subtle portability issues, as well some of which can't be resolved (e.g., 32-bit vs. 64-bit, Python version, UCS2 vs. UCS4, etc.).

    Windows developers should be able to install pure Python sdist distributions, as well, even without the compiler required to build C extensions.

    If your package does include C extensions, then you should plan, if possible, to build and distribute the bdist_win package, for the benefit of Windows users who can't compile the extension. You might also distribute a bdist_egg built for Windows (some users prefer the installer version, and others the egg). Generally, it would be best to avoid building other binary distributions at all: on any platform where Python or Zope is likely to run, the extension should be trivial to build from the sdist version.

[1] One issue with the setup.py test command is that any dependencies which are not already in the current environment will be installed as eggs into the unpacked tarball's top-level directory. These eggs will interfere with later setup.py commands, if not removed.