Personal tools
You are here: Home Documentation Tutorials Introduction to zc.buildout buildout layout and common use cases

buildout layout and common use cases

Buildout directory structure and simple, common use casese
Jim Fulton's tutorial for using buildout, originally given at DZUG 2007
Page 8 of 17.

buildout layout

  • bin directory for generated scripts

  • parts directory for generated part data

    Many parts don't use this.

  • eggs directory for (most) installed eggs

    • May be shared across buildouts.
  • develop-eggs directory

    • develop egg links
    • custom eggs
  • .installed.cfg records what has been installed

handout

Some people find the buildout layout surprising, as it isn't similar to a Unix directory layout. The buildout layout was guided by "shallow is better than nested".

If you prefer a different layout, you can specify a different layout using buildout options. You can set these options globally so that all of your buildouts have the same layout.

Common buildout use cases

  • Working on a single package

    handout

    zope.event is an example of this use case.

  • System assembly

  • Try out new packages

    • workingenv usually better
    • buildout better when custom build options needed
  • Installing egg-based scripts for personal use

    ~/bin directory is a buildout

Creating eggs

Three levels of egg development

  • Develop eggs, a minimal starting point
  • Adding data needed for distribution
  • Polished distributions

A Minimal/Develop setup.py

from setuptools import setup
setup(
    name='foo',
    package_dir = {'':'src'},
    )
handout

If we're only going to use a package as a develop egg, we just need to specify the project name, and, if there is a separate source directory, then we need to specify that location.

We'd also need to specify entry points if we had any. We'll see an example of that later.

See the setuptools and distutils documentation for more information.

Distributable setup.py

from setuptools import setup, find_packages
name='zope.event'
setup(
    name=name,
    version='3.3.0',
    url='http://www.python.org/pypi/'+name,
    author='Zope Corporation and Contributors',
    author_email='zope3-dev@zope.org',
    package_dir = {'': 'src'},
    packages=find_packages('src'),
    namespace_packages=['zope',],
    include_package_data = True,
    install_requires=['setuptools'],
    zip_safe = False,
    )
handout

If we want to be able to create a distribution, then we need to specify a lot more information.

The options used are documented in either the distutils or setuptools documentation. Most of the options are fairly obvious.

We have to specify the Python packages used. The find_packages function can figure this out for us, although it would often be easy to specify it ourselves. For example, we could have specified:

packages=['zope', 'zope.event'],

The zope package is a namespace package. This means that it exists solely as a container for other packages. It doesn't have any files or modules of it's own. It only contains an __init__ module with:

pkg_resources.declare_namespace(__name__)

or, perhaps:

# this is a namespace package
try:
    import pkg_resources
    pkg_resources.declare_namespace(__name__)
except ImportError:
    import pkgutil
    __path__ = pkgutil.extend_path(__path__, __name__)

Namespace packages have to be declared, as we've done here.

We always want to include package data.

Because the __init__ module uses setuptools, we declare it as a dependency, using install_requires.

We always want to specify whether a package is zip safe. A zip safe package doesn't try to access the package as a directory. If in doubt, specify False. If you don't specify anything, setuptools will guess.

Polished setup.py (1/3)

import os
from setuptools import setup, find_packages

def read(*rnames):
    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()

long_description=(
        read('README.txt')
        + '\n' +
        'Detailed Documentation\n'
        '**********************\n'
        + '\n' +
        read('src', 'zope', 'event', 'README.txt')
        + '\n' +
        'Download\n'
        '**********************\n'
        )

open('documentation.txt', 'w').write(long_description)
handout

In the polished version we flesh out the meta data a bit more.

When I create distributions that I consider ready for broader use and upload to PyPI, I like to include the full documentation in the long description so PyPI serves it for me.

Polished setup.py (2/3)

name='zope.event'
setup(
    name=name,
    version='3.3.0',
    url='http://www.python.org/pypi/'+name,
    license='ZPL 2.1',
    description='Zope Event Publication',
    author='Zope Corporation and Contributors',
    author_email='zope3-dev@zope.org',
    long_description=long_description,

    packages=find_packages('src'),
    package_dir = {'': 'src'},
    namespace_packages=['zope',],
    include_package_data = True,
    install_requires=['setuptools'],
    zip_safe = False,
    )

Extras

name = 'zope.component'
setup(name=name,
      ...
      namespace_packages=['zope',],
      install_requires=['zope.deprecation', 'zope.interface',
                        'zope.deferredimport', 'zope.event',
                        'setuptools', ],
      extras_require = dict(
          service = ['zope.exceptions'],
          zcml = ['zope.configuration', 'zope.security', 'zope.proxy',
                  'zope.i18nmessageid',
                  ],
          test = ['zope.testing', 'ZODB3',
                  'zope.configuration', 'zope.security', 'zope.proxy',
                  'zope.i18nmessageid',
                  'zope.location', # should be dependency of zope.security
                  ],
          hook = ['zope.hookable'],
          persistentregistry = ['ZODB3'],
          ),
      )
handout

Extras provide a way to help manage dependencies.

A common use of extras is to separate test dependencies from normal dependencies. A package may provide other optional features that cause other dependencies. For example, the zcml module in zope.component adds lots of dependencies that we don't want to impose on people that don't use it.