buildout layout and common use cases
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
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
handoutzope.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'},
)
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,
)
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)
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'],
),
)
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.

