diff -r 5ff1fc726848 -r c6bca38c1cbf eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/buildout.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/buildout.txt Sat Jan 08 11:20:57 2011 +0530 @@ -0,0 +1,2831 @@ +Buildouts +========= + +The word "buildout" refers to a description of a set of parts and the +software to create and assemble them. It is often used informally to +refer to an installed system based on a buildout definition. For +example, if we are creating an application named "Foo", then "the Foo +buildout" is the collection of configuration and application-specific +software that allows an instance of the application to be created. We +may refer to such an instance of the application informally as "a Foo +buildout". + +This document describes how to define buildouts using buildout +configuration files and recipes. There are three ways to set up the +buildout software and create a buildout instance: + +1. Install the zc.buildout egg with easy_install and use the buildout + script installed in a Python scripts area. + +2. Use the buildout bootstrap script to create a buildout that + includes both the setuptools and zc.buildout eggs. This allows you + to use the buildout software without modifying a Python install. + The buildout script is installed into your buildout local scripts + area. + +3. Use a buildout command from an already installed buildout to + bootstrap a new buildout. (See the section on bootstraping later + in this document.) + +Often, a software project will be managed in a software repository, +such as a subversion repository, that includes some software source +directories, buildout configuration files, and a copy of the buildout +bootstrap script. To work on the project, one would check out the +project from the repository and run the bootstrap script which +installs setuptools and zc.buildout into the checkout as well as any +parts defined. + +We have a sample buildout that we created using the bootstrap command +of an existing buildout (method 3 above). It has the absolute minimum +information. We have bin, develop-eggs, eggs and parts directories, +and a configuration file: + + >>> ls(sample_buildout) + d bin + - buildout.cfg + d develop-eggs + d eggs + d parts + +The bin directory contains scripts. + + >>> ls(sample_buildout, 'bin') + - buildout + + >>> ls(sample_buildout, 'eggs') + - setuptools-0.6-py2.4.egg + - zc.buildout-1.0-py2.4.egg + +The develop-eggs directory is initially empty: + + >>> ls(sample_buildout, 'develop-eggs') + +The develop-eggs directory holds egg links for software being +developed in the buildout. We separate develop-eggs and other eggs to +allow eggs directories to be shared across multiple buildouts. For +example, a common developer technique is to define a common eggs +directory in their home that all non-develop eggs are stored in. This +allows larger buildouts to be set up much more quickly and saves disk +space. + +The parts directory just contains some helpers for the buildout script +itself. + + >>> ls(sample_buildout, 'parts') + d buildout + +The parts directory provides an area where recipes can install +part data. For example, if we built a custom Python, we would +install it in the part directory. Part data is stored in a +sub-directory of the parts directory with the same name as the part. + +Buildouts are defined using configuration files. These are in the +format defined by the Python ConfigParser module, with extensions +that we'll describe later. By default, when a buildout is run, it +looks for the file buildout.cfg in the directory where the buildout is +run. + +The minimal configuration file has a buildout section that defines no +parts: + + >>> cat(sample_buildout, 'buildout.cfg') + [buildout] + parts = + +A part is simply something to be created by a buildout. It can be +almost anything, such as a Python package, a program, a directory, or +even a configuration file. + +Recipes +------- + +A part is created by a recipe. Recipes are always installed as Python +eggs. They can be downloaded from a package server, such as the +Python Package Index, or they can be developed as part of a project +using a "develop" egg. + +A develop egg is a special kind of egg that gets installed as an "egg +link" that contains the name of a source directory. Develop eggs +don't have to be packaged for distribution to be used and can be +modified in place, which is especially useful while they are being +developed. + +Let's create a recipe as part of the sample project. We'll create a +recipe for creating directories. First, we'll create a recipes source +directory for our local recipes: + + >>> mkdir(sample_buildout, 'recipes') + +and then we'll create a source file for our mkdir recipe: + + >>> write(sample_buildout, 'recipes', 'mkdir.py', + ... """ + ... import logging, os, zc.buildout + ... + ... class Mkdir: + ... + ... def __init__(self, buildout, name, options): + ... self.name, self.options = name, options + ... options['path'] = os.path.join( + ... buildout['buildout']['directory'], + ... options['path'], + ... ) + ... if not os.path.isdir(os.path.dirname(options['path'])): + ... logging.getLogger(self.name).error( + ... 'Cannot create %s. %s is not a directory.', + ... options['path'], os.path.dirname(options['path'])) + ... raise zc.buildout.UserError('Invalid Path') + ... + ... + ... def install(self): + ... path = self.options['path'] + ... logging.getLogger(self.name).info( + ... 'Creating directory %s', os.path.basename(path)) + ... os.mkdir(path) + ... return path + ... + ... def update(self): + ... pass + ... """) + +Currently, recipes must define 3 methods [#future_recipe_methods]_: + +- a constructor, + +- an install method, and + +- an update method. + +The constructor is responsible for updating a parts options to reflect +data read from other sections. The buildout system keeps track of +whether a part specification has changed. A part specification has +changed if it's options, after adjusting for data read from other +sections, has changed, or if the recipe has changed. Only the options +for the part are considered. If data are read from other sections, +then that information has to be reflected in the parts options. In +the Mkdir example, the given path is interpreted relative to the +buildout directory, and data from the buildout directory is read. The +path option is updated to reflect this. If the directory option was +changed in the buildout sections, we would know to update parts +created using the mkdir recipe using relative path names. + +When buildout is run, it saves configuration data for installed parts +in a file named ".installed.cfg". In subsequent runs, it compares +part-configuration data stored in the .installed.cfg file and the +part-configuration data loaded from the configuration files as +modified by recipe constructors to decide if the configuration of a +part has changed. If the configuration has changed, or if the recipe +has changed, then the part is uninstalled and reinstalled. The +buildout only looks at the part's options, so any data used to +configure the part needs to be reflected in the part's options. It is +the job of a recipe constructor to make sure that the options include +all relevant data. + +Of course, parts are also uninstalled if they are no-longer used. + +The recipe defines a constructor that takes a buildout object, a part +name, and an options dictionary. It saves them in instance attributes. +If the path is relative, we'll interpret it as relative to the +buildout directory. The buildout object passed in is a mapping from +section name to a mapping of options for that section. The buildout +directory is available as the directory option of the buildout +section. We normalize the path and save it back into the options +directory. + +The install method is responsible for creating the part. In this +case, we need the path of the directory to create. We'll use a path +option from our options dictionary. The install method logs what it's +doing using the Python logging call. We return the path that we +installed. If the part is uninstalled or reinstalled, then the path +returned will be removed by the buildout machinery. A recipe install +method is expected to return a string, or an iterable of strings +containing paths to be removed if a part is uninstalled. For most +recipes, this is all of the uninstall support needed. For more complex +uninstallation scenarios use `Uninstall recipes`_. + +The update method is responsible for updating an already installed +part. An empty method is often provided, as in this example, if parts +can't be updated. An update method can return None, a string, or an +iterable of strings. If a string or iterable of strings is returned, +then the saved list of paths to be uninstalled is updated with the new +information by adding any new files returned by the update method. + +We need to provide packaging information so that our recipe can be +installed as a develop egg. The minimum information we need to specify +[#packaging_info]_ is a name. For recipes, we also need to define the +names of the recipe classes as entry points. Packaging information is +provided via a setup.py script: + + >>> write(sample_buildout, 'recipes', 'setup.py', + ... """ + ... from setuptools import setup + ... + ... setup( + ... name = "recipes", + ... entry_points = {'zc.buildout': ['mkdir = mkdir:Mkdir']}, + ... ) + ... """) + +Our setup script defines an entry point. Entry points provide +a way for an egg to define the services it provides. Here we've said +that we define a zc.buildout entry point named mkdir. Recipe +classes must be exposed as entry points in the zc.buildout group. we +give entry points names within the group. + +We also need a README.txt for our recipes to avoid an annoying warning +from distutils, on which setuptools and zc.buildout are based: + + >>> write(sample_buildout, 'recipes', 'README.txt', " ") + +Now let's update our buildout.cfg: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mystuff + ... """) + +Let's go through the changes one by one:: + + develop = recipes + +This tells the buildout to install a development egg for our recipes. +Any number of paths can be listed. The paths can be relative or +absolute. If relative, they are treated as relative to the buildout +directory. They can be directory or file paths. If a file path is +given, it should point to a Python setup script. If a directory path +is given, it should point to a directory containing a setup.py file. +Development eggs are installed before building any parts, as they may +provide locally-defined recipes needed by the parts. + +:: + + parts = data-dir + +Here we've named a part to be "built". We can use any name we want +except that different part names must be unique and recipes will often +use the part name to decide what to do. + +:: + + [data-dir] + recipe = recipes:mkdir + path = mystuff + + +When we name a part, we also create a section of the same +name that contains part data. In this section, we'll define +the recipe to be used to install the part. In this case, we also +specify the path to be created. + +Let's run the buildout. We do so by running the build script in the +buildout: + + >>> import os + >>> os.chdir(sample_buildout) + >>> buildout = os.path.join(sample_buildout, 'bin', 'buildout') + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Installing data-dir. + data-dir: Creating directory mystuff + +We see that the recipe created the directory, as expected: + + >>> ls(sample_buildout) + - .installed.cfg + d bin + - buildout.cfg + d develop-eggs + d eggs + d mystuff + d parts + d recipes + +In addition, .installed.cfg has been created containing information +about the part we installed: + + >>> cat(sample_buildout, '.installed.cfg') + [buildout] + installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link + parts = data-dir + + [data-dir] + __buildout_installed__ = /sample-buildout/mystuff + __buildout_signature__ = recipes-c7vHV6ekIDUPy/7fjAaYjg== + path = /sample-buildout/mystuff + recipe = recipes:mkdir + +Note that the directory we installed is included in .installed.cfg. +In addition, the path option includes the actual destination +directory. + +If we change the name of the directory in the configuration file, +we'll see that the directory gets removed and recreated: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mydata + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling data-dir. + Installing data-dir. + data-dir: Creating directory mydata + + >>> ls(sample_buildout) + - .installed.cfg + d bin + - buildout.cfg + d develop-eggs + d eggs + d mydata + d parts + d recipes + +If any of the files or directories created by a recipe are removed, +the part will be reinstalled: + + >>> rmdir(sample_buildout, 'mydata') + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling data-dir. + Installing data-dir. + data-dir: Creating directory mydata + +Error reporting +--------------- + +If a user makes an error, an error needs to be printed and work needs +to stop. This is accomplished by logging a detailed error message and +then raising a (or an instance of a subclass of a) +zc.buildout.UserError exception. Raising an error other than a +UserError still displays the error, but labels it as a bug in the +buildout software or recipe. In the sample above, of someone gives a +non-existent directory to create the directory in: + + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = /xxx/mydata + ... """) + +We'll get a user error, not a traceback. + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + data-dir: Cannot create /xxx/mydata. /xxx is not a directory. + While: + Installing. + Getting section data-dir. + Initializing part data-dir. + Error: Invalid Path + + +Recipe Error Handling +--------------------- + +If an error occurs during installation, it is up to the recipe to +clean up any system side effects, such as files created. Let's update +the mkdir recipe to support multiple paths: + + >>> write(sample_buildout, 'recipes', 'mkdir.py', + ... """ + ... import logging, os, zc.buildout + ... + ... class Mkdir: + ... + ... def __init__(self, buildout, name, options): + ... self.name, self.options = name, options + ... + ... # Normalize paths and check that their parent + ... # directories exist: + ... paths = [] + ... for path in options['path'].split(): + ... path = os.path.join(buildout['buildout']['directory'], path) + ... if not os.path.isdir(os.path.dirname(path)): + ... logging.getLogger(self.name).error( + ... 'Cannot create %s. %s is not a directory.', + ... options['path'], os.path.dirname(options['path'])) + ... raise zc.buildout.UserError('Invalid Path') + ... paths.append(path) + ... options['path'] = ' '.join(paths) + ... + ... def install(self): + ... paths = self.options['path'].split() + ... for path in paths: + ... logging.getLogger(self.name).info( + ... 'Creating directory %s', os.path.basename(path)) + ... os.mkdir(path) + ... return paths + ... + ... def update(self): + ... pass + ... """) + +If there is an error creating a path, the install method will exit and +leave previously created paths in place: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = foo bin + ... """) + + >>> print system(buildout), # doctest: +ELLIPSIS + Develop: '/sample-buildout/recipes' + Uninstalling data-dir. + Installing data-dir. + data-dir: Creating directory foo + data-dir: Creating directory bin + While: + Installing data-dir. + + An internal error occurred due to a bug in either zc.buildout or in a + recipe being used: + Traceback (most recent call last): + ... + OSError: [Errno 17] File exists: '/sample-buildout/bin' + +We meant to create a directory bins, but typed bin. Now foo was +left behind. + + >>> os.path.exists('foo') + True + +If we fix the typo: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = foo bins + ... """) + + >>> print system(buildout), # doctest: +ELLIPSIS + Develop: '/sample-buildout/recipes' + Installing data-dir. + data-dir: Creating directory foo + While: + Installing data-dir. + + An internal error occurred due to a bug in either zc.buildout or in a + recipe being used: + Traceback (most recent call last): + ... + OSError: [Errno 17] File exists: '/sample-buildout/foo' + +Now they fail because foo exists, because it was left behind. + + >>> remove('foo') + +Let's fix the recipe: + + >>> write(sample_buildout, 'recipes', 'mkdir.py', + ... """ + ... import logging, os, zc.buildout + ... + ... class Mkdir: + ... + ... def __init__(self, buildout, name, options): + ... self.name, self.options = name, options + ... + ... # Normalize paths and check that their parent + ... # directories exist: + ... paths = [] + ... for path in options['path'].split(): + ... path = os.path.join(buildout['buildout']['directory'], path) + ... if not os.path.isdir(os.path.dirname(path)): + ... logging.getLogger(self.name).error( + ... 'Cannot create %s. %s is not a directory.', + ... options['path'], os.path.dirname(options['path'])) + ... raise zc.buildout.UserError('Invalid Path') + ... paths.append(path) + ... options['path'] = ' '.join(paths) + ... + ... def install(self): + ... paths = self.options['path'].split() + ... created = [] + ... try: + ... for path in paths: + ... logging.getLogger(self.name).info( + ... 'Creating directory %s', os.path.basename(path)) + ... os.mkdir(path) + ... created.append(path) + ... except: + ... for d in created: + ... os.rmdir(d) + ... raise + ... + ... return paths + ... + ... def update(self): + ... pass + ... """) + +And put back the typo: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = foo bin + ... """) + +When we rerun the buildout: + + >>> print system(buildout), # doctest: +ELLIPSIS + Develop: '/sample-buildout/recipes' + Installing data-dir. + data-dir: Creating directory foo + data-dir: Creating directory bin + While: + Installing data-dir. + + An internal error occurred due to a bug in either zc.buildout or in a + recipe being used: + Traceback (most recent call last): + ... + OSError: [Errno 17] File exists: '/sample-buildout/bin' + +.. Wait for the file to really disappear. My linux is weird. + + >>> wait_until("foo goes away", lambda : not os.path.exists('foo'), + ... timeout=200) + +we get the same error, but we don't get the directory left behind: + + >>> os.path.exists('foo') + False + +It's critical that recipes clean up partial effects when errors +occur. Because recipes most commonly create files and directories, +buildout provides a helper API for removing created files when an +error occurs. Option objects have a created method that can be called +to record files as they are created. If the install or update method +returns with an error, then any registered paths are removed +automatically. The method returns the files registered and can be +used to return the files created. Let's use this API to simplify the +recipe: + + >>> write(sample_buildout, 'recipes', 'mkdir.py', + ... """ + ... import logging, os, zc.buildout + ... + ... class Mkdir: + ... + ... def __init__(self, buildout, name, options): + ... self.name, self.options = name, options + ... + ... # Normalize paths and check that their parent + ... # directories exist: + ... paths = [] + ... for path in options['path'].split(): + ... path = os.path.join(buildout['buildout']['directory'], path) + ... if not os.path.isdir(os.path.dirname(path)): + ... logging.getLogger(self.name).error( + ... 'Cannot create %s. %s is not a directory.', + ... options['path'], os.path.dirname(options['path'])) + ... raise zc.buildout.UserError('Invalid Path') + ... paths.append(path) + ... options['path'] = ' '.join(paths) + ... + ... def install(self): + ... paths = self.options['path'].split() + ... for path in paths: + ... logging.getLogger(self.name).info( + ... 'Creating directory %s', os.path.basename(path)) + ... os.mkdir(path) + ... self.options.created(path) + ... + ... return self.options.created() + ... + ... def update(self): + ... pass + ... """) + +.. + + >>> remove(sample_buildout, 'recipes', 'mkdir.pyc') + +We returned by calling created, taking advantage of the fact that it +returns the registered paths. We did this for illustrative purposes. +It would be simpler just to return the paths as before. + +If we rerun the buildout, again, we'll get the error and no +directories will be created: + + >>> print system(buildout), # doctest: +ELLIPSIS + Develop: '/sample-buildout/recipes' + Installing data-dir. + data-dir: Creating directory foo + data-dir: Creating directory bin + While: + Installing data-dir. + + An internal error occurred due to a bug in either zc.buildout or in a + recipe being used: + Traceback (most recent call last): + ... + OSError: [Errno 17] File exists: '/sample-buildout/bin' + + >>> os.path.exists('foo') + False + +Now, we'll fix the typo again and we'll get the directories we expect: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = foo bins + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Installing data-dir. + data-dir: Creating directory foo + data-dir: Creating directory bins + + >>> os.path.exists('foo') + True + >>> os.path.exists('bins') + True + +Configuration file syntax +------------------------- + +As mentioned earlier, buildout configuration files use the format +defined by the Python ConfigParser module with extensions. The +extensions are: + +- option names are case sensitive + +- option values can use a substitution syntax, described below, to + refer to option values in specific sections. + +- option values can be appended or removed using the - and + + operators. + +The ConfigParser syntax is very flexible. Section names can contain +any characters other than newlines and right square braces ("]"). +Option names can contain any characters other than newlines, colons, +and equal signs, can not start with a space, and don't include +trailing spaces. + +It is likely that, in the future, some characters will be given +special buildout-defined meanings. This is already true of the +characters ":", "$", "%", "(", and ")". For now, it is a good idea to +keep section and option names simple, sticking to alphanumeric +characters, hyphens, and periods. + +Annotated sections +------------------ + +When used with the `annotate` command, buildout displays annotated sections. +All sections are displayed, sorted alphabetically. For each section, +all key-value pairs are displayed, sorted alphabetically, along with +the origin of the value (file name or COMPUTED_VALUE, DEFAULT_VALUE, +COMMAND_LINE_VALUE). + + >>> print system(buildout+ ' annotate'), + ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + + Annotated sections + ================== + + [buildout] + accept-buildout-test-releases= false + DEFAULT_VALUE + allow-hosts= * + DEFAULT_VALUE + allow-picked-versions= true + DEFAULT_VALUE + allowed-eggs-from-site-packages= * + DEFAULT_VALUE + bin-directory= bin + DEFAULT_VALUE + develop= recipes + /sample-buildout/buildout.cfg + develop-eggs-directory= develop-eggs + DEFAULT_VALUE + directory= /sample-buildout + COMPUTED_VALUE + eggs-directory= eggs + DEFAULT_VALUE + exec-sitecustomize= true + DEFAULT_VALUE + executable= ... + DEFAULT_VALUE + find-links= + DEFAULT_VALUE + include-site-packages= true + DEFAULT_VALUE + install-from-cache= false + DEFAULT_VALUE + installed= .installed.cfg + DEFAULT_VALUE + log-format= + DEFAULT_VALUE + log-level= INFO + DEFAULT_VALUE + newest= true + DEFAULT_VALUE + offline= false + DEFAULT_VALUE + parts= data-dir + /sample-buildout/buildout.cfg + parts-directory= parts + DEFAULT_VALUE + prefer-final= false + DEFAULT_VALUE + python= buildout + DEFAULT_VALUE + relative-paths= false + DEFAULT_VALUE + socket-timeout= + DEFAULT_VALUE + unzip= false + DEFAULT_VALUE + use-dependency-links= true + DEFAULT_VALUE + + [data-dir] + path= foo bins + /sample-buildout/buildout.cfg + recipe= recipes:mkdir + /sample-buildout/buildout.cfg + + +Variable substitutions +---------------------- + +Buildout configuration files support variable substitution. +To illustrate this, we'll create an debug recipe to +allow us to see interactions with the buildout: + + >>> write(sample_buildout, 'recipes', 'debug.py', + ... """ + ... class Debug: + ... + ... def __init__(self, buildout, name, options): + ... self.buildout = buildout + ... self.name = name + ... self.options = options + ... + ... def install(self): + ... items = self.options.items() + ... items.sort() + ... for option, value in items: + ... print option, value + ... return () + ... + ... update = install + ... """) + +This recipe doesn't actually create anything. The install method +doesn't return anything, because it didn't create any files or +directories. + +We also have to update our setup script: + + >>> write(sample_buildout, 'recipes', 'setup.py', + ... """ + ... from setuptools import setup + ... entry_points = ( + ... ''' + ... [zc.buildout] + ... mkdir = mkdir:Mkdir + ... debug = debug:Debug + ... ''') + ... setup(name="recipes", entry_points=entry_points) + ... """) + +We've rearranged the script a bit to make the entry points easier to +edit. In particular, entry points are now defined as a configuration +string, rather than a dictionary. + +Let's update our configuration to provide variable substitution +examples: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir debug + ... log-level = INFO + ... + ... [debug] + ... recipe = recipes:debug + ... File 1 = ${data-dir:path}/file + ... File 2 = ${debug:File 1}/log + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mydata + ... """) + +We used a string-template substitution for File 1 and File 2. This +type of substitution uses the string.Template syntax. Names +substituted are qualified option names, consisting of a section name +and option name joined by a colon. + +Now, if we run the buildout, we'll see the options with the values +substituted. + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling data-dir. + Installing data-dir. + data-dir: Creating directory mydata + Installing debug. + File 1 /sample-buildout/mydata/file + File 2 /sample-buildout/mydata/file/log + recipe recipes:debug + +Note that the substitution of the data-dir path option reflects the +update to the option performed by the mkdir recipe. + +It might seem surprising that mydata was created again. This is +because we changed our recipes package by adding the debug module. +The buildout system didn't know if this module could effect the mkdir +recipe, so it assumed it could and reinstalled mydata. If we rerun +the buildout: + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Updating data-dir. + Updating debug. + File 1 /sample-buildout/mydata/file + File 2 /sample-buildout/mydata/file/log + recipe recipes:debug + +We can see that mydata was not recreated. + +Note that, in this case, we didn't specify a log level, so +we didn't get output about what the buildout was doing. + +Section and option names in variable substitutions are only allowed to +contain alphanumeric characters, hyphens, periods and spaces. This +restriction might be relaxed in future releases. + +We can ommit the section name in a variable substitution to refer to +the current section. We can also use the special option, +_buildout_section_name_ to get the current section name. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = data-dir debug + ... log-level = INFO + ... + ... [debug] + ... recipe = recipes:debug + ... File 1 = ${data-dir:path}/file + ... File 2 = ${:File 1}/log + ... my_name = ${:_buildout_section_name_} + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mydata + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Updating data-dir. + Installing debug. + File 1 /sample-buildout/mydata/file + File 2 /sample-buildout/mydata/file/log + my_name debug + recipe recipes:debug + +Automatic part selection and ordering +------------------------------------- + +When a section with a recipe is referred to, either through variable +substitution or by an initializing recipe, the section is treated as a +part and added to the part list before the referencing part. For +example, we can leave data-dir out of the parts list: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... log-level = INFO + ... + ... [debug] + ... recipe = recipes:debug + ... File 1 = ${data-dir:path}/file + ... File 2 = ${debug:File 1}/log + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mydata + ... """) + + +It will still be treated as a part: + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Updating data-dir. + Installing debug. + File 1 /sample-buildout/mydata/file + File 2 /sample-buildout/mydata/file/log + recipe recipes:debug + + >>> cat('.installed.cfg') # doctest: +ELLIPSIS + [buildout] + installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link + parts = data-dir debug + ... + +Note that the data-dir part is included *before* the debug part, +because the debug part refers to the data-dir part. Even if we list +the data-dir part after the debug part, it will be included before: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug data-dir + ... log-level = INFO + ... + ... [debug] + ... recipe = recipes:debug + ... File 1 = ${data-dir:path}/file + ... File 2 = ${debug:File 1}/log + ... + ... [data-dir] + ... recipe = recipes:mkdir + ... path = mydata + ... """) + + +It will still be treated as a part: + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Updating data-dir. + Updating debug. + File 1 /sample-buildout/mydata/file + File 2 /sample-buildout/mydata/file/log + recipe recipes:debug + + >>> cat('.installed.cfg') # doctest: +ELLIPSIS + [buildout] + installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link + parts = data-dir debug + ... + +Extending sections (macros) +--------------------------- + +A section (other than the buildout section) can extend one or more +other sections using the ``<=`` option. Options from the referenced +sections are copied to the refering section *before* variable +substitution. This, together with the ability to refer to variables +of the current section allows sections to be used as macros. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = myfiles + ... log-level = INFO + ... + ... [debug] + ... recipe = recipes:debug + ... + ... [with_file1] + ... <= debug + ... file1 = ${:path}/file1 + ... color = red + ... + ... [with_file2] + ... <= debug + ... file2 = ${:path}/file2 + ... color = blue + ... + ... [myfiles] + ... <= with_file1 + ... with_file2 + ... path = mydata + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Uninstalling data-dir. + Installing myfiles. + color blue + file1 mydata/file1 + file2 mydata/file2 + path mydata + recipe recipes:debug + +In this example, the debug, with_file1 and with_file2 sections act as +macros. In particular, the variable substitutions are performed +relative to the myfiles section. + +Adding and removing options +--------------------------- + +We can append and remove values to an option by using the + and - +operators. + +This is illustrated below; first we define a base configuration. + + >>> write(sample_buildout, 'base.cfg', + ... """ + ... [buildout] + ... parts = part1 part2 part3 + ... + ... [part1] + ... recipe = + ... option = a1 a2 + ... + ... [part2] + ... recipe = + ... option = b1 b2 b3 b4 + ... + ... [part3] + ... recipe = + ... option = c1 c2 + ... + ... """) + +Extending this configuration, we can "adjust" the values set in the +base configuration file. + + >>> write(sample_buildout, 'extension1.cfg', + ... """ + ... [buildout] + ... extends = base.cfg + ... + ... # appending values + ... [part1] + ... option += a3 a4 + ... + ... # removing values + ... [part2] + ... option -= b1 b2 + ... + ... # alt. spelling + ... [part3] + ... option+=c3 c4 c5 + ... + ... # normal assignment + ... [part4] + ... option = h1 h2 + ... + ... """) + +An additional extension. + + >>> write(sample_buildout, 'extension2.cfg', + ... """ + ... [buildout] + ... extends = extension1.cfg + ... + ... # appending values + ... [part1] + ... option += a5 + ... + ... # removing values + ... [part2] + ... option -= b1 b2 b3 + ... + ... """) + +To verify that the options are adjusted correctly, we'll set up an +extension that prints out the options. + + >>> mkdir(sample_buildout, 'demo') + >>> write(sample_buildout, 'demo', 'demo.py', + ... """ + ... def ext(buildout): + ... print [part['option'] for name, part in buildout.items() \ + ... if name.startswith('part')] + ... """) + + >>> write(sample_buildout, 'demo', 'setup.py', + ... """ + ... from setuptools import setup + ... + ... setup( + ... name="demo", + ... entry_points={'zc.buildout.extension': ['ext = demo:ext']}, + ... ) + ... """) + +Set up a buildout configuration for this extension. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = demo + ... parts = + ... """) + + >>> os.chdir(sample_buildout) + >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')), + Develop: '/sample-buildout/demo' + Uninstalling myfiles. + Getting distribution for 'recipes'. + zip_safe flag not set; analyzing archive contents... + Got recipes 0.0.0. + warning: install_lib: 'build/lib' does not exist -- no Python modules to install + +Verify option values. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = demo + ... extensions = demo + ... extends = extension2.cfg + ... """) + + >>> print system(os.path.join('bin', 'buildout')), + ['a1 a2/na3 a4/na5', 'b1 b2 b3 b4', 'c1 c2/nc3 c4 c5', 'h1 h2'] + Develop: '/sample-buildout/demo' + +Annotated sections output shows which files are responsible for which +operations. + + >>> print system(os.path.join('bin', 'buildout') + ' annotate'), + ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + + Annotated sections + ================== + ... + + [part1] + option= a1 a2 + a3 a4 + a5 + /sample-buildout/base.cfg + += /sample-buildout/extension1.cfg + += /sample-buildout/extension2.cfg + recipe= + /sample-buildout/base.cfg + + [part2] + option= b1 b2 b3 b4 + /sample-buildout/base.cfg + -= /sample-buildout/extension1.cfg + -= /sample-buildout/extension2.cfg + recipe= + /sample-buildout/base.cfg + + [part3] + option= c1 c2 + c3 c4 c5 + /sample-buildout/base.cfg + += /sample-buildout/extension1.cfg + recipe= + /sample-buildout/base.cfg + + [part4] + option= h1 h2 + /sample-buildout/extension1.cfg + +Cleanup. + + >>> os.remove(os.path.join(sample_buildout, 'base.cfg')) + >>> os.remove(os.path.join(sample_buildout, 'extension1.cfg')) + >>> os.remove(os.path.join(sample_buildout, 'extension2.cfg')) + +Multiple configuration files +---------------------------- + +A configuration file can "extend" another configuration file. +Options are read from the other configuration file if they aren't +already defined by your configuration file. + +The configuration files your file extends can extend +other configuration files. The same file may be +used more than once although, of course, cycles aren't allowed. + +To see how this works, we use an example: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... extends = base.cfg + ... + ... [debug] + ... op = buildout + ... """) + + >>> write(sample_buildout, 'base.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... + ... [debug] + ... recipe = recipes:debug + ... op = base + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Installing debug. + op buildout + recipe recipes:debug + +The example is pretty trivial, but the pattern it illustrates is +pretty common. In a more practical example, the base buildout might +represent a product and the extending buildout might be a +customization. + +Here is a more elaborate example. + + >>> other = tmpdir('other') + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... extends = b1.cfg b2.cfg %(b3)s + ... + ... [debug] + ... op = buildout + ... """ % dict(b3=os.path.join(other, 'b3.cfg'))) + + >>> write(sample_buildout, 'b1.cfg', + ... """ + ... [buildout] + ... extends = base.cfg + ... + ... [debug] + ... op1 = b1 1 + ... op2 = b1 2 + ... """) + + >>> write(sample_buildout, 'b2.cfg', + ... """ + ... [buildout] + ... extends = base.cfg + ... + ... [debug] + ... op2 = b2 2 + ... op3 = b2 3 + ... """) + + >>> write(other, 'b3.cfg', + ... """ + ... [buildout] + ... extends = b3base.cfg + ... + ... [debug] + ... op4 = b3 4 + ... """) + + >>> write(other, 'b3base.cfg', + ... """ + ... [debug] + ... op5 = b3base 5 + ... """) + + >>> write(sample_buildout, 'base.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... + ... [debug] + ... recipe = recipes:debug + ... name = base + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + name base + op buildout + op1 b1 1 + op2 b2 2 + op3 b2 3 + op4 b3 4 + op5 b3base 5 + recipe recipes:debug + +There are several things to note about this example: + +- We can name multiple files in an extends option. + +- We can reference files recursively. + +- Relative file names in extended options are interpreted relative to + the directory containing the referencing configuration file. + +Loading Configuration from URLs +------------------------------- + +Configuration files can be loaded from URLs. To see how this works, +we'll set up a web server with some configuration files. + + >>> server_data = tmpdir('server_data') + + >>> write(server_data, "r1.cfg", + ... """ + ... [debug] + ... op1 = r1 1 + ... op2 = r1 2 + ... """) + + >>> write(server_data, "r2.cfg", + ... """ + ... [buildout] + ... extends = r1.cfg + ... + ... [debug] + ... op2 = r2 2 + ... op3 = r2 3 + ... """) + + >>> server_url = start_server(server_data) + + >>> write('client.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... extends = %(url)s/r2.cfg + ... + ... [debug] + ... recipe = recipes:debug + ... name = base + ... """ % dict(url=server_url)) + + + >>> print system(buildout+ ' -c client.cfg'), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + name base + op1 r1 1 + op2 r2 2 + op3 r2 3 + recipe recipes:debug + +Here we specified a URL for the file we extended. The file we +downloaded, itself referred to a file on the server using a relative +URL reference. Relative references are interpreted relative to the +base URL when they appear in configuration files loaded via URL. + +We can also specify a URL as the configuration file to be used by a +buildout. + + >>> os.remove('client.cfg') + >>> write(server_data, 'remote.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... extends = r2.cfg + ... + ... [debug] + ... recipe = recipes:debug + ... name = remote + ... """) + + >>> print system(buildout + ' -c ' + server_url + '/remote.cfg'), + While: + Initializing. + Error: Missing option: buildout:directory + +Normally, the buildout directory defaults to directory +containing a configuration file. This won't work for configuration +files loaded from URLs. In this case, the buildout directory would +normally be defined on the command line: + + >>> print system(buildout + ... + ' -c ' + server_url + '/remote.cfg' + ... + ' buildout:directory=' + sample_buildout + ... ), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + name remote + op1 r1 1 + op2 r2 2 + op3 r2 3 + recipe recipes:debug + +User defaults +------------- + +If the file $HOME/.buildout/default.cfg, exists, it is read before +reading the configuration file. ($HOME is the value of the HOME +environment variable. The '/' is replaced by the operating system file +delimiter.) + + >>> old_home = os.environ['HOME'] + >>> home = tmpdir('home') + >>> mkdir(home, '.buildout') + >>> write(home, '.buildout', 'default.cfg', + ... """ + ... [debug] + ... op1 = 1 + ... op7 = 7 + ... """) + + >>> os.environ['HOME'] = home + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + name base + op buildout + op1 b1 1 + op2 b2 2 + op3 b2 3 + op4 b3 4 + op5 b3base 5 + op7 7 + recipe recipes:debug + +A buildout command-line argument, -U, can be used to suppress reading +user defaults: + + >>> print system(buildout + ' -U'), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + name base + op buildout + op1 b1 1 + op2 b2 2 + op3 b2 3 + op4 b3 4 + op5 b3base 5 + recipe recipes:debug + + >>> os.environ['HOME'] = old_home + +Log level +--------- + +We can control the level of logging by specifying a log level in out +configuration file. For example, so suppress info messages, we can +set the logging level to WARNING + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... log-level = WARNING + ... extends = b1.cfg b2.cfg + ... """) + + >>> print system(buildout), + name base + op1 b1 1 + op2 b2 2 + op3 b2 3 + recipe recipes:debug + +Uninstall recipes +----------------- + +As we've seen, when parts are installed, buildout keeps track of files +and directories that they create. When the parts are uninstalled these +files and directories are deleted. + +Sometimes more clean up is needed. For example, a recipe might add a +system service by calling chkconfig --add during installation. Later +during uninstallation, chkconfig --del will need to be called to +remove the system service. + +In order to deal with these uninstallation issues, you can register +uninstall recipes. Uninstall recipes are registered using the +'zc.buildout.uninstall' entry point. Parts specify uninstall recipes +using the 'uninstall' option. + +In comparison to regular recipes, uninstall recipes are much +simpler. They are simply callable objects that accept the name of the +part to be uninstalled and the part's options dictionary. Uninstall +recipes don't have access to the part itself since it maybe not be +able to be instantiated at uninstallation time. + +Here's a recipe that simulates installation of a system service, along +with an uninstall recipe that simulates removing the service. + + >>> write(sample_buildout, 'recipes', 'service.py', + ... """ + ... class Service: + ... + ... def __init__(self, buildout, name, options): + ... self.buildout = buildout + ... self.name = name + ... self.options = options + ... + ... def install(self): + ... print "chkconfig --add %s" % self.options['script'] + ... return () + ... + ... def update(self): + ... pass + ... + ... + ... def uninstall_service(name, options): + ... print "chkconfig --del %s" % options['script'] + ... """) + +To use these recipes we must register them using entry points. Make +sure to use the same name for the recipe and uninstall recipe. This is +required to let buildout know which uninstall recipe goes with which +recipe. + + >>> write(sample_buildout, 'recipes', 'setup.py', + ... """ + ... from setuptools import setup + ... entry_points = ( + ... ''' + ... [zc.buildout] + ... mkdir = mkdir:Mkdir + ... debug = debug:Debug + ... service = service:Service + ... + ... [zc.buildout.uninstall] + ... service = service:uninstall_service + ... ''') + ... setup(name="recipes", entry_points=entry_points) + ... """) + +Here's how these recipes could be used in a buildout: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = service + ... + ... [service] + ... recipe = recipes:service + ... script = /path/to/script + ... """) + +When the buildout is run the service will be installed + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing service. + chkconfig --add /path/to/script + + +The service has been installed. If the buildout is run again with no +changes, the service shouldn't be changed. + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Updating service. + + +Now we change the service part to trigger uninstallation and +re-installation. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = service + ... + ... [service] + ... recipe = recipes:service + ... script = /path/to/a/different/script + ... """) + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Uninstalling service. + Running uninstall recipe. + chkconfig --del /path/to/script + Installing service. + chkconfig --add /path/to/a/different/script + + +Now we remove the service part, and add another part. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... + ... [debug] + ... recipe = recipes:debug + ... """) + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Uninstalling service. + Running uninstall recipe. + chkconfig --del /path/to/a/different/script + Installing debug. + recipe recipes:debug + + +Uninstall recipes don't have to take care of removing all the files +and directories created by the part. This is still done automatically, +following the execution of the uninstall recipe. An upshot is that an +uninstallation recipe can access files and directories created by a +recipe before they are deleted. + +For example, here's an uninstallation recipe that simulates backing up +a directory before it is deleted. It is designed to work with the +mkdir recipe introduced earlier. + + >>> write(sample_buildout, 'recipes', 'backup.py', + ... """ + ... import os + ... def backup_directory(name, options): + ... path = options['path'] + ... size = len(os.listdir(path)) + ... print "backing up directory %s of size %s" % (path, size) + ... """) + +It must be registered with the zc.buildout.uninstall entry +point. Notice how it is given the name 'mkdir' to associate it with +the mkdir recipe. + + >>> write(sample_buildout, 'recipes', 'setup.py', + ... """ + ... from setuptools import setup + ... entry_points = ( + ... ''' + ... [zc.buildout] + ... mkdir = mkdir:Mkdir + ... debug = debug:Debug + ... service = service:Service + ... + ... [zc.buildout.uninstall] + ... uninstall_service = service:uninstall_service + ... mkdir = backup:backup_directory + ... ''') + ... setup(name="recipes", entry_points=entry_points) + ... """) + +Now we can use it with a mkdir part. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = dir debug + ... + ... [dir] + ... recipe = recipes:mkdir + ... path = my_directory + ... + ... [debug] + ... recipe = recipes:debug + ... """) + +Run the buildout to install the part. + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing dir. + dir: Creating directory my_directory + Installing debug. + recipe recipes:debug + + +Now we remove the part from the configuration file. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... + ... [debug] + ... recipe = recipes:debug + ... """) + +When the buildout is run the part is removed, and the uninstall recipe +is run before the directory is deleted. + + >>> print system(buildout) + Develop: '/sample-buildout/recipes' + Uninstalling dir. + Running uninstall recipe. + backing up directory /sample-buildout/my_directory of size 0 + Updating debug. + recipe recipes:debug + + +Now we will return the registration to normal for the benefit of the +rest of the examples. + + >>> write(sample_buildout, 'recipes', 'setup.py', + ... """ + ... from setuptools import setup + ... entry_points = ( + ... ''' + ... [zc.buildout] + ... mkdir = mkdir:Mkdir + ... debug = debug:Debug + ... ''') + ... setup(name="recipes", entry_points=entry_points) + ... """) + + +Command-line usage +------------------ + +A number of arguments can be given on the buildout command line. The +command usage is:: + + buildout [options and assignments] [command [command arguments]] + +The following options are supported: + +-h (or --help) + Print basic usage information. If this option is used, then all + other options are ignored. + +-c filename + The -c option can be used to specify a configuration file, rather than + buildout.cfg in the current directory. + + +-t socket_timeout + + Specify the socket timeout in seconds. + +-v + Increment the verbosity by 10. The verbosity is used to adjust + the logging level. The verbosity is subtracted from the numeric + value of the log-level option specified in the configuration file. + +-q + Decrement the verbosity by 10. + +-U + Don't read user-default configuration. + +-o + Run in off-line mode. This is equivalent to the assignment + buildout:offline=true. + +-O + Run in non-off-line mode. This is equivalent to the assignment + buildout:offline=false. This is the default buildout mode. The + -O option would normally be used to override a true offline + setting in a configuration file. + +-n + Run in newest mode. This is equivalent to the assignment + buildout:newest=true. With this setting, which is the default, + buildout will try to find the newest versions of distributions + available that satisfy its requirements. + +-N + Run in non-newest mode. This is equivalent to the assignment + buildout:newest=false. With this setting, buildout will not seek + new distributions if installed distributions satisfy it's + requirements. + +Assignments are of the form:: + + section_name:option_name=value + +Options and assignments can be given in any order. + +Here's an example: + + >>> write(sample_buildout, 'other.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... installed = .other.cfg + ... log-level = WARNING + ... + ... [debug] + ... name = other + ... recipe = recipes:debug + ... """) + +Note that we used the installed buildout option to specify an +alternate file to store information about installed parts. + + >>> print system(buildout+' -c other.cfg debug:op1=foo -v'), + Develop: '/sample-buildout/recipes' + Installing debug. + name other + op1 foo + recipe recipes:debug + +Here we used the -c option to specify an alternate configuration file, +and the -v option to increase the level of logging from the default, +WARNING. + +Options can also be combined in the usual Unix way, as in: + + >>> print system(buildout+' -vcother.cfg debug:op1=foo'), + Develop: '/sample-buildout/recipes' + Updating debug. + name other + op1 foo + recipe recipes:debug + +Here we combined the -v and -c options with the configuration file +name. Note that the -c option has to be last, because it takes an +argument. + + >>> os.remove(os.path.join(sample_buildout, 'other.cfg')) + >>> os.remove(os.path.join(sample_buildout, '.other.cfg')) + +The most commonly used command is 'install' and it takes a list of +parts to install. if any parts are specified, only those parts are +installed. To illustrate this, we'll update our configuration and run +the buildout in the usual way: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug d1 d2 d3 + ... + ... [d1] + ... recipe = recipes:mkdir + ... path = d1 + ... + ... [d2] + ... recipe = recipes:mkdir + ... path = d2 + ... + ... [d3] + ... recipe = recipes:mkdir + ... path = d3 + ... + ... [debug] + ... recipe = recipes:debug + ... """) + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling debug. + Installing debug. + recipe recipes:debug + Installing d1. + d1: Creating directory d1 + Installing d2. + d2: Creating directory d2 + Installing d3. + d3: Creating directory d3 + + >>> ls(sample_buildout) + - .installed.cfg + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d d1 + d d2 + d d3 + d demo + d develop-eggs + d eggs + d parts + d recipes + + >>> cat(sample_buildout, '.installed.cfg') + ... # doctest: +NORMALIZE_WHITESPACE + [buildout] + installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link + parts = debug d1 d2 d3 + + [debug] + __buildout_installed__ = + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + recipe = recipes:debug + + [d1] + __buildout_installed__ = /sample-buildout/d1 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/d1 + recipe = recipes:mkdir + + [d2] + __buildout_installed__ = /sample-buildout/d2 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/d2 + recipe = recipes:mkdir + + [d3] + __buildout_installed__ = /sample-buildout/d3 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/d3 + recipe = recipes:mkdir + +Now we'll update our configuration file: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug d2 d3 d4 + ... + ... [d2] + ... recipe = recipes:mkdir + ... path = data2 + ... + ... [d3] + ... recipe = recipes:mkdir + ... path = data3 + ... + ... [d4] + ... recipe = recipes:mkdir + ... path = ${d2:path}-extra + ... + ... [debug] + ... recipe = recipes:debug + ... x = 1 + ... """) + +and run the buildout specifying just d3 and d4: + + >>> print system(buildout+' install d3 d4'), + Develop: '/sample-buildout/recipes' + Uninstalling d3. + Installing d3. + d3: Creating directory data3 + Installing d4. + d4: Creating directory data2-extra + + >>> ls(sample_buildout) + - .installed.cfg + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d d1 + d d2 + d data2-extra + d data3 + d demo + d develop-eggs + d eggs + d parts + d recipes + +Only the d3 and d4 recipes ran. d3 was removed and data3 and data2-extra +were created. + +The .installed.cfg is only updated for the recipes that ran: + + >>> cat(sample_buildout, '.installed.cfg') + ... # doctest: +NORMALIZE_WHITESPACE + [buildout] + installed_develop_eggs = /sample-buildout/develop-eggs/recipes.egg-link + parts = debug d1 d2 d3 d4 + + [debug] + __buildout_installed__ = + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + recipe = recipes:debug + + [d1] + __buildout_installed__ = /sample-buildout/d1 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/d1 + recipe = recipes:mkdir + + [d2] + __buildout_installed__ = /sample-buildout/d2 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/d2 + recipe = recipes:mkdir + + [d3] + __buildout_installed__ = /sample-buildout/data3 + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/data3 + recipe = recipes:mkdir + + [d4] + __buildout_installed__ = /sample-buildout/data2-extra + __buildout_signature__ = recipes-PiIFiO8ny5yNZ1S3JfT0xg== + path = /sample-buildout/data2-extra + recipe = recipes:mkdir + +Note that the installed data for debug, d1, and d2 haven't changed, +because we didn't install those parts and that the d1 and d2 +directories are still there. + +Now, if we run the buildout without the install command: + + >>> print system(buildout), + Develop: '/sample-buildout/recipes' + Uninstalling d2. + Uninstalling d1. + Uninstalling debug. + Installing debug. + recipe recipes:debug + x 1 + Installing d2. + d2: Creating directory data2 + Updating d3. + Updating d4. + +We see the output of the debug recipe and that data2 was created. We +also see that d1 and d2 have gone away: + + >>> ls(sample_buildout) + - .installed.cfg + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d data2 + d data2-extra + d data3 + d demo + d develop-eggs + d eggs + d parts + d recipes + +Alternate directory and file locations +-------------------------------------- + +The buildout normally puts the bin, eggs, and parts directories in the +directory in the directory containing the configuration file. You can +provide alternate locations, and even names for these directories. + + >>> alt = tmpdir('sample-alt') + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = + ... develop-eggs-directory = %(developbasket)s + ... eggs-directory = %(basket)s + ... bin-directory = %(scripts)s + ... parts-directory = %(work)s + ... """ % dict( + ... developbasket = os.path.join(alt, 'developbasket'), + ... basket = os.path.join(alt, 'basket'), + ... scripts = os.path.join(alt, 'scripts'), + ... work = os.path.join(alt, 'work'), + ... )) + + >>> print system(buildout), + Creating directory '/sample-alt/scripts'. + Creating directory '/sample-alt/work'. + Creating directory '/sample-alt/basket'. + Creating directory '/sample-alt/developbasket'. + Develop: '/sample-buildout/recipes' + Uninstalling d4. + Uninstalling d3. + Uninstalling d2. + Uninstalling debug. + + >>> ls(alt) + d basket + d developbasket + d scripts + d work + + >>> ls(alt, 'developbasket') + - recipes.egg-link + +You can also specify an alternate buildout directory: + + >>> rmdir(alt) + >>> alt = tmpdir('sample-alt') + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... directory = %(alt)s + ... develop = %(recipes)s + ... parts = + ... """ % dict( + ... alt=alt, + ... recipes=os.path.join(sample_buildout, 'recipes'), + ... )) + + >>> print system(buildout), + Creating directory '/sample-alt/bin'. + Creating directory '/sample-alt/parts'. + Creating directory '/sample-alt/eggs'. + Creating directory '/sample-alt/develop-eggs'. + Develop: '/sample-buildout/recipes' + + >>> ls(alt) + - .installed.cfg + d bin + d develop-eggs + d eggs + d parts + + >>> ls(alt, 'develop-eggs') + - recipes.egg-link + +Logging control +--------------- + +Three buildout options are used to control logging: + +log-level + specifies the log level + +verbosity + adjusts the log level + +log-format + allows an alternate logging for mat to be specified + +We've already seen the log level and verbosity. Let's look at an example +of changing the format: + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = + ... log-level = 25 + ... verbosity = 5 + ... log-format = %(levelname)s %(message)s + ... """) + +Here, we've changed the format to include the log-level name, rather +than the logger name. + +We've also illustrated, with a contrived example, that the log level +can be a numeric value and that the verbosity can be specified in the +configuration file. Because the verbosity is subtracted from the log +level, we get a final log level of 20, which is the INFO level. + + >>> print system(buildout), + INFO Develop: '/sample-buildout/recipes' + +Predefined buildout options +--------------------------- + +Buildouts have a number of predefined options that recipes can use +and that users can override in their configuration files. To see +these, we'll run a minimal buildout configuration with a debug logging +level. One of the features of debug logging is that the configuration +database is shown. + + >>> write(sample_buildout, 'buildout.cfg', + ... """ + ... [buildout] + ... parts = + ... """) + + >>> print system(buildout+' -vv'), # doctest: +NORMALIZE_WHITESPACE + Installing 'zc.buildout', 'setuptools'. + We have a develop egg: zc.buildout X.X. + We have the best distribution that satisfies 'setuptools'. + Picked: setuptools = V.V + + Configuration data: + [buildout] + accept-buildout-test-releases = false + allow-hosts = * + allow-picked-versions = true + allowed-eggs-from-site-packages = * + bin-directory = /sample-buildout/bin + develop-eggs-directory = /sample-buildout/develop-eggs + directory = /sample-buildout + eggs-directory = /sample-buildout/eggs + exec-sitecustomize = true + executable = python + find-links = + include-site-packages = true + install-from-cache = false + installed = /sample-buildout/.installed.cfg + log-format = + log-level = INFO + newest = true + offline = false + parts = + parts-directory = /sample-buildout/parts + prefer-final = false + python = buildout + relative-paths = false + socket-timeout = + unzip = false + use-dependency-links = true + verbosity = 20 + + +All of these options can be overridden by configuration files or by +command-line assignments. We've discussed most of these options +already, but let's review them and touch on some we haven't discussed: + +allowed-eggs-from-site-packages + Sometimes you need or want to control what eggs from site-packages are + used. The allowed-eggs-from-site-packages option allows you to specify a + whitelist of project names that may be included from site-packages. You + can use globs to specify the value. It defaults to a single value of '*', + indicating that any package may come from site-packages. + + Here's a usage example:: + + [buildout] + ... + + allowed-eggs-from-site-packages = + demo + bigdemo + zope.* + + This option interacts with the ``include-site-packages`` option in the + following ways. + + If ``include-site-packages`` is true, then + ``allowed-eggs-from-site-packages`` filters what eggs from site-packages + may be chosen. Therefore, if ``allowed-eggs-from-site-packages`` is an + empty list, then no eggs from site-packages are chosen, but site-packages + will still be included at the end of path lists. + + If ``include-site-packages`` is false, the value of + ``allowed-eggs-from-site-packages`` is irrelevant. + + See the ``include-site-packages`` description for more information. + +bin-directory + The directory path where scripts are written. This can be a + relative path, which is interpreted relative to the directory + option. + +develop-eggs-directory + The directory path where development egg links are created for software + being created in the local project. This can be a relative path, + which is interpreted relative to the directory option. + +directory + The buildout directory. This is the base for other buildout file + and directory locations, when relative locations are used. + +eggs-directory + The directory path where downloaded eggs are put. It is common to share + this directory across buildouts. Eggs in this directory should + *never* be modified. This can be a relative path, which is + interpreted relative to the directory option. + +exec-sitecustomize + Normally the Python's real sitecustomize module is processed. + If you do not want it to be processed in order to increase the + repeatability of your buildout, set this value to 'false'. This will + be honored irrespective of the setting for include-site-packages. + This option will be honored by some recipes and not others. + z3c.recipe.scripts honors this and zc.recipe.egg does not, for + instance. + +executable + The Python executable used to run the buildout. See the python + option below. + +include-site-packages + You can choose not to have the site-packages of the underlying Python + available to your script or interpreter, in addition to the packages + from your eggs. This can increase repeatability for your buildout. + This option will be better used by some recipes than others. + z3c.recipe.scripts honors this fully and zc.recipe.egg only + partially, for instance. + +installed + The file path where information about the results of the previous + buildout run is written. This can be a relative path, which is + interpreted relative to the directory option. This file provides + an inventory of installed parts with information needed to decide + which if any parts need to be uninstalled. + +log-format + The format used for logging messages. + +log-level + The log level before verbosity adjustment + +parts + A white space separated list of parts to be installed. + +parts-directory + A working directory that parts can used to store data. + +python + The name of a section containing information about the default + Python interpreter. Recipes that need a installation + typically have options to tell them which Python installation to + use. By convention, if a section-specific option isn't used, the + option is looked for in the buildout section. The option must + point to a section with an executable option giving the path to a + Python executable. By default, the buildout section defines the + default Python as the Python used to run the buildout. + +relative-paths + The paths generated by zc.buildout are absolute by default, and this + option is ``false``. However, if you set this value to be ``true``, + bin/buildout will be generated with code that makes the paths relative. + Some recipes, such as zc.recipe.egg and z3c.recipe.scripts, honor this + value as well. + +unzip + By default, zc.buildout doesn't unzip zip-safe eggs ("unzip = false"). + This follows the policy followed by setuptools itself. Experience shows + this policy to to be inconvenient. Zipped eggs make debugging more + difficult and often import more slowly. You can include an unzip option in + the buildout section to change the default unzipping policy ("unzip = + true"). + +use-dependency-links + By default buildout will obey the setuptools dependency_links metadata + when it looks for dependencies. This behavior can be controlled with + the use-dependency-links buildout option:: + + [buildout] + ... + use-dependency-links = false + + The option defaults to true. If you set it to false, then dependency + links are only looked for in the locations specified by find-links. + +verbosity + A log-level adjustment. Typically, this is set via the -q and -v + command-line options. + + +Creating new buildouts and bootstrapping +---------------------------------------- + +If zc.buildout is installed, you can use it to create a new buildout +with it's own local copies of zc.buildout and setuptools and with +local buildout scripts. + + >>> sample_bootstrapped = tmpdir('sample-bootstrapped') + + >>> print system(buildout + ... +' -c'+os.path.join(sample_bootstrapped, 'setup.cfg') + ... +' init'), + Creating '/sample-bootstrapped/setup.cfg'. + Creating directory '/sample-bootstrapped/bin'. + Creating directory '/sample-bootstrapped/parts'. + Creating directory '/sample-bootstrapped/eggs'. + Creating directory '/sample-bootstrapped/develop-eggs'. + Generated script '/sample-bootstrapped/bin/buildout'. + +Note that a basic setup.cfg was created for us. + + >>> ls(sample_bootstrapped) + d bin + d develop-eggs + d eggs + d parts + - setup.cfg + + >>> ls(sample_bootstrapped, 'bin') + - buildout + + >>> _ = (ls(sample_bootstrapped, 'eggs'), + ... ls(sample_bootstrapped, 'develop-eggs')) + - setuptools-0.6-py2.3.egg + - zc.buildout-1.0-py2.3.egg + +(We list both the eggs and develop-eggs directories because the +buildout or setuptools egg could be installed in the develop-eggs +directory if the original buildout had develop eggs for either +buildout or setuptools.) + +If relative-paths is ``true``, the buildout script uses relative paths. + + >>> write(sample_bootstrapped, 'setup.cfg', + ... ''' + ... [buildout] + ... relative-paths = true + ... parts = + ... ''') + + >>> print system(buildout + ... +' -c'+os.path.join(sample_bootstrapped, 'setup.cfg') + ... +' bootstrap'), + Generated script '/sample-bootstrapped/bin/buildout'. + + >>> buildout_script = join(sample_bootstrapped, 'bin', 'buildout') + >>> import sys + >>> if sys.platform.startswith('win'): + ... buildout_script += '-script.py' + >>> print open(buildout_script).read() # doctest: +ELLIPSIS + #!... -S + + import os + + join = os.path.join + base = os.path.dirname(os.path.abspath(os.path.realpath(__file__))) + base = os.path.dirname(base) + + import sys + sys.path[0:0] = [ + join(base, 'parts/buildout'), + ] + + + import os + path = sys.path[0] + if os.environ.get('PYTHONPATH'): + path = os.pathsep.join([path, os.environ['PYTHONPATH']]) + os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = path + import site # imports custom buildout-generated site.py + + import zc.buildout.buildout + + if __name__ == '__main__': + zc.buildout.buildout.main() + + + +Note that, in the above two examples, the buildout script was installed +but not run. To run the buildout, we'd have to run the installed +buildout script. + +If we have an existing buildout that already has a buildout.cfg, we'll +normally use the bootstrap command instead of init. It will complain +if there isn't a configuration file: + + >>> sample_bootstrapped2 = tmpdir('sample-bootstrapped2') + + >>> print system(buildout + ... +' -c'+os.path.join(sample_bootstrapped2, 'setup.cfg') + ... +' bootstrap'), + While: + Initializing. + Error: Couldn't open /sample-bootstrapped2/setup.cfg + + >>> write(sample_bootstrapped2, 'setup.cfg', + ... """ + ... [buildout] + ... parts = + ... """) + + >>> print system(buildout + ... +' -c'+os.path.join(sample_bootstrapped2, 'setup.cfg') + ... +' bootstrap'), + Creating directory '/sample-bootstrapped2/bin'. + Creating directory '/sample-bootstrapped2/parts'. + Creating directory '/sample-bootstrapped2/eggs'. + Creating directory '/sample-bootstrapped2/develop-eggs'. + Generated script '/sample-bootstrapped2/bin/buildout'. + + +Newest and Offline Modes +------------------------ + +By default buildout and recipes will try to find the newest versions +of distributions needed to satisfy requirements. This can be very +time consuming, especially when incrementally working on setting up a +buildout or working on a recipe. The buildout newest option can be +used to to suppress this. If the newest option is set to false, then +new distributions won't be sought if an installed distribution meets +requirements. The newest option can be set to false using the -N +command-line option. + +The offline option goes a bit further. If the buildout offline option +is given a value of "true", the buildout and recipes that are aware of +the option will avoid doing network access. This is handy when +running the buildout when not connected to the internet. It also +makes buildouts run much faster. This option is typically set using +the buildout -o option. + +Preferring Final Releases +------------------------- + +Currently, when searching for new releases of your project's +dependencies, the newest available release is used. This isn't usually +ideal, as you may get a development release or alpha releases not ready +to be widely used. You can request that final releases be preferred +using the ``prefer-final`` option in the buildout section:: + + [buildout] + ... + prefer-final = true + +When the ``prefer-final`` option is set to true, then when searching for +new releases, final releases are preferred. If there are final +releases that satisfy distribution requirements, then those releases +are used even if newer non-final releases are available. + +In buildout version 2, all final releases will be preferred by +default--that is ``prefer-final`` will also default to 'true'. You will +then need to use a 'false' value for ``prefer-final`` to get the newest +releases. + +A separate option controls the behavior of the build system itself. +When buildout looks for recipes, extensions, and for updates to itself, +it does prefer final releases by default, as of the 1.5.0 release. The +``accept-buildout-test-releases`` option will let you override this behavior. +However, it is typically changed by the --accept-buildout-test-releases +option to the bootstrap script, since bootstrapping is the first step to +selecting a buildout. + +Finding distributions +--------------------- + +By default, buildout searches the Python Package Index when looking +for distributions. You can, instead, specify your own index to search +using the `index` option:: + + [buildout] + ... + index = http://index.example.com/ + +This index, or the default of http://pypi.python.org/simple/ if no +index is specified, will always be searched for distributions unless +running buildout with options that prevent searching for +distributions. The latest version of the distribution that meets the +requirements of the buildout will always be used. + +You can also specify more locations to search for distributions using +the `find-links` option. All locations specified will be searched for +distributions along with the package index as described before. + +Locations can be urls:: + + [buildout] + ... + find-links = http://download.zope.org/distribution/ + +They can also be directories on disk:: + + [buildout] + ... + find-links = /some/path + +Finally, they can also be direct paths to distributions:: + + [buildout] + ... + find-links = /some/path/someegg-1.0.0-py2.3.egg + +Any number of locations can be specified in the `find-links` option:: + + [buildout] + ... + find-links = + http://download.zope.org/distribution/ + /some/otherpath + /some/path/someegg-1.0.0-py2.3.egg + +Dependency links +---------------- + +By default buildout will obey the setuptools dependency_links metadata +when it looks for dependencies. This behavior can be controlled with +the use-dependency-links buildout option:: + + [buildout] + ... + use-dependency-links = false + +The option defaults to true. If you set it to false, then dependency +links are only looked for in the locations specified by find-links. + +Controlling the installation database +------------------------------------- + +The buildout installed option is used to specify the file used to save +information on installed parts. This option is initialized to +".installed.cfg", but it can be overridden in the configuration file +or on the command line: + + >>> write('buildout.cfg', + ... """ + ... [buildout] + ... develop = recipes + ... parts = debug + ... + ... [debug] + ... recipe = recipes:debug + ... """) + + >>> print system(buildout+' buildout:installed=inst.cfg'), + Develop: '/sample-buildout/recipes' + Installing debug. + recipe recipes:debug + + >>> ls(sample_buildout) + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d demo + d develop-eggs + d eggs + - inst.cfg + d parts + d recipes + +The installation database can be disabled by supplying an empty +buildout installed option: + + >>> os.remove('inst.cfg') + >>> print system(buildout+' buildout:installed='), + Develop: '/sample-buildout/recipes' + Installing debug. + recipe recipes:debug + + >>> ls(sample_buildout) + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d demo + d develop-eggs + d eggs + d parts + d recipes + + +Note that there will be no installation database if there are no parts: + + >>> write('buildout.cfg', + ... """ + ... [buildout] + ... parts = + ... """) + + >>> print system(buildout+' buildout:installed=inst.cfg'), + + >>> ls(sample_buildout) + - b1.cfg + - b2.cfg + - base.cfg + d bin + - buildout.cfg + d demo + d develop-eggs + d eggs + d parts + d recipes + +Extensions +---------- + +A feature allows code to be loaded and run after +configuration files have been read but before the buildout has begun +any processing. The intent is to allow special plugins such as +urllib2 request handlers to be loaded. + +To load an extension, we use the extensions option and list one or +more distribution requirements, on separate lines. The distributions +named will be loaded and any ``zc.buildout.extension`` entry points found +will be called with the buildout as an argument. When buildout +finishes processing, any ``zc.buildout.unloadextension`` entry points +found will be called with the buildout as an argument. + +Let's create a sample extension in our sample buildout created in the +previous section: + + >>> mkdir(sample_bootstrapped, 'demo') + + >>> write(sample_bootstrapped, 'demo', 'demo.py', + ... """ + ... def ext(buildout): + ... print 'ext', list(buildout) + ... def unload(buildout): + ... print 'unload', list(buildout) + ... """) + + >>> write(sample_bootstrapped, 'demo', 'setup.py', + ... """ + ... from setuptools import setup + ... + ... setup( + ... name = "demo", + ... entry_points = { + ... 'zc.buildout.extension': ['ext = demo:ext'], + ... 'zc.buildout.unloadextension': ['ext = demo:unload'], + ... }, + ... ) + ... """) + +Our extension just prints out the word 'demo', and lists the sections +found in the buildout passed to it. + +We'll update our buildout.cfg to list the demo directory as a develop +egg to be built: + + >>> write(sample_bootstrapped, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = demo + ... parts = + ... """) + + >>> os.chdir(sample_bootstrapped) + >>> print system(os.path.join(sample_bootstrapped, 'bin', 'buildout')), + Develop: '/sample-bootstrapped/demo' + +Now we can add the extensions option. We were a bit tricky and ran +the buildout once with the demo develop egg defined but without the +extension option. This is because extensions are loaded before the +buildout creates develop eggs. We needed to use a separate buildout +run to create the develop egg. Normally, when eggs are loaded from +the network, we wouldn't need to do anything special. + + >>> write(sample_bootstrapped, 'buildout.cfg', + ... """ + ... [buildout] + ... develop = demo + ... extensions = demo + ... parts = + ... """) + +We see that our extension is loaded and executed: + + >>> print system(os.path.join(sample_bootstrapped, 'bin', 'buildout')), + ext ['buildout'] + Develop: '/sample-bootstrapped/demo' + unload ['buildout'] + +Allow hosts +----------- + +On some environments the links visited by `zc.buildout` can be forbidden +by paranoiac firewalls. These URL might be on the chain of links +visited by `zc.buildout` wheter they are defined in the `find-links` option, +wheter they are defined by various eggs in their `url`, `download_url`, +`dependency_links` metadata. + +It is even harder to track that package_index works like a spider and +might visit links and go to other location. + +The `allow-hosts` option provides a way to prevent this, and +works exactly like the one provided in `easy_install`. + +You can provide a list of allowed host, together with wildcards:: + + [buildout] + ... + + allow-hosts = + *.python.org + example.com + +All urls that does not match these hosts will not be visited. + +.. [#future_recipe_methods] In the future, additional methods may be + added. Older recipes with fewer methods will still be + supported. + +.. [#packaging_info] If we wanted to create a distribution from this + package, we would need specify much more information. See the + `setuptools documentation + `_.