[Checkins] SVN: zc.buildout/branches/encolpe-escape-command/ update this branch with trunk

Encolpe DEGOUTE encolpe.degoute at free.fr
Tue Mar 6 22:53:03 UTC 2012


Log message for revision 124531:
  update this branch with trunk

Changed:
  _U  zc.buildout/branches/encolpe-escape-command/
  A   zc.buildout/branches/encolpe-escape-command/.bzrignore
  U   zc.buildout/branches/encolpe-escape-command/CHANGES.txt
  A   zc.buildout/branches/encolpe-escape-command/COPYRIGHT.txt
  U   zc.buildout/branches/encolpe-escape-command/DEVELOPERS.txt
  A   zc.buildout/branches/encolpe-escape-command/LICENSE.txt
  A   zc.buildout/branches/encolpe-escape-command/MANIFEST.in
  U   zc.buildout/branches/encolpe-escape-command/README.txt
  A   zc.buildout/branches/encolpe-escape-command/SYSTEM_PYTHON_HELP.txt
  U   zc.buildout/branches/encolpe-escape-command/bootstrap/bootstrap.py
  U   zc.buildout/branches/encolpe-escape-command/buildout.cfg
  U   zc.buildout/branches/encolpe-escape-command/dev.py
  U   zc.buildout/branches/encolpe-escape-command/setup.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/__init__.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/allowhosts.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/bootstrap.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/debugging.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/dependencylinks.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/extends-cache.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/rmtree.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing_bugfix.txt
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/tests.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testselectingpython.py
  U   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/update.txt
  A   zc.buildout/branches/encolpe-escape-command/src/zc/buildout/virtualenv.txt
  A   zc.buildout/branches/encolpe-escape-command/z3c.recipe.scripts_/
  _U  zc.buildout/branches/encolpe-escape-command/z3c.recipe.scripts_/src/
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/CHANGES.txt
  A   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/MANIFEST.in
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/setup.py
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/README.txt
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/api.txt
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.py
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.txt
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/egg.py
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt
  U   zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/tests.py

-=-

Property changes on: zc.buildout/branches/encolpe-escape-command
___________________________________________________________________
Added: svn:mergeinfo
   + /zc.buildout/branches/gary-1.5.2:116116,116196
/zc.buildout/branches/gary-launchpad:111587
/zc.buildout/branches/gary-betafix:115514,115815,115845-115847,115859
/zc.buildout/trunk:108946

Added: svk:merge
   + 62d5b8a3-27da-0310-9561-8e5933582275:/zc.buildout/branches/gary-1.5.2:116196
62d5b8a3-27da-0310-9561-8e5933582275:/zc.buildout/branches/gary-betafix:115859
62d5b8a3-27da-0310-9561-8e5933582275:/zc.buildout/branches/gary-launchpad:111587
62d5b8a3-27da-0310-9561-8e5933582275:/zc.buildout/trunk:108946


Copied: zc.buildout/branches/encolpe-escape-command/.bzrignore (from rev 124424, zc.buildout/trunk/.bzrignore)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/.bzrignore	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/.bzrignore	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,9 @@
+.installed.cfg
+bin
+build
+develop-eggs
+eggs
+parts
+src/zc.buildout.egg-info
+z3c.recipe.scripts_/src/z3c.recipe.scripts.egg-info
+zc.recipe.egg_/src/zc.recipe.egg.egg-info

Modified: zc.buildout/branches/encolpe-escape-command/CHANGES.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/CHANGES.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/CHANGES.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,10 +1,198 @@
 Change History
 **************
 
-1.4.4 (?)
-=========
+1.6.0 (unreleased)
+==================
 
+- The buildout init command now accepts distribution requirements and
+  paths to set up a custom interpreter part that has the distributions
+  or parts in the path. For example::
 
+     python bootstrap.py init BeautifulSoup
+
+- Introduce a cache for the expensive `buildout._dir_hash` function.
+
+- Remove duplicate path from script's sys.path setup.
+
+- changed broken dash S check to pass the configuration options
+  -S -c separately, to make zc.buildout more compatible with the PyPy
+  interpreter, which has less flexible argument parsing than CPython.
+
+  Note that PyPy post 1.4.0 is needed to make buildout work at all,
+  due to missing support for the ``-E`` option, which only got added
+  afterwards.
+
+- Made sure to download extended configuration files only once per buildout
+  run even if they are referenced multiple times (patch by Rafael Monnerat).
+
+Bugs fixed:
+
+- In the download module, fixed the handling of directories that are pointed
+  to by file-system paths and ``file:`` URLs.
+
+- Removed any traces of the implementation of ``extended-by``. Raise a
+  UserError if the option is encountered instead of ignoring it, though.
+
+- https://bugs.launchpad.net/bugs/697913 : Buildout doesn't honor exit code
+  from scripts. Fixed.
+
+1.5.2 (2010-10-11)
+==================
+
+- changed metadata 'url' to pypi.python.org in order to solve
+  a temporary outage of buildout.org
+
+- IMPORTANT: For better backwards compatibility with the pre-1.5 line,
+  this release has two big changes from 1.5.0 and 1.5.1.
+
+  - Buildout defaults to including site packages.
+
+  - Buildout loads recipes and extensions with the same constraints to
+    site-packages that it builds eggs, instead of never allowing access
+    to site-packages.
+
+  This means that the default configuration should better support
+  pre-existing use of system Python in recipes or builds.
+
+- To make it easier to detect the fact that buildout has set the PYTHONPATH,
+  BUILDOUT_ORIGINAL_PYTHONPATH is always set in the environment, even if
+  PYTHONPATH was not originally set.  BUILDOUT_ORIGINAL_PYTHONPATH will
+  be an empty string if PYTHONPATH was not set.
+
+1.5.1 (2010-08-29)
+==================
+
+New features:
+
+- Scripts store the old PYTHONPATH in BUILDOUT_ORIGINAL_PYTHONPATH if it
+  existed, and store nothing in the value if it did not exist.  This allows
+  code that does not want subprocesses to have the system-Python-protected
+  site.py to set the environment of the subprocess as it was originally.
+
+Bugs fixed:
+
+- https://bugs.launchpad.net/bugs/623590 : If include-site-packages were
+  true and versions were not set explicitly, system eggs were preferred
+  over newer released eggs.  Fixed.
+
+1.5.0 (2010-08-23)
+==================
+
+New features:
+
+- zc.buildout supports Python 2.7.
+
+- By default, Buildout and the bootstrap script now prefer final versions of
+  Buildout, recipes, and extensions.  This can be changed by using the
+  --accept-buildout-test-releases flag (or -t for short) when calling
+  bootstrap.  This will hopefully allow beta releases of these items to
+  be more easily and safely made in the future.
+
+  NOTE: dependencies of your own software are not affected by this new
+  behavior. Buildout continues to choose the newest available versions
+  of your dependencies regardless of whether they are final releases. To
+  prevent this, use the pre-existing switch ``prefer-final = true`` in
+  the [buildout] section of your configuration file (see
+  http://pypi.python.org/pypi/zc.buildout#preferring-final-releases) or
+  pin your versions using a versions section (see
+  http://pypi.python.org/pypi/zc.buildout#repeatable-buildouts-controlling-eggs-used).
+
+Bugs fixed:
+
+- You can now again use virtualenv with Buildout.  The new features to let
+  buildout be used with a system Python are disabled in this configuration,
+  and the previous script generation behavior (1.4.3) is used, even if
+  the new function ``zc.buildout.easy_install.sitepackage_safe_scripts``
+  is used.
+
+1.5.0b2 (2010-04-29)
+====================
+
+This was a re-release of 1.4.3 in order to keep 1.5.0b1 release from hurting
+workflows that combined virtualenv with zc.buildout.
+
+1.5.0b1 (2010-04-29)
+====================
+
+New Features:
+
+- Added buildout:socket-timout option so that socket timeout can be configured
+  both from command line and from config files. (gotcha)
+
+- Buildout can be safely used with a system Python (or any Python with code
+  in site-packages), as long as you use (1) A fresh checkout, (2) the
+  new bootstrap.py, and (3) recipes that use the new
+  ``zc.buildout.easy_install.sitepackage_safe_scripts`` function to generate
+  scripts and interpreters.  Many recipes will need to be updated to use
+  this new function.  The scripts and interpreters generated by
+  ``zc.recipe.egg`` will continue to use the older function, not safe
+  with system Pythons.  Use the ``z3c.recipe.scripts`` as a replacement.
+
+  zc.recipe.egg is still a fully supported, and simpler, way of
+  generating scripts and interpreters if you are using a "clean" Python,
+  without code installed in site-packages. It keeps its previous behavior in
+  order to provide backwards compatibility.
+
+  The z3c.recipe.scripts recipe allows you to control how you use the
+  code in site-packages.  You can exclude it entirely (preferred); allow
+  eggs in it to fulfill package dependencies declared in setup.py and
+  buildout configuration; allow it to be available but not used to
+  fulfill dependencies declared in setup.py or buildout configuration;
+  or only allow certain eggs in site-packages to fulfill dependencies.
+
+- Added new function, ``zc.buildout.easy_install.sitepackage_safe_scripts``,
+  to generate scripts and interpreter.  It produces a full-featured
+  interpreter (all command-line options supported) and the ability to
+  safely let scripts include site packages, such as with a system
+  Python.  The ``z3c.recipe.scripts`` recipe uses this new function.
+
+- Improve bootstrap.
+
+  * New options let you specify where to find ez_setup.py and where to find
+    a download cache.  These options can keep bootstrap from going over the
+    network.
+
+  * Another new option lets you specify where to put generated eggs.
+
+  * The buildout script generated by bootstrap honors more of the settings
+    in the designated configuration file (e.g., buildout.cfg).
+
+  * Correctly handle systems where pkg_resources is present but the rest of
+    setuptools is missing (like Ubuntu installs).
+    https://bugs.launchpad.net/zc.buildout/+bug/410528
+
+- You can develop zc.buildout using Distribute instead of Setuptools.  Use
+  the --distribute option on the dev.py script.  (Releases should be tested
+  with both Distribute and Setuptools.)  The tests for zc.buildout pass
+  with Setuptools and Python 2.4, 2.5, 2.6, and 2.7; and with Distribute and
+  Python 2.5, 2.6, and 2.7.  Using zc.buildout with Distribute and Python 2.4
+  is not recommended.
+
+- The ``distribute-version`` now works in the [buildout] section, mirroring
+  the ``setuptools-version`` option (this is for consistency; using the
+  general-purpose ``versions`` option is preferred).
+
+Bugs fixed:
+
+- Using Distribute with the ``allow-picked-versions = false`` buildout
+  option no longer causes an error.
+
+- The handling and documenting of default buildout options was normalized.
+  This means, among other things, that ``bin/buildout -vv`` and
+  ``bin/buildout annotate`` correctly list more of the options.
+
+- Installing a namespace package using a Python that already has a package
+  in the same namespace (e.g., in the Python's site-packages) failed in
+  some cases.  It is now handled correctly.
+
+- Another variation of this error showed itself when at least two
+  dependencies were in a shared location like site-packages, and the
+  first one met the "versions" setting.  The first dependency would be
+  added, but subsequent dependencies from the same location (e.g.,
+  site-packages) would use the version of the package found in the
+  shared location, ignoring the version setting.  This is also now
+  handled correctly.
+
 1.4.3 (2009-12-10)
 ==================
 
@@ -428,7 +616,7 @@
 - 92891: bootstrap crashes with recipe option in buildout section.
 
 - 113085: Buildout exited with a zero exist status when internal errors
-  occured.
+  occurred.
 
 
 1.0.0b23 (2007-03-19)

Copied: zc.buildout/branches/encolpe-escape-command/COPYRIGHT.txt (from rev 124424, zc.buildout/trunk/COPYRIGHT.txt)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/COPYRIGHT.txt	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/COPYRIGHT.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1 @@
+Zope Foundation and Contributors
\ No newline at end of file

Modified: zc.buildout/branches/encolpe-escape-command/DEVELOPERS.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/DEVELOPERS.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/DEVELOPERS.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -9,3 +9,17 @@
 
 - Don't bootstrap with ``python bootstrap/bootstrap.py`` but with ``python
   dev.py``.
+
++1 for testing:
+
+- You should have specific python executable versions at specific locations or
+  PYTHONxy environment variables pointing to those
+  See zc.buildout testing.py, def find_python(version)
+
+  If you use python2.5 to test then the test runner will look for
+  python2.6 in your path. If you use any other python version it will look for
+  python2.5 in your path or it will look for the PYTHON25 environment variable.
+  
+  To start the tests with Python 2.7 for instance would be::
+
+    $ env PYTHON25=/path/to/bin/python2.5 bin/test

Copied: zc.buildout/branches/encolpe-escape-command/LICENSE.txt (from rev 124424, zc.buildout/trunk/LICENSE.txt)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/LICENSE.txt	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/LICENSE.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,44 @@
+Zope Public License (ZPL) Version 2.1
+
+A copyright notice accompanies this license document that identifies the
+copyright holders.
+
+This license has been certified as open source. It has also been designated as
+GPL compatible by the Free Software Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions in source code must retain the accompanying copyright
+notice, this list of conditions, and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying copyright
+notice, this list of conditions, and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to endorse or promote
+products derived from this software without prior written permission from the
+copyright holders.
+
+4. The right to distribute this software or to use it for any purpose does not
+give you the right to use Servicemarks (sm) or Trademarks (tm) of the
+copyright
+holders. Use of them is covered by separate agreement with the copyright
+holders.
+
+5. If any files are modified, you must cause the modified files to carry
+prominent notices stating that you changed the files and the date of any
+change.
+
+Disclaimer
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Copied: zc.buildout/branches/encolpe-escape-command/MANIFEST.in (from rev 124424, zc.buildout/trunk/MANIFEST.in)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/MANIFEST.in	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/MANIFEST.in	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,3 @@
+include *.txt
+recursive-include src *.txt
+exclude MANIFEST.in buildout.cfg .bzrignore

Modified: zc.buildout/branches/encolpe-escape-command/README.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/README.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/README.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -35,8 +35,16 @@
 `zc.recipe.egg <http://pypi.python.org/pypi/zc.recipe.egg>`_
    The egg recipe installes one or more eggs, with their
    dependencies.  It installs their console-script entry points with
-   the needed eggs included in their paths.
+   the needed eggs included in their paths.  It is suitable for use with
+   a "clean" Python: one without packages installed in site-packages.
 
+`z3c.recipe.scripts <http://pypi.python.org/pypi/z3c.recipe.scripts>`_
+  Like zc.recipe.egg, this recipe builds interpreter scripts and entry
+  point scripts based on eggs.  It can be used with a Python that has
+  packages installed in site-packages, such as a system Python.  The
+  interpreter also has more features than the one offered by
+  zc.recipe.egg.
+
 `zc.recipe.testrunner <http://pypi.python.org/pypi/zc.recipe.testrunner>`_
    The testrunner egg creates a test runner script for one or
    more eggs.
@@ -126,7 +134,7 @@
 `--version` option of the bootstrap.py script::
 
     $ python bootstrap.py --version 1.1.3
-    
+
 The `zc.buildout project <http://svn.zope.org/zc.buildout/trunk>`_
 is a slightly more complex example of this type of buildout.
 
@@ -191,5 +199,4 @@
 `distutils SIG mailing list <mailto://distutils-sig@python.org>`_.
 
 Report bugs using the `zc.buildout Launchpad Bug Tracker
-<https://launchpad.net/products/zc.buildout/+bugs>`_.
-
+<https://launchpad.net/zc.buildout/+bugs>`_.

Copied: zc.buildout/branches/encolpe-escape-command/SYSTEM_PYTHON_HELP.txt (from rev 124424, zc.buildout/trunk/SYSTEM_PYTHON_HELP.txt)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/SYSTEM_PYTHON_HELP.txt	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/SYSTEM_PYTHON_HELP.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,234 @@
+System Python and zc.buildout 1.5
+*********************************
+
+The 1.5 line of zc.buildout introduced a number of changes.
+
+Problems
+========
+
+As usual, please send questions and comments to the `distutils SIG
+mailing list <mailto://distutils-sig@python.org>`_. Report bugs using
+the `zc.buildout Launchpad Bug Tracker
+<https://launchpad.net/zc.buildout/+bugs>`_.
+
+If problems are keeping you from your work, here's an easy way to
+revert to the old code temporarily: switch to a custom "emergency"
+bootstrap script, available from
+http://svn.zope.org/repos/main/zc.buildout/branches/1.4/bootstrap/bootstrap.py .
+
+This customized script will select zc.buildout 1.4.4 by default.
+zc.buildout 1.4.4 will not upgrade itself unless you explicitly specify
+a new version.  It will also prefer older versions of zc.recipe.egg and
+some other common recipes.  If you have trouble with other recipes,
+consider using a standard buildout "versions" section to specify older
+versions of these, as described in the Buildout documentation
+(http://pypi.python.org/pypi/zc.buildout#repeatable-buildouts-controlling-eggs-used).
+
+Working with a System Python
+============================
+
+While there are a number of new features available in zc.buildout 1.5,
+the biggest is that Buildout itself supports usage with a system Python.
+This can work if you follow a couple of simple rules.
+
+1. Use the new bootstrap.py (available from
+   svn://svn.zope.org/repos/main/zc.buildout/trunk/bootstrap/bootstrap.py).
+
+2. Use buildout recipes that have been upgraded to work with zc.buildout 1.5
+   and higher.  Specifically, they should use
+   ``zc.buildout.easy_install.sitepackage_safe_scripts`` to generate
+   their scripts, if any, rather than ``zc.buildout.easy_install.scripts``.
+   See the `Recipes That Support a System Python`_ section below for more
+   details on recipes that are available as of this writing, and
+   `Updating Recipes to Support a System Python`_ for instructions on
+   how to update a recipe.  Note that you should generally only need to
+   update recipes that generate scripts.
+
+You can then use ``include-site-packages = false`` and
+``exec-sitecustomize = false`` buildout options to eliminate access to
+your Python's site packages and not execute its sitecustomize file, if
+it exists, respectively.
+
+Alternately, you can use the ``allowed-eggs-from-site-packages`` buildout
+option as a glob-aware whitelist of eggs that may come from site-packages.
+This value defaults to "*", accepting all eggs.
+
+It's important to note that recipes not upgraded for zc.buildout 1.5.0
+should continue to work--just without internal support for a system Python.
+
+Using a system Python is inherently fragile.  Using a clean,
+freshly-installed Python without customization in site-packages is more
+robust and repeatable.  See some of the regression tests added to the
+1.5.0 line for the kinds of issues that you can encounter with a system
+Python, and see
+http://pypi.python.org/pypi/z3c.recipe.scripts#including-site-packages-and-sitecustomize
+for more discussion.
+
+However, using a system Python can be very convenient, and the
+zc.buildout code for this feature has been tested by many users already.
+Moreover, it has automated tests to exercise the problems that have been
+encountered and fixed.  Many people rely on it.
+
+Recipes That Support a System Python
+====================================
+
+zc.recipe.egg continues to generate old-style scripts that are not safe
+for use with a system Python.  This was done for backwards
+compatibility, because it is integral to so many buildouts and used as a
+dependency of so many other recipes.
+
+If you want to generate new-style scripts that do support system Python
+usage, use z3c.recipe.scripts instead
+(http://pypi.python.org/pypi/z3c.recipe.scripts). z3c.recipe.scripts has
+the same script and interpreter generation options as zc.recipe.egg,
+plus a few more for the new features mentioned above.  In the simplest
+case, you should be able to simply change ``recipe = zc.recipe.egg`` to
+``recipe = z3c.recipe.scripts`` in the pertinent sections of your
+buildout configuration and your generated scripts will work with a system
+Python.
+
+Other updated recipes include zc.recipe.testrunner 1.4.0 and
+z3c.recipe.tag 0.4.0.  Others should be updated soon: see their change
+documents for details, or see `Updating Recipes to Support a System
+Python`_ for instructions on how to update recipes yourself.
+
+Templates for creating Python scripts with the z3c.recipe.filetemplate
+recipe can be easily changed to support a system Python.
+
+- If you don't care about supporting relative paths, simply using a
+  generated interpreter with the eggs you want should be sufficient, as
+  it was before. For instance, if the interpreter is named "py", use
+  ``#!${buildout:bin-directory/py}`` or ``#!/usr/bin/env
+  ${buildout:bin-directory/py}``).
+
+- If you do care about relative paths,  (``relative-paths = true`` in
+  your buildout configuration), then z3c.recipe.scripts does require a
+  bit more changes, as is usual for the relative path support in that
+  package.  First, use z3c.recipe.scripts to generate a script or
+  interpreter with the dependencies you want.  This will create a
+  directory in ``parts`` that has a site.py and sitecustomize.py.  Then,
+  begin your script as in the snippet below.  The example assumes that
+  the z3c.recipe.scripts generated were from a Buildout configuration
+  section labeled "scripts": adjust accordingly.
+
+  ::
+
+    #!${buildout:executable} -S
+    ${python-relative-path-setup}
+    import sys
+    sys.path.insert(0, ${scripts:parts-directory|path-repr})
+    import site
+
+Updating Recipes to Support a System Python
+===========================================
+
+You should generally only need to update recipes that generate scripts.
+These recipes need to change from using ``zc.buildout.easy_install.scripts``
+to be using ``zc.buildout.easy_install.sitepackage_safe_scripts``.
+The signatures of the two functions are different.  Please compare::
+
+    def scripts(
+        reqs, working_set, executable, dest,
+        scripts=None,
+        extra_paths=(),
+        arguments='',
+        interpreter=None,
+        initialization='',
+        relative_paths=False,
+        ):
+
+    def sitepackage_safe_scripts(
+        dest, working_set, executable, site_py_dest,
+        reqs=(),
+        scripts=None,
+        interpreter=None,
+        extra_paths=(),
+        initialization='',
+        include_site_packages=False,
+        exec_sitecustomize=False,
+        relative_paths=False,
+        script_arguments='',
+        script_initialization='',
+        ):
+
+In most cases, the arguments are merely reordered.  The ``reqs``
+argument is no longer required in order to make it easier to generate an
+interpreter alone.  The ``arguments`` argument was renamed to
+``script_arguments`` to clarify that it did not affect interpreter
+generation.
+
+The only new required argument is ``site_py_dest``.  It must be the path
+to a directory in which the customized site.py and sitecustomize.py
+files will be written.  A typical generation in a recipe will look like
+this.
+
+(In the recipe's __init__ method...)
+
+::
+
+        self.options = options
+        b_options = buildout['buildout']
+        options['parts-directory'] = os.path.join(
+            b_options['parts-directory'], self.name)
+
+(In the recipe's install method...)
+
+::
+
+        options = self.options
+        generated = []
+        if not os.path.exists(options['parts-directory']):
+            os.mkdir(options['parts-directory'])
+            generated.append(options['parts-directory'])
+
+Then ``options['parts-directory']`` can be used for the ``site_py_dest``
+value.
+
+If you want to support the other arguments (``include_site_packages``,
+``exec_sitecustomize``, ``script_initialization``, as well as the
+``allowed-eggs-from-site-packages`` option),  you might want to look at
+some of the code in
+svn://svn.zope.org/repos/main/zc.buildout/trunk/z3c.recipe.scripts\_/src/z3c/recipe/scripts/scripts.py .
+You might even be able to adopt some of it by subclassing or delegating.
+The Scripts class in that file is the closest to what you might be used
+to from zc.recipe.egg.
+
+Important note for recipe authors: As of buildout 1.5.2, the code in
+recipes is *always run with the access to the site-packages as
+configured in the buildout section*.
+
+virtualenv
+==========
+
+Using virtualenv (http://pypi.python.org/pypi/virtualenv) with the
+--no-site-packages option already provided a simple way of using a
+system Python.  This is intended to continue to work, and some automated
+tests exist to demonstrate this.
+
+However, it is only supported to the degree that people have found it to
+work in the past.  The existing Buildout tests for virtualenv are only
+for problems encountered previously.  They are very far from
+comprehensive.
+
+Using Buildout with a system python has at least three advantages over
+using Buildout in conjunction with virtualenv.  They may or may not be
+pertinent to your desired usage.
+
+- Unlike ``virtualenv --no-site-packages``, Buildout's support allows you
+  to choose to let packages from your system Python be available to your
+  software (see ``include-site-packages`` in
+  http://pypi.python.org/pypi/z3c.recipe.scripts).
+
+  You can even specify which eggs installed in your system Python can be
+  allowed to fulfill some of your packages' dependencies (see
+  ``allowed-eggs-from-site-packages`` in
+  http://pypi.python.org/pypi/z3c.recipe.scripts).
+
+  At the expense of some repeatability and platform dependency, this
+  flexibility means that, for instance, you can rely on
+  difficult-to-build eggs like lxml coming from your system Python.
+
+- Buildout's implementation has a full set of automated tests.
+
+- An integral Buildout implementation means fewer steps and fewer dependencies
+  to work with a system Python.

Modified: zc.buildout/branches/encolpe-escape-command/bootstrap/bootstrap.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/bootstrap/bootstrap.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/bootstrap/bootstrap.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -16,106 +16,247 @@
 Simply run this script in a directory containing a buildout.cfg.
 The script accepts buildout command-line options, so you can
 use the -c option to specify an alternate configuration file.
-
-$Id$
 """
 
-import os, shutil, sys, tempfile, urllib2
+import os, shutil, sys, tempfile, urllib, urllib2, subprocess
 from optparse import OptionParser
 
-tmpeggs = tempfile.mkdtemp()
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c  # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    quote = str
 
+# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
+stdout, stderr = subprocess.Popen(
+    [sys.executable, '-Sc',
+     'try:\n'
+     '    import ConfigParser\n'
+     'except ImportError:\n'
+     '    print 1\n'
+     'else:\n'
+     '    print 0\n'],
+    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+has_broken_dash_S = bool(int(stdout.strip()))
+
+# In order to be more robust in the face of system Pythons, we want to
+# run without site-packages loaded.  This is somewhat tricky, in
+# particular because Python 2.6's distutils imports site, so starting
+# with the -S flag is not sufficient.  However, we'll start with that:
+if not has_broken_dash_S and 'site' in sys.modules:
+    # We will restart with python -S.
+    args = sys.argv[:]
+    args[0:0] = [sys.executable, '-S']
+    args = map(quote, args)
+    os.execv(sys.executable, args)
+# Now we are running with -S.  We'll get the clean sys.path, import site
+# because distutils will do it later, and then reset the path and clean
+# out any namespace packages from site-packages that might have been
+# loaded by .pth files.
+clean_path = sys.path[:]
+import site  # imported because of its side effects
+sys.path[:] = clean_path
+for k, v in sys.modules.items():
+    if k in ('setuptools', 'pkg_resources') or (
+        hasattr(v, '__path__') and
+        len(v.__path__) == 1 and
+        not os.path.exists(os.path.join(v.__path__[0], '__init__.py'))):
+        # This is a namespace package.  Remove it.
+        sys.modules.pop(k)
+
 is_jython = sys.platform.startswith('java')
 
+setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
+distribute_source = 'http://python-distribute.org/distribute_setup.py'
+
+
 # parsing arguments
-parser = OptionParser()
+def normalize_to_url(option, opt_str, value, parser):
+    if value:
+        if '://' not in value:  # It doesn't smell like a URL.
+            value = 'file://%s' % (
+                urllib.pathname2url(
+                    os.path.abspath(os.path.expanduser(value))),)
+        if opt_str == '--download-base' and not value.endswith('/'):
+            # Download base needs a trailing slash to make the world happy.
+            value += '/'
+    else:
+        value = None
+    name = opt_str[2:].replace('-', '_')
+    setattr(parser.values, name, value)
+
+usage = '''\
+[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
+
+Bootstraps a buildout-based project.
+
+Simply run this script in a directory containing a buildout.cfg, using the
+Python that you want bin/buildout to use.
+
+Note that by using --setup-source and --download-base to point to
+local resources, you can keep this script from going over the network.
+'''
+
+parser = OptionParser(usage=usage)
 parser.add_option("-v", "--version", dest="version",
                           help="use a specific zc.buildout version")
 parser.add_option("-d", "--distribute",
-                   action="store_true", dest="distribute", default=False,
-                   help="Use Disribute rather than Setuptools.")
-
+                   action="store_true", dest="use_distribute", default=False,
+                   help="Use Distribute rather than Setuptools.")
+parser.add_option("--setup-source", action="callback", dest="setup_source",
+                  callback=normalize_to_url, nargs=1, type="string",
+                  help=("Specify a URL or file location for the setup file. "
+                        "If you use Setuptools, this will default to " +
+                        setuptools_source + "; if you use Distribute, this "
+                        "will default to " + distribute_source + "."))
+parser.add_option("--download-base", action="callback", dest="download_base",
+                  callback=normalize_to_url, nargs=1, type="string",
+                  help=("Specify a URL or directory for downloading "
+                        "zc.buildout and either Setuptools or Distribute. "
+                        "Defaults to PyPI."))
+parser.add_option("--eggs",
+                  help=("Specify a directory for storing eggs.  Defaults to "
+                        "a temporary directory that is deleted when the "
+                        "bootstrap script completes."))
+parser.add_option("-t", "--accept-buildout-test-releases",
+                  dest='accept_buildout_test_releases',
+                  action="store_true", default=False,
+                  help=("Normally, if you do not specify a --version, the "
+                        "bootstrap script and buildout gets the newest "
+                        "*final* versions of zc.buildout and its recipes and "
+                        "extensions for you.  If you use this flag, "
+                        "bootstrap and buildout will get the newest releases "
+                        "even if they are alphas or betas."))
 parser.add_option("-c", None, action="store", dest="config_file",
                    help=("Specify the path to the buildout configuration "
                          "file to be used."))
 
 options, args = parser.parse_args()
 
-# if -c was provided, we push it back into args for buildout' main function
+# if -c was provided, we push it back into args for buildout's main function
 if options.config_file is not None:
     args += ['-c', options.config_file]
 
-if options.version is not None:
-    VERSION = '==%s' % options.version
+if options.eggs:
+    eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
 else:
-    VERSION = ''
+    eggs_dir = tempfile.mkdtemp()
 
-USE_DISTRIBUTE = options.distribute
-args = args + ['bootstrap']
+if options.setup_source is None:
+    if options.use_distribute:
+        options.setup_source = distribute_source
+    else:
+        options.setup_source = setuptools_source
 
-to_reload = False
+if options.accept_buildout_test_releases:
+    args.append('buildout:accept-buildout-test-releases=true')
+args.append('bootstrap')
+
 try:
     import pkg_resources
+    import setuptools  # A flag.  Sometimes pkg_resources is installed alone.
     if not hasattr(pkg_resources, '_distribute'):
-        to_reload = True
         raise ImportError
 except ImportError:
+    ez_code = urllib2.urlopen(
+        options.setup_source).read().replace('\r\n', '\n')
     ez = {}
-    if USE_DISTRIBUTE:
-        exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
-                         ).read() in ez
-        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
-    else:
-        exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
-                             ).read() in ez
-        ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+    exec ez_code in ez
+    setup_args = dict(to_dir=eggs_dir, download_delay=0)
+    if options.download_base:
+        setup_args['download_base'] = options.download_base
+    if options.use_distribute:
+        setup_args['no_fake'] = True
+    ez['use_setuptools'](**setup_args)
+    if 'pkg_resources' in sys.modules:
+        reload(sys.modules['pkg_resources'])
+    import pkg_resources
+    # This does not (always?) update the default working set.  We will
+    # do it.
+    for path in sys.path:
+        if path not in pkg_resources.working_set.entries:
+            pkg_resources.working_set.add_entry(path)
 
-    if to_reload:
-        reload(pkg_resources)
-    else:
-        import pkg_resources
+cmd = [quote(sys.executable),
+       '-c',
+       quote('from setuptools.command.easy_install import main; main()'),
+       '-mqNxd',
+       quote(eggs_dir)]
 
-if sys.platform == 'win32':
-    def quote(c):
-        if ' ' in c:
-            return '"%s"' % c # work around spawn lamosity on windows
-        else:
-            return c
-else:
-    def quote (c):
-        return c
+if not has_broken_dash_S:
+    cmd.insert(1, '-S')
 
-cmd = 'from setuptools.command.easy_install import main; main()'
-ws  = pkg_resources.working_set
+find_links = options.download_base
+if not find_links:
+    find_links = os.environ.get('bootstrap-testing-find-links')
+if find_links:
+    cmd.extend(['-f', quote(find_links)])
 
-if USE_DISTRIBUTE:
-    requirement = 'distribute'
+if options.use_distribute:
+    setup_requirement = 'distribute'
 else:
-    requirement = 'setuptools'
+    setup_requirement = 'setuptools'
+ws = pkg_resources.working_set
+setup_requirement_path = ws.find(
+    pkg_resources.Requirement.parse(setup_requirement)).location
+env = dict(
+    os.environ,
+    PYTHONPATH=setup_requirement_path)
 
+requirement = 'zc.buildout'
+version = options.version
+if version is None and not options.accept_buildout_test_releases:
+    # Figure out the most recent final version of zc.buildout.
+    import setuptools.package_index
+    _final_parts = '*final-', '*final'
+
+    def _final_version(parsed_version):
+        for part in parsed_version:
+            if (part[:1] == '*') and (part not in _final_parts):
+                return False
+        return True
+    index = setuptools.package_index.PackageIndex(
+        search_path=[setup_requirement_path])
+    if find_links:
+        index.add_find_links((find_links,))
+    req = pkg_resources.Requirement.parse(requirement)
+    if index.obtain(req) is not None:
+        best = []
+        bestv = None
+        for dist in index[req.project_name]:
+            distv = dist.parsed_version
+            if _final_version(distv):
+                if bestv is None or distv > bestv:
+                    best = [dist]
+                    bestv = distv
+                elif distv == bestv:
+                    best.append(dist)
+        if best:
+            best.sort()
+            version = best[-1].version
+if version:
+    requirement = '=='.join((requirement, version))
+cmd.append(requirement)
+
 if is_jython:
     import subprocess
+    exitcode = subprocess.Popen(cmd, env=env).wait()
+else:  # Windows prefers this, apparently; otherwise we would prefer subprocess
+    exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
+if exitcode != 0:
+    sys.stdout.flush()
+    sys.stderr.flush()
+    print ("An error occurred when trying to install zc.buildout. "
+           "Look above this message for any errors that "
+           "were output by easy_install.")
+    sys.exit(exitcode)
 
-    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
-           quote(tmpeggs), 'zc.buildout' + VERSION],
-           env=dict(os.environ,
-               PYTHONPATH=
-               ws.find(pkg_resources.Requirement.parse(requirement)).location
-               ),
-           ).wait() == 0
-
-else:
-    assert os.spawnle(
-        os.P_WAIT, sys.executable, quote (sys.executable),
-        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
-        dict(os.environ,
-            PYTHONPATH=
-            ws.find(pkg_resources.Requirement.parse(requirement)).location
-            ),
-        ) == 0
-
-ws.add_entry(tmpeggs)
-ws.require('zc.buildout' + VERSION)
+ws.add_entry(eggs_dir)
+ws.require(requirement)
 import zc.buildout.buildout
 zc.buildout.buildout.main(args)
-shutil.rmtree(tmpeggs)
+if not options.eggs:  # clean up temporary egg directory
+    shutil.rmtree(eggs_dir)

Modified: zc.buildout/branches/encolpe-escape-command/buildout.cfg
===================================================================
--- zc.buildout/branches/encolpe-escape-command/buildout.cfg	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/buildout.cfg	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,25 +1,27 @@
 [buildout]
-develop = zc.recipe.egg_ .
+develop = zc.recipe.egg_ z3c.recipe.scripts_ .
 parts = test oltest py
 
 [py]
-recipe = zc.recipe.egg
+recipe = z3c.recipe.scripts
 eggs = zc.buildout
        zope.testing
 interpreter = py
 
 [test]
 recipe = zc.recipe.testrunner
-eggs = 
-  zc.buildout
+eggs =
+  zc.buildout[test]
   zc.recipe.egg
+  z3c.recipe.scripts
 
 # Tests that can be run wo a network
 [oltest]
 recipe = zc.recipe.testrunner
-eggs = 
-  zc.buildout
+eggs =
+  zc.buildout[test]
   zc.recipe.egg
+  z3c.recipe.scripts
 defaults =
   [
   '-t',

Modified: zc.buildout/branches/encolpe-escape-command/dev.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/dev.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/dev.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2005 Zope Corporation and Contributors.
+# Copyright (c) 2005 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -13,43 +13,134 @@
 ##############################################################################
 """Bootstrap the buildout project itself.
 
-This is different from a normal boostrapping process because the
+This is different from a normal bootstrapping process because the
 buildout egg itself is installed as a develop egg.
 
 $Id$
 """
 
-import os, shutil, sys, subprocess, urllib2
+import os, shutil, sys, subprocess, urllib2, subprocess
+from optparse import OptionParser
 
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    quote = str
+
+# Detect https://bugs.launchpad.net/virtualenv/+bug/572545 .
+has_broken_dash_S = subprocess.call(
+    [sys.executable, '-Sc', 'import ConfigParser'])
+
+# In order to be more robust in the face of system Pythons, we want to
+# run without site-packages loaded.  This is somewhat tricky, in
+# particular because Python 2.6's distutils imports site, so starting
+# with the -S flag is not sufficient.  However, we'll start with that:
+if not has_broken_dash_S and 'site' in sys.modules:
+    # We will restart with python -S.
+    args = sys.argv[:]
+    args[0:0] = [sys.executable, '-S']
+    args = map(quote, args)
+    os.execv(sys.executable, args)
+# Now we are running with -S.  We'll get the clean sys.path, import site
+# because distutils will do it later, and then reset the path and clean
+# out any namespace packages from site-packages that might have been
+# loaded by .pth files.
+clean_path = sys.path[:]
+import site
+sys.path[:] = clean_path
+for k, v in sys.modules.items():
+    if (hasattr(v, '__path__') and
+        len(v.__path__)==1 and
+        not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
+        # This is a namespace package.  Remove it.
+        sys.modules.pop(k)
+
 is_jython = sys.platform.startswith('java')
 
+setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
+distribute_source = 'http://python-distribute.org/distribute_setup.py'
+
+usage = '''\
+[DESIRED PYTHON FOR DEVELOPING BUILDOUT] dev.py [options]
+
+Bootstraps buildout itself for development.
+
+This is different from a normal bootstrapping process because the
+buildout egg itself is installed as a develop egg.
+'''
+
+parser = OptionParser(usage=usage)
+parser.add_option("-d", "--distribute",
+                   action="store_true", dest="use_distribute", default=False,
+                   help="Use Distribute rather than Setuptools.")
+
+options, args = parser.parse_args()
+
+if args:
+    parser.error('This script accepts no arguments other than its options.')
+
+if options.use_distribute:
+    setup_source = distribute_source
+else:
+    setup_source = setuptools_source
+
 for d in 'eggs', 'develop-eggs', 'bin':
     if not os.path.exists(d):
         os.mkdir(d)
-
 if os.path.isdir('build'):
     shutil.rmtree('build')
 
 try:
+    to_reload = False
     import pkg_resources
+    to_reload = True
+    if not hasattr(pkg_resources, '_distribute'):
+        raise ImportError
+    import setuptools # A flag.  Sometimes pkg_resources is installed alone.
 except ImportError:
+    ez_code = urllib2.urlopen(setup_source).read().replace('\r\n', '\n')
     ez = {}
-    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
-                         ).read() in ez
-    ez['use_setuptools'](to_dir='eggs', download_delay=0)
+    exec ez_code in ez
+    setup_args = dict(to_dir='eggs', download_delay=0)
+    if options.use_distribute:
+        setup_args['no_fake'] = True
+    ez['use_setuptools'](**setup_args)
+    if to_reload:
+        reload(pkg_resources)
+    else:
+        import pkg_resources
+    # This does not (always?) update the default working set.  We will
+    # do it.
+    for path in sys.path:
+        if path not in pkg_resources.working_set.entries:
+            pkg_resources.working_set.add_entry(path)
 
-    import pkg_resources
+env = os.environ.copy() # Windows needs yet-to-be-determined values from this.
+env['PYTHONPATH'] = os.path.dirname(pkg_resources.__file__)
 
-subprocess.Popen(
-    [sys.executable] +
-    ['setup.py', '-q', 'develop', '-m', '-x', '-d', 'develop-eggs'],
-    env = {'PYTHONPATH': os.path.dirname(pkg_resources.__file__)}).wait()
+cmd = [sys.executable,
+       'setup.py', '-q', 'develop', '-m', '-x', '-d', 'develop-eggs']
 
+if not has_broken_dash_S:
+    cmd.insert(1, '-S')
+
+subprocess.Popen(cmd, env=env).wait()
+
 pkg_resources.working_set.add_entry('src')
 
 import zc.buildout.easy_install
-zc.buildout.easy_install.scripts(
-    ['zc.buildout'], pkg_resources.working_set , sys.executable, 'bin')
+if not os.path.exists('parts'):
+    os.mkdir('parts')
+partsdir = os.path.join('parts', 'buildout')
+if not os.path.exists(partsdir):
+    os.mkdir(partsdir)
+zc.buildout.easy_install.sitepackage_safe_scripts(
+    'bin', pkg_resources.working_set, sys.executable, partsdir,
+    reqs=['zc.buildout'])
 
 bin_buildout = os.path.join('bin', 'buildout')
 
@@ -57,4 +148,5 @@
     # Jython needs the script to be called twice via sys.executable
     assert subprocess.Popen([sys.executable] + [bin_buildout]).wait() == 0
 
+
 sys.exit(subprocess.Popen(bin_buildout).wait())

Modified: zc.buildout/branches/encolpe-escape-command/setup.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/setup.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/setup.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006-2009 Zope Corporation and Contributors.
+# Copyright (c) 2006-2009 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -12,7 +12,7 @@
 #
 ##############################################################################
 name = "zc.buildout"
-version = "1.4.4dev"
+version = "1.5.3-dev"
 
 import os
 from setuptools import setup
@@ -23,6 +23,8 @@
 long_description=(
         read('README.txt')
         + '\n' +
+        read('SYSTEM_PYTHON_HELP.txt')
+        + '\n' +
         'Detailed Documentation\n'
         '**********************\n'
         + '\n' +
@@ -74,7 +76,7 @@
     long_description=long_description,
     license = "ZPL 2.1",
     keywords = "development build",
-    url='http://buildout.org',
+    url='http://pypi.python.org/pypi/zc.buildout',
 
     data_files = [('.', ['README.txt'])],
     packages = ['zc', 'zc.buildout'],
@@ -83,6 +85,7 @@
     install_requires = 'setuptools',
     include_package_data = True,
     entry_points = entry_points,
+    extras_require = dict(test=['zope.testing']),
     zip_safe=False,
     classifiers = [
        'Intended Audience :: Developers',

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/__init__.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/__init__.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/__init__.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/allowhosts.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/allowhosts.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/allowhosts.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -2,14 +2,14 @@
 -----------
 
 On some environments the links visited by `zc.buildout` can be forbidden
-by paranoiac firewalls. These URL might be on the chain of links 
+by paranoiac firewalls. These URL might be on the chain of links
 visited by `zc.buildout` whether they are defined in the `find-links` option
 or by various eggs in their `url`, `download_url` and `dependency_links` metadata.
 
-It is even harder to track that package_index works like a spider and 
+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 
+The `allow-hosts` option provides a way to prevent this, and
 works exactly like the one provided in `easy_install`
 (see `easy_install allow-hosts option`_).
 
@@ -17,7 +17,7 @@
 
     [buildout]
     ...
-    
+
     allow-hosts =
         *.python.org
         example.com
@@ -36,7 +36,7 @@
     ...     zip_safe=True, version='1')
     ... ''')
 
-Now let's configure the buildout to use the develop egg, 
+Now let's configure the buildout to use the develop egg,
 together with some rules that disallow any website but PyPI and
 local files::
 
@@ -55,14 +55,11 @@
 
 Now we can run the buildout and make sure all attempts to dist.plone.org fails::
 
-    >>> print system(buildout)
+    >>> print system(buildout) # doctest: +ELLIPSIS
     Develop: '/sample-buildout/allowdemo'
-    Installing eggs.
-    <BLANKLINE>
+    ...
     Link to http://dist.plone.org ***BLOCKED*** by --allow-hosts
-    <BLANKLINE>
-    Couldn't find index page for 'kss.core' (maybe misspelled?)
-    Getting distribution for 'kss.core'.
+    ...
     While:
       Installing eggs.
       Getting distribution for 'kss.core'.
@@ -91,14 +88,11 @@
 
 Now we can run the buildout and make sure all attempts to dist.plone.org fails::
 
-    >>> print system(buildout)
+    >>> print system(buildout) # doctest: +ELLIPSIS
     Develop: '/sample-buildout/allowdemo'
-    Installing eggs.
-    <BLANKLINE>
+    ...
     Link to http://dist.plone.org ***BLOCKED*** by --allow-hosts
-    <BLANKLINE>
-    Couldn't find index page for 'kss.core' (maybe misspelled?)
-    Getting distribution for 'kss.core'.
+    ...
     While:
       Installing eggs.
       Getting distribution for 'kss.core'.
@@ -115,7 +109,7 @@
     ... [buildout]
     ... parts=python
     ... foo = ${python:interpreter}
-    ... 
+    ...
     ... [python]
     ... recipe=zc.recipe.egg
     ... eggs=zc.buildout

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/bootstrap.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/bootstrap.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/bootstrap.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -47,17 +47,118 @@
     X...
     d  zc.buildout-...egg
 
-Now trying the `--version` option, that let you define a version for
+The buildout script it has generated is a new-style script, using a
+customized site.py.
+
+    >>> buildout_script = join(sample_buildout, 'bin', 'buildout')
+    >>> if sys.platform.startswith('win'):
+    ...     buildout_script += '-script.py'
+    >>> print open(buildout_script).read() # doctest: +ELLIPSIS
+    #...
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+        '/sample/parts/buildout',
+        ]
+    <BLANKLINE>
+    <BLANKLINE>
+    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
+    <BLANKLINE>
+    import zc.buildout.buildout
+    <BLANKLINE>
+    if __name__ == '__main__':
+        sys.exit(zc.buildout.buildout.main())
+    <BLANKLINE>
+
+The bootstrap process prefers final versions of zc.buildout, so it has
+selected the (generated-locally) 99.99 egg rather than the also-available
+100.0b1 egg.  We can see that in the buildout script's site.py.
+
+    >>> buildout_site_py = join(
+    ...     sample_buildout, 'parts', 'buildout', 'site.py')
+    >>> print open(buildout_site_py).read() # doctest: +ELLIPSIS
+    "...
+        buildout_paths = [
+            '/sample/eggs/setuptools-...egg',
+            '/sample/eggs/zc.buildout-99.99-pyN.N.egg'
+            ]
+    ...
+
+If you want to accept early releases of zc.buildout, you either need to
+specify an explicit version (using --version here and specifying the
+version in the buildout configuration file using the
+``buildout-version`` option or the ``versions`` option) or specify that you
+accept early releases by using ``--accept-buildout-test-releases`` on the
+bootstrap script.
+
+Here's an example.
+
+    >>> ignored = system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --accept-buildout-test-releases')
+    >>> print open(buildout_site_py).read() # doctest: +ELLIPSIS
+    "...
+        buildout_paths = [
+            '/sample/eggs/setuptools-...egg',
+            '/sample/eggs/zc.buildout-100.0b1-pyN.N.egg'
+            ]
+    ...
+
+Notice we are now using zc.buildout 100.0b1, a non-final release.
+
+The buildout script remembers the decision to accept early releases, and
+alerts the user.
+
+    >>> print system(join('bin', 'buildout')),
+    ... # doctest: +NORMALIZE_WHITESPACE
+    NOTE: Accepting early releases of build system packages.  Rerun bootstrap
+          without --accept-buildout-test-releases (-t) to return to default
+          behavior.
+
+This is accomplished within the script itself.
+
+    >>> print open(buildout_script).read() # doctest: +ELLIPSIS
+    #...
+    sys.argv.insert(1, 'buildout:accept-buildout-test-releases=true')
+    print ('NOTE: Accepting early releases of build system packages.  Rerun '
+           'bootstrap without --accept-buildout-test-releases (-t) to return to '
+           'default behavior.')
+    ...
+
+As the note says, to undo, you just need to re-run bootstrap without
+--accept-buildout-test-releases.
+
+    >>> ignored = system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py')
+    >>> print open(buildout_site_py).read() # doctest: +ELLIPSIS
+    "...
+        buildout_paths = [
+            '/sample/eggs/setuptools-...egg',
+            '/sample/eggs/zc.buildout-99.99-pyN.N.egg'
+            ]
+    ...
+    >>> ('buildout:accept-buildout-test-releases=true' in
+    ... open(buildout_script).read())
+    False
+
+Now we will try the `--version` option, which lets you define a version for
 `zc.buildout`. If not provided, bootstrap will look for the latest one.
 
 Let's try with an unknown version::
 
-    >>> print 'X'; print system(
+    >>> print 'XX'; print system(
     ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
     ...     'bootstrap.py --version UNKNOWN'); print 'X' # doctest: +ELLIPSIS
     ...
-    X
-    No local packages or download links found for zc.buildout==UNKNOWN
+    X...
+    No local packages or download links found for zc.buildout==UNKNOWN...
     ...
 
 Now let's try with `1.1.2`, which happens to exist::
@@ -71,11 +172,9 @@
     <BLANKLINE>
     X
 
-Let's make sure the generated `buildout` script uses it::
+Versions older than 1.5.0 put their egg dependencies in the ``buildout`` script.
+Let's make sure it was generated as we expect::
 
-    >>> buildout_script = join(sample_buildout, 'bin', 'buildout')
-    >>> if sys.platform.startswith('win'):
-    ...     buildout_script += '-script.py'
     >>> print open(buildout_script).read() # doctest: +ELLIPSIS
     #...
     <BLANKLINE>
@@ -102,7 +201,7 @@
     <BLANKLINE>
     X
 
-Let's make sure the generated `buildout` script uses it::
+Let's make sure the generated ``buildout`` script uses it::
 
     >>> print open(buildout_script).read() # doctest: +ELLIPSIS
     #...
@@ -119,49 +218,34 @@
         zc.buildout.buildout.main()
     <BLANKLINE>
 
-`zc.buildout` now can also run with `Distribute` with the `--distribute` option::
+``zc.buildout`` now can also run with `Distribute` with the `--distribute`
+option::
 
-
-    >>> print 'X'; print system(
+    >>> print 'XX'; print system(
     ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
-    ...     'bootstrap.py --distribute'); print 'X' # doctest: +ELLIPSIS
+    ...     'bootstrap.py --distribute') # doctest: +ELLIPSIS
     ...
-    X
+    X...Generated script '/sample/bin/buildout'...
+
+Let's make sure the generated ``site.py`` uses it::
+    >>> print open(buildout_site_py).read() # doctest: +ELLIPSIS
+    "...
+        buildout_paths = [
+            '/sample/eggs/distribute-...egg',
+            '/sample/eggs/zc.buildout-99.99-pyN.N.egg'
+            ]
     ...
-    Generated script '/sample/bin/buildout'.
-    <BLANKLINE>
-    X
 
-Let's make sure the generated `buildout` script uses it::
-
-    >>> print open(buildout_script).read() # doctest: +ELLIPSIS
-    #...
-    <BLANKLINE>
-    import sys
-    sys.path[0:0] = [
-      '/sample/eggs/distribute-...egg',
-      '/sample/eggs/zc.buildout-...egg',
-      ]
-    <BLANKLINE>
-    import zc.buildout.buildout
-    <BLANKLINE>
-    if __name__ == '__main__':
-        zc.buildout.buildout.main()
-    <BLANKLINE>
-
 Make sure both options can be used together::
 
-    >>> print 'X'; print system(
+    >>> print 'XX'; print system(
     ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
-    ...     'bootstrap.py --distribute --version 1.2.1'); print 'X' # doctest: +ELLIPSIS
-    ...
-    X
-    ...
-    Generated script '/sample/bin/buildout'.
-    <BLANKLINE>
-    X
+    ...     'bootstrap.py --distribute --version 1.2.1')
+    ... # doctest: +ELLIPSIS
+    X...Generated script '/sample/bin/buildout'...
 
-Let's make sure the generated `buildout` script uses ``Distribute`` *and* ``zc.buildout-1.2.1``::
+Let's make sure the old-style generated ``buildout`` script uses
+``Distribute`` *and* ``zc.buildout-1.2.1``::
 
     >>> print open(buildout_script).read() # doctest: +ELLIPSIS
     #...
@@ -184,14 +268,82 @@
     >>> f = open(conf_file, 'w')
     >>> f.write('[buildout]\nparts=\n\n')
     >>> f.close()
-    >>> print 'X'; print system(
+    >>> print 'XX'; print system(
     ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
-    ...     'bootstrap.py -c %s --distribute' % conf_file); print 'X' # doctest: +ELLIPSIS
-    ...
-    X
-    ...
-    Generated script '/sample/bin/buildout'.
+    ...     'bootstrap.py -c %s --distribute' % conf_file) # doctest: +ELLIPSIS
+    X...Generated script '/sample/bin/buildout'...
+
+You can specify a location of ez_setup.py or distribute_setup, so you
+can rely on a local or remote location.  We'll write our own ez_setup.py
+that we will also use to test some other bootstrap options.
+
+    >>> write('ez_setup.py', '''\
+    ... def use_setuptools(**kwargs):
+    ...     import sys, pprint
+    ...     pprint.pprint(kwargs, width=40)
+    ...     sys.exit()
+    ... ''')
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --setup-source=./ez_setup.py')
+    ... # doctest: +ELLIPSIS
+    {'download_delay': 0,
+     'to_dir': '...'}
     <BLANKLINE>
-    X
 
+You can also pass a download-cache, and a place in which eggs should be stored
+(they are normally stored in a temporary directory).
 
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --setup-source=./ez_setup.py '+
+    ...     '--download-base=./download-cache --eggs=eggs')
+    ... # doctest: +ELLIPSIS
+    {'download_base': '/sample/download-cache/',
+     'download_delay': 0,
+     'to_dir': '/sample/eggs'}
+    <BLANKLINE>
+
+Here's the entire help text.
+
+    >>> print system(
+    ...     zc.buildout.easy_install._safe_arg(sys.executable)+' '+
+    ...     'bootstrap.py --help'),
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    Usage: [DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
+    <BLANKLINE>
+    Bootstraps a buildout-based project.
+    <BLANKLINE>
+    Simply run this script in a directory containing a buildout.cfg, using the
+    Python that you want bin/buildout to use.
+    <BLANKLINE>
+    Note that by using --setup-source and --download-base to point to
+    local resources, you can keep this script from going over the network.
+    <BLANKLINE>
+    <BLANKLINE>
+    Options:
+      -h, --help            show this help message and exit
+      -v VERSION, --version=VERSION
+                            use a specific zc.buildout version
+      -d, --distribute      Use Distribute rather than Setuptools.
+      --setup-source=SETUP_SOURCE
+                            Specify a URL or file location for the setup file. If
+                            you use Setuptools, this will default to
+                            http://peak.telecommunity.com/dist/ez_setup.py; if you
+                            use Distribute, this will default to http://python-
+                            distribute.org/distribute_setup.py.
+      --download-base=DOWNLOAD_BASE
+                            Specify a URL or directory for downloading zc.buildout
+                            and either Setuptools or Distribute. Defaults to PyPI.
+      --eggs=EGGS           Specify a directory for storing eggs.  Defaults to a
+                            temporary directory that is deleted when the bootstrap
+                            script completes.
+      -t, --accept-buildout-test-releases
+                            Normally, if you do not specify a --version, the
+                            bootstrap script and buildout gets the newest *final*
+                            versions of zc.buildout and its recipes and extensions
+                            for you.  If you use this flag, bootstrap and buildout
+                            will get the newest releases even if they are alphas
+                            or betas.
+      -c CONFIG_FILE        Specify the path to the buildout configuration file to
+                            be used.

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2005-2009 Zope Corporation and Contributors.
+# Copyright (c) 2005-2009 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -34,6 +34,8 @@
 import sys
 import tempfile
 import UserDict
+import warnings
+import subprocess
 import zc.buildout
 import zc.buildout.download
 import zc.buildout.easy_install
@@ -48,8 +50,8 @@
 
 is_jython = sys.platform.startswith('java')
 
-if is_jython:
-    import subprocess
+_sys_executable_has_broken_dash_S = (
+    zc.buildout.easy_install._has_broken_dash_S(sys.executable))
 
 class MissingOption(zc.buildout.UserError, KeyError):
     """A required option was missing.
@@ -112,22 +114,38 @@
     return data
 
 _buildout_default_options = _annotate_section({
+    'accept-buildout-test-releases': 'false',
+    'allow-hosts': '*',
+    'allow-picked-versions': 'true',
+    'allowed-eggs-from-site-packages': '*',
+    'bin-directory': 'bin',
+    'develop-eggs-directory': 'develop-eggs',
     'eggs-directory': 'eggs',
-    'develop-eggs-directory': 'develop-eggs',
-    'bin-directory': 'bin',
+    'executable': sys.executable,
+    'exec-sitecustomize': 'true',
+    'find-links': '',
+    'include-site-packages': 'true',
+    'install-from-cache': 'false',
+    'installed': '.installed.cfg',
+    'log-format': '',
+    'log-level': 'INFO',
+    'newest': 'true',
+    'offline': 'false',
     'parts-directory': 'parts',
-    'installed': '.installed.cfg',
+    'prefer-final': 'false',
     'python': 'buildout',
-    'executable': sys.executable,
-    'log-level': 'INFO',
-    'log-format': '',
+    'relative-paths': 'false',
+    'socket-timeout': '',
+    'unzip': 'false',
+    'use-dependency-links': 'true',
     }, 'DEFAULT_VALUE')
 
 
 class Buildout(UserDict.DictMixin):
 
     def __init__(self, config_file, cloptions,
-                 user_defaults=True, windows_restart=False, command=None):
+                 user_defaults=True, windows_restart=False,
+                 command=None, args=()):
 
         __doing__ = 'Initializing.'
 
@@ -142,8 +160,7 @@
             base = os.path.dirname(config_file)
             if not os.path.exists(config_file):
                 if command == 'init':
-                    print 'Creating %r.' % config_file
-                    open(config_file, 'w').write('[buildout]\nparts = \n')
+                    self._init_config(config_file, args)
                 elif command == 'setup':
                     # Sigh. This model of a buildout instance
                     # with methods is breaking down. :(
@@ -152,6 +169,9 @@
                 else:
                     raise zc.buildout.UserError(
                         "Couldn't open %s" % config_file)
+            elif command == 'init':
+                raise zc.buildout.UserError(
+                    "%r already exists." % config_file)
 
             if config_file:
                 data['buildout']['directory'] = (os.path.dirname(config_file),
@@ -174,12 +194,13 @@
                                        '.buildout', 'default.cfg')
             if os.path.exists(user_config):
                 _update(data, _open(os.path.dirname(user_config), user_config,
-                                    [], data['buildout'].copy(), override))
+                                    [], data['buildout'].copy(), override,
+                                    set()))
 
         # load configuration files
         if config_file:
             _update(data, _open(os.path.dirname(config_file), config_file, [],
-                                data['buildout'].copy(), override))
+                                data['buildout'].copy(), override, set()))
 
         # apply command-line options
         _update(data, cloptions)
@@ -191,7 +212,7 @@
         # provide some defaults before options are parsed
         # because while parsing options those attributes might be
         # used already (Gottfried Ganssauge)
-        buildout_section = data.get('buildout')
+        buildout_section = data['buildout']
 
         # Try to make sure we have absolute paths for standard
         # directories. We do this before doing substitutions, in case
@@ -204,22 +225,30 @@
                 d = self._buildout_path(buildout_section[name+'-directory'])
                 buildout_section[name+'-directory'] = d
 
-        links = buildout_section and buildout_section.get('find-links', '')
+        # Attributes on this buildout object shouldn't be used by
+        # recipes in their __init__.  It can cause bugs, because the
+        # recipes will be instantiated below (``options = self['buildout']``)
+        # before this has completed initializing.  These attributes are
+        # left behind for legacy support but recipe authors should
+        # beware of using them.  A better practice is for a recipe to
+        # use the buildout['buildout'] options.
+        links = buildout_section['find-links']
         self._links = links and links.split() or ()
-
-        allow_hosts = buildout_section and buildout_section.get(
-             'allow-hosts', '*').split('\n')
+        allow_hosts = buildout_section['allow-hosts'].split('\n')
         self._allow_hosts = tuple([host.strip() for host in allow_hosts
                                    if host.strip() != ''])
-
         self._logger = logging.getLogger('zc.buildout')
-        self.offline = False
-        self.newest = True
+        self.offline = (buildout_section['offline'] == 'true')
+        self.newest = (buildout_section['newest'] == 'true')
+        self.accept_buildout_test_releases = (
+            buildout_section['accept-buildout-test-releases'] == 'true')
 
         ##################################################################
         ## WARNING!!!
         ## ALL ATTRIBUTES MUST HAVE REASONABLE DEFAULTS AT THIS POINT
-        ## OTHERWISE ATTRIBUTEERRORS MIGHT HAPPEN ANY TIME
+        ## OTHERWISE ATTRIBUTEERRORS MIGHT HAPPEN ANY TIME FROM RECIPES.
+        ## RECIPES SHOULD GENERALLY USE buildout['buildout'] OPTIONS, NOT
+        ## BUILDOUT ATTRIBUTES.
         ##################################################################
         # initialize some attrs and buildout directories.
         options = self['buildout']
@@ -228,7 +257,7 @@
         links = options.get('find-links', '')
         self._links = links and links.split() or ()
 
-        allow_hosts = options.get('allow-hosts', '*').split('\n')
+        allow_hosts = options['allow-hosts'].split('\n')
         self._allow_hosts = tuple([host.strip() for host in allow_hosts
                                    if host.strip() != ''])
 
@@ -246,44 +275,38 @@
 
         self._setup_logging()
 
-        offline = options.get('offline', 'false')
-        if offline not in ('true', 'false'):
-            self._error('Invalid value for offline option: %s', offline)
-        options['offline'] = offline
-        self.offline = offline == 'true'
-
-        if self.offline:
-            newest = options['newest'] = 'false'
-        else:
-            newest = options.get('newest', 'true')
-            if newest not in ('true', 'false'):
-                self._error('Invalid value for newest option: %s', newest)
-            options['newest'] = newest
-        self.newest = newest == 'true'
-
         versions = options.get('versions')
         if versions:
             zc.buildout.easy_install.default_versions(dict(self[versions]))
 
-        prefer_final = options.get('prefer-final', 'false')
-        if prefer_final not in ('true', 'false'):
-            self._error('Invalid value for prefer-final option: %s',
-                        prefer_final)
-        zc.buildout.easy_install.prefer_final(prefer_final=='true')
 
-        use_dependency_links = options.get('use-dependency-links', 'true')
-        if use_dependency_links not in ('true', 'false'):
-            self._error('Invalid value for use-dependency-links option: %s',
-                        use_dependency_links)
+        self.offline = options.get_bool('offline')
+        if self.offline:
+            options['newest'] = 'false'
+        self.newest = options.get_bool('newest')
+        zc.buildout.easy_install.prefer_final(
+            options.get_bool('prefer-final'))
+        self.accept_buildout_test_releases = options.get_bool(
+            'accept-buildout-test-releases')
         zc.buildout.easy_install.use_dependency_links(
-            use_dependency_links == 'true')
-
-        allow_picked_versions = options.get('allow-picked-versions', 'true')
-        if allow_picked_versions not in ('true', 'false'):
-            self._error('Invalid value for allow-picked-versions option: %s',
-                        allow_picked_versions)
+            options.get_bool('use-dependency-links'))
         zc.buildout.easy_install.allow_picked_versions(
-            allow_picked_versions=='true')
+            options.get_bool('allow-picked-versions'))
+        zc.buildout.easy_install.install_from_cache(
+            options.get_bool('install-from-cache'))
+        zc.buildout.easy_install.always_unzip(options.get_bool('unzip'))
+        allowed_eggs = tuple(name.strip() for name in options[
+            'allowed-eggs-from-site-packages'].split('\n'))
+        self.include_site_packages = options.get_bool('include-site-packages')
+        self.exec_sitecustomize = options.get_bool('exec-sitecustomize')
+        if (_sys_executable_has_broken_dash_S and
+            (not self.include_site_packages or allowed_eggs != ('*',))):
+            # We can't do this if the executable has a broken -S.
+            warnings.warn(zc.buildout.easy_install.BROKEN_DASH_S_WARNING)
+            self.include_site_packages = True
+        zc.buildout.easy_install.allowed_eggs_from_site_packages(allowed_eggs)
+        zc.buildout.easy_install.include_site_packages(
+            self.include_site_packages)
 
         download_cache = options.get('download-cache')
         if download_cache:
@@ -300,23 +323,6 @@
 
             zc.buildout.easy_install.download_cache(download_cache)
 
-        install_from_cache = options.get('install-from-cache')
-        if install_from_cache:
-            if install_from_cache not in ('true', 'false'):
-                self._error('Invalid value for install-from-cache option: %s',
-                            install_from_cache)
-            if install_from_cache == 'true':
-                zc.buildout.easy_install.install_from_cache(True)
-
-
-        always_unzip = options.get('unzip')
-        if always_unzip:
-            if always_unzip not in ('true', 'false'):
-                self._error('Invalid value for unzip option: %s',
-                            always_unzip)
-            if always_unzip == 'true':
-                zc.buildout.easy_install.always_unzip(True)
-
         # "Use" each of the defaults so they aren't reported as unused options.
         for name in _buildout_default_options:
             options[name]
@@ -338,11 +344,35 @@
 
         self._setup_directories()
 
+        options = self['buildout']
+
+        # Get a base working set for our distributions that corresponds to the
+        # stated desires in the configuration.
+        distributions = ['setuptools', 'zc.buildout']
+        if options.get('offline') == 'true':
+            ws = zc.buildout.easy_install.working_set(
+                distributions, options['executable'],
+                [options['develop-eggs-directory'],
+                 options['eggs-directory']],
+                prefer_final=not self.accept_buildout_test_releases,
+                )
+        else:
+            ws = zc.buildout.easy_install.install(
+                distributions, options['eggs-directory'],
+                links=self._links,
+                index=options.get('index'),
+                executable=options['executable'],
+                path=[options['develop-eggs-directory']],
+                newest=self.newest,
+                allow_hosts=self._allow_hosts,
+                prefer_final=not self.accept_buildout_test_releases,
+                )
+
         # Now copy buildout and setuptools eggs, and record destination eggs:
         entries = []
         for name in 'setuptools', 'zc.buildout':
             r = pkg_resources.Requirement.parse(name)
-            dist = pkg_resources.working_set.find(r)
+            dist = ws.find(r)
             if dist.precedence == pkg_resources.DEVELOP_DIST:
                 dest = os.path.join(self['buildout']['develop-eggs-directory'],
                                     name+'.egg-link')
@@ -358,15 +388,71 @@
                     else:
                         shutil.copy2(dist.location, dest)
 
-        # Create buildout script
+        # Create buildout script.
+        # Ideally the (possibly) new version of buildout would get a
+        # chance to write the script.  Not sure how to do that.
         ws = pkg_resources.WorkingSet(entries)
         ws.require('zc.buildout')
-        zc.buildout.easy_install.scripts(
-            ['zc.buildout'], ws, sys.executable,
-            self['buildout']['bin-directory'])
+        partsdir = os.path.join(options['parts-directory'], 'buildout')
+        if not os.path.exists(partsdir):
+            os.mkdir(partsdir)
+        # (Honor the relative-paths option.)
+        relative_paths = options.get('relative-paths', 'false')
+        if relative_paths == 'true':
+            relative_paths = options['directory']
+        elif relative_paths == 'false':
+            relative_paths = ''
+        else:
+            raise zc.buildout.UserError("relative_paths must be true or false")
+        if (self.accept_buildout_test_releases and
+            self._annotated['buildout']['accept-buildout-test-releases'][1] ==
+            'COMMAND_LINE_VALUE'):
+            # Bootstrap was called with '--accept-buildout-test-releases'.
+            # Continue to honor that setting.
+            script_initialization = _early_release_initialization_code
+        else:
+            script_initialization = ''
+        zc.buildout.easy_install.sitepackage_safe_scripts(
+            options['bin-directory'], ws, options['executable'], partsdir,
+            reqs=['zc.buildout'], relative_paths=relative_paths,
+            include_site_packages=self.include_site_packages,
+            script_initialization=script_initialization,
+            exec_sitecustomize=self.exec_sitecustomize,
+            )
 
-    init = bootstrap
+    def _init_config(self, config_file, args):
+        print 'Creating %r.' % config_file
+        f = open(config_file, 'w')
+        sep = re.compile(r'[\\/]')
+        if args:
+            eggs = '\n  '.join(a for a in args if not sep.search(a))
+            paths = '\n  '.join(
+                sep.sub(os.path.sep, a) for a in args if sep.search(a))
+            f.write('[buildout]\n'
+                    'parts = py\n'
+                    '\n'
+                    '[py]\n'
+                    'recipe = zc.recipe.egg\n'
+                    'interpreter = py\n'
+                    'eggs =\n'
+                    )
+            if eggs:
+                f.write('  %s\n' % eggs)
+            if paths:
+                f.write('extra-paths =\n  %s\n' % paths)
+                for p in [a for a in args if sep.search(a)]:
+                    if not os.path.exists(p):
+                        os.mkdir(p)
 
+        else:
+            f.write('[buildout]\nparts =\n')
+        f.close()
+
+    def init(self, args):
+        self.bootstrap(())
+        if args:
+            self.install(())
+
     def install(self, install_args):
         __doing__ = 'Installing.'
 
@@ -377,7 +463,7 @@
         # for eggs:
         sys.path.insert(0, self['buildout']['develop-eggs-directory'])
 
-        # Check for updates. This could cause the process to be rstarted
+        # Check for updates. This could cause the process to be restarted.
         self._maybe_upgrade()
 
         # load installed data
@@ -430,7 +516,7 @@
         # compute new part recipe signatures
         self._compute_part_signatures(install_parts)
 
-        # uninstall parts that are no-longer used or who's configs
+        # uninstall parts that are no-longer used or whose configs
         # have changed
         for part in reversed(installed_parts):
             if part in install_parts:
@@ -533,7 +619,7 @@
                 if installed_files is None:
                     self._logger.warning(
                         "The %s install returned None.  A path or "
-                        "iterable os paths should be returned.",
+                        "iterable of paths should be returned.",
                         part)
                     installed_files = ()
                 elif isinstance(installed_files, str):
@@ -556,7 +642,7 @@
                 self._save_installed_options(installed_part_options)
                 installed_exists = True
             else:
-                assert installed_exists
+                assert installed_exists  # nothing to tell the user here
                 self._update_installed(parts=' '.join(installed_parts))
 
         if installed_develop_eggs:
@@ -576,11 +662,11 @@
         f.close()
 
     def _uninstall_part(self, part, installed_part_options):
-        # ununstall part
+        # uninstall part
         __doing__ = 'Uninstalling %s.', part
         self._logger.info(*__doing__)
 
-        # run uinstall recipe
+        # run uninstall recipe
         recipe, entry = _recipe(installed_part_options[part])
         try:
             uninstaller = _install_and_load(
@@ -792,16 +878,24 @@
         if not self.newest:
             return
 
+        options = self['buildout']
+
+        specs = ['zc.buildout']
+        if zc.buildout.easy_install.is_distribute:
+            specs.append('distribute')
+        else:
+            specs.append('setuptools')
         ws = zc.buildout.easy_install.install(
             [
-            (spec + ' ' + self['buildout'].get(spec+'-version', '')).strip()
-            for spec in ('zc.buildout', 'setuptools')
+            (spec + ' ' + options.get(spec+'-version', '')).strip()
+            for spec in specs
             ],
-            self['buildout']['eggs-directory'],
-            links = self['buildout'].get('find-links', '').split(),
-            index = self['buildout'].get('index'),
-            path = [self['buildout']['develop-eggs-directory']],
-            allow_hosts = self._allow_hosts
+            options['eggs-directory'],
+            links = options.get('find-links', '').split(),
+            index = options.get('index'),
+            path = [options['develop-eggs-directory']],
+            allow_hosts = self._allow_hosts,
+            prefer_final=not self.accept_buildout_test_releases,
             )
 
         upgraded = []
@@ -817,7 +911,7 @@
         __doing__ = 'Upgrading.'
 
         should_run = realpath(
-            os.path.join(os.path.abspath(self['buildout']['bin-directory']),
+            os.path.join(os.path.abspath(options['bin-directory']),
                          'buildout')
             )
         if sys.platform == 'win32':
@@ -849,22 +943,52 @@
 
         # the new dist is different, so we've upgraded.
         # Update the scripts and return True
-        zc.buildout.easy_install.scripts(
-            ['zc.buildout'], ws, sys.executable,
-            self['buildout']['bin-directory'],
+        # Ideally the new version of buildout would get a chance to write the
+        # script.  Not sure how to do that.
+        partsdir = os.path.join(options['parts-directory'], 'buildout')
+        if os.path.exists(partsdir):
+            # This is primarily for unit tests, in which .py files change too
+            # fast for Python to know to regenerate the .pyc/.pyo files.
+            shutil.rmtree(partsdir)
+        os.mkdir(partsdir)
+        if (self.accept_buildout_test_releases and
+            self._annotated['buildout']['accept-buildout-test-releases'][1] ==
+            'COMMAND_LINE_VALUE'):
+            # Bootstrap was called with '--accept-buildout-test-releases'.
+            # Continue to honor that setting.
+            script_initialization = _early_release_initialization_code
+        else:
+            script_initialization = ''
+        # (Honor the relative-paths option.)
+        relative_paths = options.get('relative-paths', 'false')
+        if relative_paths == 'true':
+            relative_paths = options['directory']
+        elif relative_paths == 'false':
+            relative_paths = ''
+        else:
+            raise zc.buildout.UserError("relative_paths must be true or false")
+        zc.buildout.easy_install.sitepackage_safe_scripts(
+            options['bin-directory'], ws, options['executable'], partsdir,
+            reqs=['zc.buildout'], relative_paths=relative_paths,
+            include_site_packages=self.include_site_packages,
+            script_initialization=script_initialization,
+            exec_sitecustomize=self.exec_sitecustomize,
             )
 
         # Restart
         args = map(zc.buildout.easy_install._safe_arg, sys.argv)
         if not __debug__:
             args.insert(0, '-O')
-        args.insert(0, zc.buildout.easy_install._safe_arg (sys.executable))
+        args.insert(0, zc.buildout.easy_install._safe_arg(sys.executable))
+        # We want to make sure that our new site.py is used for rerunning
+        # buildout, so we put the partsdir in PYTHONPATH for our restart.
+        # This overrides any set PYTHONPATH, but since we generally are
+        # trying to run with a completely "clean" python (only the standard
+        # library) then that should be fine.
+        env = os.environ.copy()
+        env['PYTHONPATH'] = partsdir
+        sys.exit(subprocess.Popen(args, env=env).wait())
 
-        if is_jython:
-            sys.exit(subprocess.Popen([sys.executable] + list(args)).wait())
-        else:
-            sys.exit(os.spawnv(os.P_WAIT, sys.executable, args))
-
     def _load_extensions(self):
         __doing__ = 'Loading extensions.'
         specs = self['buildout'].get('extensions', '').split()
@@ -884,7 +1008,8 @@
                 working_set=pkg_resources.working_set,
                 links = self['buildout'].get('find-links', '').split(),
                 index = self['buildout'].get('index'),
-                newest=self.newest, allow_hosts=self._allow_hosts)
+                newest=self.newest, allow_hosts=self._allow_hosts,
+                prefer_final=not self.accept_buildout_test_releases)
 
             # Clear cache because extensions might now let us read pages we
             # couldn't read before.
@@ -915,6 +1040,7 @@
         setup = os.path.abspath(setup)
 
         fd, tsetup = tempfile.mkstemp()
+        exe = zc.buildout.easy_install._safe_arg(sys.executable)
         try:
             os.write(fd, zc.buildout.easy_install.runsetup_template % dict(
                 setuptools=pkg_resources_loc,
@@ -928,14 +1054,10 @@
                 for a in args:
                     arg_list.append(zc.buildout.easy_install._safe_arg(a))
 
-                subprocess.Popen(
-                    [zc.buildout.easy_install._safe_arg(sys.executable)]
-                    + list(tsetup)
-                    + arg_list
-                    ).wait()
+                subprocess.Popen([exe] + list(tsetup) + arg_list).wait()
 
             else:
-                os.spawnl(os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable), tsetup,
+                os.spawnl(os.P_WAIT, sys.executable, exe, tsetup,
                         *[zc.buildout.easy_install._safe_arg(a)
                             for a in args])
         finally:
@@ -1001,8 +1123,8 @@
                 path=path,
                 working_set=pkg_resources.working_set,
                 newest=buildout.newest,
-                allow_hosts=buildout._allow_hosts
-                )
+                allow_hosts=buildout._allow_hosts,
+                prefer_final=not buildout.accept_buildout_test_releases)
 
         __doing__ = 'Loading %s recipe entry %s:%s.', group, spec, entry
         return pkg_resources.load_entry_point(
@@ -1015,6 +1137,7 @@
             group, entry, spec, v)
         raise
 
+
 class Options(UserDict.DictMixin):
 
     def __init__(self, buildout, section, data):
@@ -1225,6 +1348,32 @@
                 self.name)
         return self._created
 
+    def query_bool(self, name, default=None):
+        """Given a name, return a boolean value for that name.
+
+        ``default``, if given, should be 'true', 'false', or None.
+        """
+        if default is not None:
+            value = self.setdefault(name, default=default)
+        else:
+            value = self.get(name)
+            if value is None:
+                return value
+        return _convert_bool(name, value)
+
+    def get_bool(self, name):
+        """Given a name, return a boolean value for that name.
+        """
+        return _convert_bool(name, self[name])
+
+
+def _convert_bool(name, value):
+    if value not in ('true', 'false'):
+        raise zc.buildout.UserError(
+            'Invalid value for %s option: %s' % (name, value))
+    else:
+        return value == 'true'
+
 _spacey_nl = re.compile('[ \t\r\f\v]*\n[ \t\r\f\v\n]*'
                         '|'
                         '^[ \t\r\f\v]+'
@@ -1269,17 +1418,19 @@
     for option, value in items:
         _save_option(option, value, f)
 
-def _open(base, filename, seen, dl_options, override):
+def _open(base, filename, seen, dl_options, override, downloaded):
     """Open a configuration file and return the result as a dictionary,
 
     Recursively open other files based on buildout options found.
     """
     _update_section(dl_options, override)
     _dl_options = _unannotate_section(dl_options.copy())
+    newest = _convert_bool('newest', _dl_options.get('newest', 'false'))
+    fallback = newest and not (filename in downloaded)
+    download = zc.buildout.download.Download(
+        _dl_options, cache=_dl_options.get('extends-cache'),
+        fallback=fallback, hash_name=True)
     is_temp = False
-    download = zc.buildout.download.Download(
-        _dl_options, cache=_dl_options.get('extends-cache'), fallback=True,
-        hash_name=True)
     if _isurl(filename):
         path, is_temp = download(filename)
         fp = open(path)
@@ -1297,6 +1448,7 @@
         filename = os.path.join(base, filename)
         fp = open(filename)
         base = os.path.dirname(filename)
+    downloaded.add(filename)
 
     if filename in seen:
         if is_temp:
@@ -1316,12 +1468,15 @@
         fp.close()
         os.remove(path)
 
-    extends = extended_by = None
+    extends = None
     for section in parser.sections():
         options = dict(parser.items(section))
         if section == 'buildout':
             extends = options.pop('extends', extends)
-            extended_by = options.pop('extended-by', extended_by)
+            if 'extended-by' in options:
+                raise zc.buildout.UserError(
+                    'No-longer supported "extended-by" option found in %s.' %
+                    filename)
         result[section] = options
 
     result = _annotate(result, filename)
@@ -1331,25 +1486,23 @@
 
     if extends:
         extends = extends.split()
-        eresult = _open(base, extends.pop(0), seen, dl_options, override)
+        eresult = _open(base, extends.pop(0), seen, dl_options, override,
+                        downloaded)
         for fname in extends:
-            _update(eresult, _open(base, fname, seen, dl_options, override))
+            _update(eresult, _open(base, fname, seen, dl_options, override,
+                    downloaded))
         result = _update(eresult, result)
 
-    if extended_by:
-        self._logger.warn(
-            "The extendedBy option is deprecated.  Stop using it."
-            )
-        for fname in extended_by.split():
-            result = _update(result,
-                             _open(base, fname, seen, dl_options, override))
-
     seen.pop()
     return result
 
 
 ignore_directories = '.svn', 'CVS'
+_dir_hashes = {}
 def _dir_hash(dir):
+    dir_hash = _dir_hashes.get(dir, None)
+    if dir_hash is not None:
+        return dir_hash
     hash = md5()
     for (dirpath, dirnames, filenames) in os.walk(dir):
         dirnames[:] = [n for n in dirnames if n not in ignore_directories]
@@ -1361,7 +1514,8 @@
         hash.update(' '.join(filenames))
         for name in filenames:
             hash.update(open(os.path.join(dirpath, name)).read())
-    return hash.digest().encode('base64').strip()
+    _dir_hashes[dir] = dir_hash = hash.digest().encode('base64').strip()
+    return dir_hash
 
 def _dists_sig(dists):
     result = []
@@ -1435,7 +1589,7 @@
     sys.exit(1)
 
 _internal_error_template = """
-An internal error occured due to a bug in either zc.buildout or in a
+An internal error occurred due to a bug in either zc.buildout or in a
 recipe being used:
 """
 
@@ -1447,6 +1601,13 @@
                               % (section, ' '.join(map(repr, unused)))
                               )
 
+_early_release_initialization_code = """\
+sys.argv.insert(1, 'buildout:accept-buildout-test-releases=true')
+print ('NOTE: Accepting early releases of build system packages.  Rerun '
+       'bootstrap without --accept-buildout-test-releases (-t) to return to '
+       'default behavior.')
+"""
+
 _usage = """\
 Usage: buildout [options] [assignments] [command [command arguments]]
 
@@ -1510,6 +1671,11 @@
     will be started. This is especially useful for debuging recipe
     problems.
 
+  -s
+
+    Squelch warnings about using an executable with a broken -S
+    implementation.
+
 Assignments are of the form: section:option=value and are used to
 provide configuration options that override those given in the
 configuration file.  For example, to run the buildout in offline mode,
@@ -1575,11 +1741,12 @@
     windows_restart = False
     user_defaults = True
     debug = False
+    ignore_broken_dash_s = False
     while args:
         if args[0][0] == '-':
             op = orig_op = args.pop(0)
             op = op[1:]
-            while op and op[0] in 'vqhWUoOnNDA':
+            while op and op[0] in 'vqhWUoOnNDAs':
                 if op[0] == 'v':
                     verbosity += 10
                 elif op[0] == 'q':
@@ -1598,6 +1765,8 @@
                     options.append(('buildout', 'newest', 'false'))
                 elif op[0] == 'D':
                     debug = True
+                elif op[0] == 's':
+                    ignore_broken_dash_s = True
                 else:
                     _help()
                 op = op[1:]
@@ -1641,6 +1810,17 @@
             # The rest should be commands, so we'll stop here
             break
 
+    if verbosity < 0 or ignore_broken_dash_s:
+        broken_dash_S_filter_action = 'ignore'
+    elif verbosity == 0: # This is the default.
+        broken_dash_S_filter_action = 'once'
+    else:
+        broken_dash_S_filter_action = 'default'
+    warnings.filterwarnings(
+        broken_dash_S_filter_action,
+        re.escape(
+            zc.buildout.easy_install.BROKEN_DASH_S_WARNING),
+        UserWarning)
     if verbosity:
         options.append(('buildout', 'verbosity', str(verbosity)))
 
@@ -1657,10 +1837,9 @@
     try:
         try:
             buildout = Buildout(config_file, options,
-                                user_defaults, windows_restart, command)
+                                user_defaults, windows_restart,
+                                command, args)
             getattr(buildout, command)(args)
-        except SystemExit:
-            pass
         except Exception, v:
             _doing()
             exc_info = sys.exc_info()

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/buildout.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -56,10 +56,9 @@
     -  setuptools-0.6-py2.4.egg
     -  zc.buildout-1.0-py2.4.egg
 
-The develop-eggs and parts directories are initially empty:
+The develop-eggs directory is initially empty:
 
     >>> ls(sample_buildout, 'develop-eggs')
-    >>> ls(sample_buildout, 'parts')
 
 The develop-eggs directory holds egg links for software being
 developed in the buildout.  We separate develop-eggs and other eggs to
@@ -69,6 +68,12 @@
 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
@@ -458,7 +463,7 @@
     While:
       Installing data-dir.
     <BLANKLINE>
-    An internal error occured due to a bug in either zc.buildout or in a
+    An internal error occurred due to a bug in either zc.buildout or in a
     recipe being used:
     Traceback (most recent call last):
       ...
@@ -490,7 +495,7 @@
     While:
       Installing data-dir.
     <BLANKLINE>
-    An internal error occured due to a bug in either zc.buildout or in a
+    An internal error occurred due to a bug in either zc.buildout or in a
     recipe being used:
     Traceback (most recent call last):
     ...
@@ -567,7 +572,7 @@
     While:
       Installing data-dir.
     <BLANKLINE>
-    An internal error occured due to a bug in either zc.buildout or in a
+    An internal error occurred due to a bug in either zc.buildout or in a
     recipe being used:
     Traceback (most recent call last):
     ...
@@ -576,7 +581,7 @@
 .. Wait for the file to really disappear. My linux is weird.
 
     >>> wait_until("foo goes away", lambda : not os.path.exists('foo'),
-    ...            timeout=100)
+    ...            timeout=200)
 
 we get the same error, but we don't get the directory left behind:
 
@@ -648,7 +653,7 @@
     While:
       Installing data-dir.
     <BLANKLINE>
-    An internal error occured due to a bug in either zc.buildout or in a
+    An internal error occurred due to a bug in either zc.buildout or in a
     recipe being used:
     Traceback (most recent call last):
     ...
@@ -724,6 +729,14 @@
     ==================
     <BLANKLINE>
     [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
@@ -734,20 +747,42 @@
         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
     <BLANKLINE>
     [data-dir]
     path= foo bins
@@ -985,7 +1020,7 @@
 ---------------------------
 
 A section (other than the buildout section) can extend one or more
-other sections using the ``<`` option.  Options from the referenced
+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.
@@ -2194,17 +2229,25 @@
 
     >>> print system(buildout+' -vv'), # doctest: +NORMALIZE_WHITESPACE
     Installing 'zc.buildout', 'setuptools'.
-    We have a develop egg: zc.buildout 1.0.0.
+    We have a develop egg: zc.buildout X.X.
     We have the best distribution that satisfies 'setuptools'.
-    Picked: setuptools = 0.6
+    Picked: setuptools = V.V
     <BLANKLINE>
     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
-    executable = /usr/local/bin/python2.3
+    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
@@ -2212,7 +2255,12 @@
     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
     <BLANKLINE>
 
@@ -2220,6 +2268,37 @@
 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
@@ -2240,10 +2319,27 @@
    *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
@@ -2273,6 +2369,33 @@
    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.
@@ -2297,8 +2420,16 @@
     Creating directory '/sample-bootstrapped/develop-eggs'.
     Generated script '/sample-bootstrapped/bin/buildout'.
 
-Note that a basic setup.cfg was created for us.
+Note that a basic setup.cfg was created for us.  This is because we
+provided an 'init' argument.  By default, the generated
+``setup.cfg`` is as minimal as it could be:
 
+    >>> cat(sample_bootstrapped, 'setup.cfg')
+    [buildout]
+    parts =
+
+We also get other buildout artifacts:
+
     >>> ls(sample_bootstrapped)
     d  bin
     d  develop-eggs
@@ -2319,9 +2450,58 @@
 directory if the original buildout had develop eggs for either
 buildout or setuptools.)
 
-Note that the buildout script was installed but not run.  To run
-the buildout, we'd have to run the installed buildout script.
+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
+    <BLANKLINE>
+    import os
+    <BLANKLINE>
+    join = os.path.join
+    base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+    base = os.path.dirname(base)
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+        join(base, 'parts/buildout'),
+        ]
+    <BLANKLINE>
+    <BLANKLINE>
+    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
+    <BLANKLINE>
+    import zc.buildout.buildout
+    <BLANKLINE>
+    if __name__ == '__main__':
+        sys.exit(zc.buildout.buildout.main())
+    <BLANKLINE>
+
+
+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:
@@ -2350,7 +2530,94 @@
     Creating directory '/sample-bootstrapped2/develop-eggs'.
     Generated script '/sample-bootstrapped2/bin/buildout'.
 
+Similarly, if there is a configuration file and we use the init
+command, we'll get an error that the configuration file already
+exists:
 
+    >>> print system(buildout
+    ...              +' -c'+os.path.join(sample_bootstrapped, 'setup.cfg')
+    ...              +' init'),
+    While:
+      Initializing.
+    Error: '/sample-bootstrapped/setup.cfg' already exists.
+
+Initial eggs
+------------
+
+When using the ``init`` command, you can specify distribution requirements
+or paths to use:
+
+    >>> cd(sample_bootstrapped)
+    >>> remove('setup.cfg')
+    >>> print system(buildout + ' -csetup.cfg init demo other ./src'),
+    Creating '/sample-bootstrapped/setup.cfg'.
+    Generated script '/sample-bootstrapped/bin/buildout'.
+    Getting distribution for 'zc.recipe.egg'.
+    Got zc.recipe.egg 1.3.3dev.
+    Installing py.
+    Getting distribution for 'demo'.
+    Got demo 0.4c1.
+    Getting distribution for 'other'.
+    Got other 1.0.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated script '/sample-bootstrapped/bin/demo'.
+    Generated interpreter '/sample-bootstrapped/bin/py'.
+
+This causes a ``py`` part to be included that sets up a custom python
+interpreter with the given requirements or paths:
+
+    >>> cat('setup.cfg')
+    [buildout]
+    parts = py
+    <BLANKLINE>
+    [py]
+    recipe = zc.recipe.egg
+    interpreter = py
+    eggs =
+      demo
+      other
+    extra-paths =
+      ./src
+
+Passing requirements or paths causes the the builout to be run as part
+of initialization.  In the example above, we got a number of
+distributions installed and 2 scripts generated.  The first, ``demo``,
+was defined by the ``demo`` project. The second, ``py`` was defined by
+the generated configuration.  It's a "custom interpreter" that behaves
+like a standard Python interpeter, except that includes the specified
+eggs and extra paths in it's Python path.
+
+We specified a source directory that didn't exist. Buildout created it
+for us:
+
+    >>> ls('.')
+    -  .installed.cfg
+    d  bin
+    d  develop-eggs
+    d  eggs
+    d  parts
+    -  setup.cfg
+    d  src
+
+    >>> uncd()
+
+.. Make sure it works if the dir is already there:
+
+    >>> cd(sample_bootstrapped)
+    >>> _ = system(buildout + ' -csetup.cfg buildout:parts=')
+    >>> remove('setup.cfg')
+    >>> print system(buildout + ' -csetup.cfg init demo other ./src'),
+    Creating '/sample-bootstrapped/setup.cfg'.
+    Installing py.
+    Generated script '/sample-bootstrapped/bin/demo'.
+    Generated interpreter '/sample-bootstrapped/bin/py'.
+
+.. cleanup
+
+    >>> _ = system(buildout + ' -csetup.cfg buildout:parts=')
+    >>> uncd()
+
 Newest and Offline Modes
 ------------------------
 
@@ -2373,26 +2640,34 @@
 Preferring Final Releases
 -------------------------
 
-Currently, when searching for new releases, 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::
+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
+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.  The buildout
-prefer-final option can be used to override this behavior.
+are used even if newer non-final releases are available.
 
-In buildout version 2, final releases will be preferred by default.
-You will then need to use a false value for prefer-final to get the
-newest releases.
+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
 ---------------------
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/debugging.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/debugging.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/debugging.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -5,12 +5,12 @@
 always obvious why.  Errors can occur due to problems in user input or
 due to bugs in zc.buildout or recipes.  When an error occurs, Python's
 post-mortem debugger can be used to inspect the state of the buildout
-or recipe code were there error occured.  To enable this, use the -D
+or recipe code where the error occurred.  To enable this, use the -D
 option to the buildout.  Let's create a recipe that has a bug:
 
     >>> mkdir(sample_buildout, 'recipes')
 
-    >>> write(sample_buildout, 'recipes', 'mkdir.py', 
+    >>> write(sample_buildout, 'recipes', 'mkdir.py',
     ... """
     ... import os, zc.buildout
     ...
@@ -35,7 +35,7 @@
     >>> write(sample_buildout, 'recipes', 'setup.py',
     ... """
     ... from setuptools import setup
-    ... 
+    ...
     ... setup(name = "recipes",
     ...       entry_points = {'zc.buildout': ['mkdir = mkdir:Mkdir']},
     ...       )

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/dependencylinks.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/dependencylinks.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/dependencylinks.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -20,7 +20,7 @@
 
 Turn on logging on this server so that we can see when eggs are pulled
 from it.
-    
+
     >>> get(link_server2 + 'enable_server_logging')
     GET 200 /enable_server_logging
     ''
@@ -83,10 +83,9 @@
     ...     for egg in glob(join(sample_buildout, 'eggs', 'demoneeded*.egg')):
     ...         remove(sample_buildout, 'eggs', egg)
     >>> remove_demoneeded_egg()
-    >>> print system(buildout)
+    >>> print system(buildout) # doctest: +ELLIPSIS
     Develop: '/sample-buildout/depdemo'
-    Updating eggs.
-    Couldn't find index page for 'demoneeded' (maybe misspelled?)
+    ...
     Getting distribution for 'demoneeded'.
     While:
       Updating eggs.

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2009 Zope Corporation and Contributors.
+# Copyright (c) 2009 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -45,11 +45,14 @@
 
     Handles the download cache and offline mode.
 
-    Download(options=None, cache=None, namespace=None, hash_name=False)
+    Download(options=None, cache=None, namespace=None,
+             offline=False, fallback=False, hash_name=False, logger=None)
 
     options: mapping of buildout options (e.g. a ``buildout`` config section)
     cache: path to the download cache (excluding namespaces)
     namespace: namespace directory to use inside the cache
+    offline: whether to operate in offline mode
+    fallback: whether to use the cache as a fallback (try downloading first)
     hash_name: whether to use a hash of the URL as cache file name
     logger: an optional logger to receive download-related log messages
 
@@ -118,7 +121,7 @@
         cached_path = os.path.join(cache_dir, cache_key)
 
         self.logger.debug('Searching cache at %s' % cache_dir)
-        if os.path.isfile(cached_path):
+        if os.path.exists(cached_path):
             is_temp = False
             if self.fallback:
                 try:
@@ -149,8 +152,11 @@
         returned and the client code is responsible for cleaning it up.
 
         """
+        # Make sure the drive letter in windows-style file paths isn't
+        # interpreted as a URL scheme.
         if re.match(r"^[A-Za-z]:\\", url):
             url = 'file:' + url
+
         parsed_url = urlparse.urlparse(url, 'file')
         url_scheme, _, url_path = parsed_url[:3]
         if url_scheme == 'file':
@@ -169,16 +175,19 @@
         urllib._urlopener = url_opener
         handle, tmp_path = tempfile.mkstemp(prefix='buildout-')
         try:
-            try:
-                tmp_path, headers = urllib.urlretrieve(url, tmp_path)
-                if not check_md5sum(tmp_path, md5sum):
-                    raise ChecksumError(
-                        'MD5 checksum mismatch downloading %r' % url)
-            finally:
-                os.close(handle)
-        except:
+            tmp_path, headers = urllib.urlretrieve(url, tmp_path)
+            if not check_md5sum(tmp_path, md5sum):
+                raise ChecksumError(
+                    'MD5 checksum mismatch downloading %r' % url)
+        except IOError, e:
             os.remove(tmp_path)
+            raise zc.buildout.UserError("Error downloading extends for URL "
+                              "%s: %r" % (url, e[1:3]))
+        except Exception, e:
+            os.remove(tmp_path)
             raise
+        finally:
+            os.close(handle)
 
         if path:
             shutil.move(tmp_path, path)
@@ -244,8 +253,11 @@
     if dest is None or realpath(dest) == realpath(source):
         return source
 
-    try:
-        os.link(source, dest)
-    except (AttributeError, OSError):
-        shutil.copyfile(source, dest)
+    if os.path.isdir(source):
+        shutil.copytree(source, dest)
+    else:
+        try:
+            os.link(source, dest)
+        except (AttributeError, OSError):
+            shutil.copyfile(source, dest)
     return dest

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/download.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -254,6 +254,13 @@
 
 >>> remove(path)
 
+If the file is completely missing it should notify the user of the error:
+
+>>> download(server_url+'bar.txt')
+Traceback (most recent call last):
+UserError: Error downloading extends for URL http://localhost/bar.txt: (404, 'Not Found')
+>>> ls(cache)
+
 Finally, let's see what happens if the download cache to be used doesn't exist
 as a directory in the file system yet:
 
@@ -537,7 +544,26 @@
 >>> path, is_temp = Download()(server_url+'foo.txt', md5(text).hexdigest())
 >>> remove(path)
 
+When "downloading" a directory given by file-system path or ``file:`` URL and
+using a download cache at the same time, the cached directory wasn't handled
+correctly. Consequently, the cache was defeated and an attempt to cache the
+directory a second time broke. This is how it should work:
 
+>>> download = Download(cache=cache)
+>>> dirpath = join(server_data, 'some_directory')
+>>> mkdir(dirpath)
+>>> dest, _ = download(dirpath)
+
+If we now modify the source tree, the second download will produce the
+original one from the cache:
+
+>>> mkdir(join(dirpath, 'foo'))
+>>> ls(dirpath)
+d foo
+>>> dest, _ = download(dirpath)
+>>> ls(dest)
+
+
 Clean up
 --------
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 #############################################################################
 #
-# Copyright (c) 2005 Zope Corporation and Contributors.
+# Copyright (c) 2005 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -19,6 +19,7 @@
 """
 
 import distutils.errors
+import fnmatch
 import glob
 import logging
 import os
@@ -32,6 +33,7 @@
 import subprocess
 import sys
 import tempfile
+import warnings
 import zc.buildout
 import zipimport
 
@@ -50,24 +52,120 @@
 
 is_win32 = sys.platform == 'win32'
 is_jython = sys.platform.startswith('java')
+is_distribute = (
+    pkg_resources.Requirement.parse('setuptools').key=='distribute')
 
+BROKEN_DASH_S_WARNING = (
+    'Buildout has been asked to exclude or limit site-packages so that '
+    'builds can be repeatable when using a system Python.  However, '
+    'the chosen Python executable has a broken implementation of -S (see '
+    'https://bugs.launchpad.net/virtualenv/+bug/572545 for an example '
+    "problem) and this breaks buildout's ability to isolate site-packages.  "
+    "If the executable already has a clean site-packages (e.g., "
+    "using virtualenv's ``--no-site-packages`` option) you may be getting "
+    'equivalent repeatability.  To silence this warning, use the -s argument '
+    'to the buildout script.  Alternatively, use a Python executable with a '
+    'working -S (such as a standard Python binary).')
+
 if is_jython:
     import java.lang.System
     jython_os_name = (java.lang.System.getProperties()['os.name']).lower()
 
-
 setuptools_loc = pkg_resources.working_set.find(
     pkg_resources.Requirement.parse('setuptools')
     ).location
 
-# Include buildout and setuptools eggs in paths
-buildout_and_setuptools_path = [
-    setuptools_loc,
-    pkg_resources.working_set.find(
-        pkg_resources.Requirement.parse('zc.buildout')).location,
-    ]
+# Include buildout and setuptools eggs in paths.  We prevent dupes just to
+# keep from duplicating any log messages about them.
+buildout_loc = pkg_resources.working_set.find(
+    pkg_resources.Requirement.parse('zc.buildout')).location
+buildout_and_setuptools_path = [setuptools_loc]
+if os.path.normpath(setuptools_loc) != os.path.normpath(buildout_loc):
+    buildout_and_setuptools_path.append(buildout_loc)
 
+def _has_broken_dash_S(executable):
+    """Detect https://bugs.launchpad.net/virtualenv/+bug/572545 ."""
+    # The first attempt here was to simply have the executable attempt to import
+    # ConfigParser and return the return code. That worked except for tests on
+    # Windows, where the return code was wrong for the fake Python executable
+    # generated by the virtualenv.txt test, apparently because setuptools' .exe
+    # file does not pass the -script.py's returncode back properly, at least in
+    # some circumstances. Therefore...print statements.
+    stdout, stderr = subprocess.Popen(
+        [executable, '-S', '-c',
+         'try:\n'
+         '    import ConfigParser\n'
+         'except ImportError:\n'
+         '    print 1\n'
+         'else:\n'
+         '    print 0\n'],
+        stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+    return bool(int(stdout.strip()))
 
+def _get_system_paths(executable):
+    """Return lists of standard lib and site paths for executable.
+    """
+    # We want to get a list of the site packages, which is not easy.
+    # The canonical way to do this is to use
+    # distutils.sysconfig.get_python_lib(), but that only returns a
+    # single path, which does not reflect reality for many system
+    # Pythons, which have multiple additions.  Instead, we start Python
+    # with -S, which does not import site.py and set up the extra paths
+    # like site-packages or (Ubuntu/Debian) dist-packages and
+    # python-support. We then compare that sys.path with the normal one
+    # (minus user packages if this is Python 2.6, because we don't
+    # support those (yet?).  The set of the normal one minus the set of
+    # the ones in ``python -S`` is the set of packages that are
+    # effectively site-packages.
+    #
+    # The given executable might not be the current executable, so it is
+    # appropriate to do another subprocess to figure out what the
+    # additional site-package paths are. Moreover, even if this
+    # executable *is* the current executable, this code might be run in
+    # the context of code that has manipulated the sys.path--for
+    # instance, to add local zc.buildout or setuptools eggs.
+    def get_sys_path(*args, **kwargs):
+        cmd = [executable]
+        cmd.extend(args)
+        cmd.extend([
+            "-c", "import sys, os;"
+            "print repr([os.path.normpath(p) for p in sys.path if p])"])
+        # Windows needs some (as yet to be determined) part of the real env.
+        env = os.environ.copy()
+        # We need to make sure that PYTHONPATH, which will often be set
+        # to include a custom buildout-generated site.py, is not set, or
+        # else we will not get an accurate sys.path for the executable.
+        env.pop('PYTHONPATH', None)
+        env.update(kwargs)
+        _proc = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+        stdout, stderr = _proc.communicate();
+        if _proc.returncode:
+            raise RuntimeError(
+                'error trying to get system packages:\n%s' % (stderr,))
+        res = eval(stdout.strip())
+        try:
+            res.remove('.')
+        except ValueError:
+            pass
+        return res
+    stdlib = get_sys_path('-S') # stdlib only
+    no_user_paths = get_sys_path(PYTHONNOUSERSITE='x')
+    site_paths = [p for p in no_user_paths if p not in stdlib]
+    return (stdlib, site_paths)
+
+def _get_version_info(executable):
+    cmd = [executable, '-Sc',
+           'import sys; print(repr(tuple(x for x in sys.version_info)))']
+    _proc = subprocess.Popen(
+        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    stdout, stderr = _proc.communicate();
+    if _proc.returncode:
+        raise RuntimeError(
+            'error trying to get system packages:\n%s' % (stderr,))
+    return eval(stdout.strip())
+
+
 class IncompatibleVersionError(zc.buildout.UserError):
     """A specified version is incompatible with a given requirement.
     """
@@ -109,7 +207,12 @@
 
 
 _indexes = {}
-def _get_index(executable, index_url, find_links, allow_hosts=('*',)):
+def _get_index(executable, index_url, find_links, allow_hosts=('*',),
+               path=None):
+    # If path is None, the index will use sys.path.  If you provide an empty
+    # path ([]), it will complain uselessly about missing index pages for
+    # packages found in the paths that you expect to use.  Therefore, this path
+    # is always the same as the _env path in the Installer.
     key = executable, index_url, tuple(find_links)
     index = _indexes.get(key)
     if index is not None:
@@ -118,7 +221,8 @@
     if index_url is None:
         index_url = default_index_url
     index = AllowHostsPackageIndex(
-        index_url, hosts=allow_hosts, python=_get_version(executable)
+        index_url, hosts=allow_hosts, search_path=path,
+        python=_get_version(executable)
         )
 
     if find_links:
@@ -131,15 +235,78 @@
 
 if is_win32:
     # work around spawn lamosity on windows
-    # XXX need safe quoting (see the subproces.list2cmdline) and test
+    # XXX need safe quoting (see the subprocess.list2cmdline) and test
     def _safe_arg(arg):
         return '"%s"' % arg
 else:
     _safe_arg = str
 
-_easy_install_cmd = _safe_arg(
-    'from setuptools.command.easy_install import main; main()'
-    )
+# The following string is used to run easy_install in
+# Installer._call_easy_install.  It is usually started with python -S
+# (that is, don't import site at start).  That flag, and all of the code
+# in this snippet above the last two lines, exist to work around a
+# relatively rare problem.  If
+#
+# - your buildout configuration is trying to install a package that is within
+#   a namespace package, and
+#
+# - you use a Python that has a different version of this package
+#   installed in in its site-packages using
+#   --single-version-externally-managed (that is, using the mechanism
+#   sometimes used by system packagers:
+#   http://peak.telecommunity.com/DevCenter/setuptools#install-command ), and
+#
+# - the new package tries to do sys.path tricks in the setup.py to get a
+#   __version__,
+#
+# then the older package will be loaded first, making the setup version
+# the wrong number. While very arguably packages simply shouldn't do
+# the sys.path tricks, some do, and we don't want buildout to fall over
+# when they do.
+#
+# The namespace packages installed in site-packages with
+# --single-version-externally-managed use a mechanism that cause them to
+# be processed when site.py is imported  (see
+# http://mail.python.org/pipermail/distutils-sig/2009-May/011730.html
+# for another description of the problem).  Simply starting Python with
+# -S addresses the problem in Python 2.4 and 2.5, but Python 2.6's
+# distutils imports a value from the site module, so we unfortunately
+# have to do more drastic surgery in the _easy_install_preface code below.
+#
+# Here's an example of the .pth files created by setuptools when using that
+# flag:
+#
+# import sys,new,os;
+# p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('<NAMESPACE>',));
+# ie = os.path.exists(os.path.join(p,'__init__.py'));
+# m = not ie and sys.modules.setdefault('<NAMESPACE>',new.module('<NAMESPACE>'));
+# mp = (m or []) and m.__dict__.setdefault('__path__',[]);
+# (p not in mp) and mp.append(p)
+#
+# The code, below, then, runs under -S, indicating that site.py should
+# not be loaded initially.  It gets the initial sys.path under these
+# circumstances, and then imports site (because Python 2.6's distutils
+# will want it, as mentioned above). It then reinstates the old sys.path
+# value. Then it removes namespace packages (created by the setuptools
+# code above) from sys.modules.  It identifies namespace packages by
+# iterating over every loaded module.  It first looks if there is a
+# __path__, so it is a package; and then it sees if that __path__ does
+# not have an __init__.py.  (Note that PEP 382,
+# http://www.python.org/dev/peps/pep-0382, makes it possible to have a
+# namespace package that has an __init__.py, but also should make it
+# unnecessary for site.py to preprocess these packages, so it should be
+# fine, as far as can be guessed as of this writing.)  Finally, it
+# imports easy_install and runs it.
+_easy_install_preface = '''\
+import sys,os;\
+p = sys.path[:];\
+import site;\
+sys.path[:] = p;\
+[sys.modules.pop(k) for k, v in sys.modules.items()\
+ if hasattr(v, '__path__') and len(v.__path__)==1 and\
+ not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))];'''
+_easy_install_cmd = (
+    'from setuptools.command.easy_install import main;main()')
 
 
 class Installer:
@@ -151,6 +318,8 @@
     _use_dependency_links = True
     _allow_picked_versions = True
     _always_unzip = False
+    _include_site_packages = True
+    _allowed_eggs_from_site_packages = ('*',)
 
     def __init__(self,
                  dest=None,
@@ -162,7 +331,10 @@
                  newest=True,
                  versions=None,
                  use_dependency_links=None,
-                 allow_hosts=('*',)
+                 allow_hosts=('*',),
+                 include_site_packages=None,
+                 allowed_eggs_from_site_packages=None,
+                 prefer_final=None,
                  ):
         self._dest = dest
         self._allow_hosts = allow_hosts
@@ -176,15 +348,47 @@
 
         if use_dependency_links is not None:
             self._use_dependency_links = use_dependency_links
+        if prefer_final is not None:
+            self._prefer_final = prefer_final
         self._links = links = list(_fix_file_links(links))
         if self._download_cache and (self._download_cache not in links):
             links.insert(0, self._download_cache)
 
         self._index_url = index
         self._executable = executable
+        self._has_broken_dash_S = _has_broken_dash_S(self._executable)
         if always_unzip is not None:
             self._always_unzip = always_unzip
-        path = (path and path[:] or []) + buildout_and_setuptools_path
+        path = (path and path[:] or [])
+        if include_site_packages is not None:
+            self._include_site_packages = include_site_packages
+        if allowed_eggs_from_site_packages is not None:
+            self._allowed_eggs_from_site_packages = tuple(
+                allowed_eggs_from_site_packages)
+        if self._has_broken_dash_S:
+            if (not self._include_site_packages or
+                self._allowed_eggs_from_site_packages != ('*',)):
+                # We can't do this if the executable has a broken -S.
+                warnings.warn(BROKEN_DASH_S_WARNING)
+                self._include_site_packages = True
+                self._allowed_eggs_from_site_packages = ('*',)
+            self._easy_install_cmd = _easy_install_cmd
+        else:
+            self._easy_install_cmd = _easy_install_preface + _easy_install_cmd
+        self._easy_install_cmd = _safe_arg(self._easy_install_cmd)
+        stdlib, self._site_packages = _get_system_paths(executable)
+        version_info = _get_version_info(executable)
+        if version_info == sys.version_info:
+            # Maybe we can add the buildout and setuptools path.  If we
+            # are including site_packages, we only have to include the extra
+            # bits here, so we don't duplicate.  On the other hand, if we
+            # are not including site_packages, we only want to include the
+            # parts that are not in site_packages, so the code is the same.
+            path.extend(
+                set(buildout_and_setuptools_path).difference(
+                    self._site_packages))
+        if self._include_site_packages:
+            path.extend(self._site_packages)
         if dest is not None and dest not in path:
             path.insert(0, dest)
         self._path = path
@@ -193,13 +397,42 @@
         self._newest = newest
         self._env = pkg_resources.Environment(path,
                                               python=_get_version(executable))
-        self._index = _get_index(executable, index, links, self._allow_hosts)
+        self._index = _get_index(executable, index, links, self._allow_hosts,
+                                 self._path)
 
         if versions is not None:
             self._versions = versions
 
+    _allowed_eggs_from_site_packages_regex = None
+    def allow_site_package_egg(self, name):
+        if (not self._include_site_packages or
+            not self._allowed_eggs_from_site_packages):
+            # If the answer is a blanket "no," perform a shortcut.
+            return False
+        if self._allowed_eggs_from_site_packages_regex is None:
+            pattern = '(%s)' % (
+                '|'.join(
+                    fnmatch.translate(name)
+                    for name in self._allowed_eggs_from_site_packages),
+                )
+            self._allowed_eggs_from_site_packages_regex = re.compile(pattern)
+        return bool(self._allowed_eggs_from_site_packages_regex.match(name))
+
     def _satisfied(self, req, source=None):
-        dists = [dist for dist in self._env[req.project_name] if dist in req]
+        # We get all distributions that match the given requirement.  If we are
+        # not supposed to include site-packages for the given egg, we also
+        # filter those out. Even if include_site_packages is False and so we
+        # have excluded site packages from the _env's paths (see
+        # Installer.__init__), we need to do the filtering here because an
+        # .egg-link, such as one for setuptools or zc.buildout installed by
+        # zc.buildout.buildout.Buildout.bootstrap, can indirectly include a
+        # path in our _site_packages.
+        dists = [dist for dist in self._env[req.project_name] if (
+                    dist in req and (
+                        dist.location not in self._site_packages or
+                        self.allow_site_package_egg(dist.project_name))
+                    )
+                ]
         if not dists:
             logger.debug('We have no distributions for %s that satisfies %r.',
                          req.project_name, str(req))
@@ -210,7 +443,12 @@
         # env.__getitem__
 
         for dist in dists:
-            if (dist.precedence == pkg_resources.DEVELOP_DIST):
+            if (dist.precedence == pkg_resources.DEVELOP_DIST and
+                dist.location not in self._site_packages):
+                # System eggs are sometimes installed as develop eggs.
+                # Those are not the kind of develop eggs we are looking for
+                # here: we want ones that the buildout itself has locally as
+                # develop eggs.
                 logger.debug('We have a develop egg: %s', dist)
                 return dist, None
 
@@ -301,7 +539,9 @@
         try:
             path = setuptools_loc
 
-            args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(tmp))
+            args = ('-c', self._easy_install_cmd, '-mUNxd', _safe_arg(tmp))
+            if not self._has_broken_dash_S:
+                args = ('-S',) + args
             if self._always_unzip:
                 args += ('-Z', )
             level = logger.getEffectiveLevel()
@@ -342,7 +582,7 @@
 
             if exit_code:
                 logger.error(
-                    "An error occured when trying to install %s. "
+                    "An error occurred when trying to install %s. "
                     "Look above this message for any errors that "
                     "were output by easy_install.",
                     dist)
@@ -400,14 +640,22 @@
             # Nothing is available.
             return None
 
-        # Filter the available dists for the requirement and source flag
-        dists = [dist for dist in index[requirement.project_name]
-                 if ((dist in requirement)
-                     and
-                     ((not source) or
-                      (dist.precedence == pkg_resources.SOURCE_DIST)
-                      )
-                     )
+        # Filter the available dists for the requirement and source flag.  If
+        # we are not supposed to include site-packages for the given egg, we
+        # also filter those out. Even if include_site_packages is False and so
+        # we have excluded site packages from the _env's paths (see
+        # Installer.__init__), we need to do the filtering here because an
+        # .egg-link, such as one for setuptools or zc.buildout installed by
+        # zc.buildout.buildout.Buildout.bootstrap, can indirectly include a
+        # path in our _site_packages.
+        dists = [dist for dist in index[requirement.project_name] if (
+                    dist in requirement and (
+                        dist.location not in self._site_packages or
+                        self.allow_site_package_egg(dist.project_name))
+                    and (
+                        (not source) or
+                        (dist.precedence == pkg_resources.SOURCE_DIST))
+                    )
                  ]
 
         # If we prefer final dists, filter for final and use the
@@ -495,7 +743,7 @@
 
                 if dist is None:
                     raise zc.buildout.UserError(
-                        "Couln't download distribution %s." % avail)
+                        "Couldn't download distribution %s." % avail)
 
                 if dist.precedence == pkg_resources.EGG_DIST:
                     # It's already an egg, just fetch it into the dest
@@ -567,7 +815,7 @@
                         self._links.append(link)
                         self._index = _get_index(self._executable,
                                                  self._index_url, self._links,
-                                                 self._allow_hosts)
+                                                 self._allow_hosts, self._path)
 
         for dist in dists:
             # Check whether we picked a version and, if we did, report it:
@@ -590,7 +838,7 @@
     def _maybe_add_setuptools(self, ws, dist):
         if dist.has_metadata('namespace_packages.txt'):
             for r in dist.requires():
-                if r.project_name == 'setuptools':
+                if r.project_name in ('setuptools', 'distribute'):
                     break
             else:
                 # We have a namespace package but no requirement for setuptools
@@ -609,6 +857,8 @@
 
 
     def _constrain(self, requirement):
+        if is_distribute and requirement.key == 'setuptools':
+            requirement = pkg_resources.Requirement.parse('distribute')
         version = self._versions.get(requirement.project_name)
         if version:
             if version not in requirement:
@@ -628,9 +878,9 @@
         logger.debug('Installing %s.', repr(specs)[1:-1])
 
         path = self._path
-        dest = self._dest
-        if dest is not None and dest not in path:
-            path.insert(0, dest)
+        destination = self._dest
+        if destination is not None and destination not in path:
+            path.insert(0, destination)
 
         requirements = [self._constrain(pkg_resources.Requirement.parse(spec))
                         for spec in specs]
@@ -648,35 +898,58 @@
                 self._maybe_add_setuptools(ws, dist)
 
         # OK, we have the requested distributions and they're in the working
-        # set, but they may have unmet requirements.  We'll simply keep
-        # trying to resolve requirements, adding missing requirements as they
-        # are reported.
-        #
-        # Note that we don't pass in the environment, because we want
+        # set, but they may have unmet requirements.  We'll resolve these
+        # requirements. This is code modified from
+        # pkg_resources.WorkingSet.resolve.  We can't reuse that code directly
+        # because we have to constrain our requirements (see
+        # versions_section_ignored_for_dependency_in_favor_of_site_packages in
+        # zc.buildout.tests).
+        requirements.reverse() # Set up the stack.
+        processed = {}  # This is a set of processed requirements.
+        best = {}  # This is a mapping of key -> dist.
+        # Note that we don't use the existing environment, because we want
         # to look for new eggs unless what we have is the best that
         # matches the requirement.
-        while 1:
-            try:
-                ws.resolve(requirements)
-            except pkg_resources.DistributionNotFound, err:
-                [requirement] = err
-                requirement = self._constrain(requirement)
-                if dest:
-                    logger.debug('Getting required %r', str(requirement))
-                else:
-                    logger.debug('Adding required %r', str(requirement))
-                _log_requirement(ws, requirement)
-
-                for dist in self._get_dist(requirement, ws, self._always_unzip
-                                           ):
-
-                    ws.add(dist)
-                    self._maybe_add_setuptools(ws, dist)
-            except pkg_resources.VersionConflict, err:
-                raise VersionConflict(err, ws)
-            else:
-                break
-
+        env = pkg_resources.Environment(ws.entries)
+        while requirements:
+            # Process dependencies breadth-first.
+            req = self._constrain(requirements.pop(0))
+            if req in processed:
+                # Ignore cyclic or redundant dependencies.
+                continue
+            dist = best.get(req.key)
+            if dist is None:
+                # Find the best distribution and add it to the map.
+                dist = ws.by_key.get(req.key)
+                if dist is None:
+                    try:
+                        dist = best[req.key] = env.best_match(req, ws)
+                    except pkg_resources.VersionConflict, err:
+                        raise VersionConflict(err, ws)
+                    if dist is None or (
+                        dist.location in self._site_packages and not
+                        self.allow_site_package_egg(dist.project_name)):
+                        # If we didn't find a distribution in the
+                        # environment, or what we found is from site
+                        # packages and not allowed to be there, try
+                        # again.
+                        if destination:
+                            logger.debug('Getting required %r', str(req))
+                        else:
+                            logger.debug('Adding required %r', str(req))
+                        _log_requirement(ws, req)
+                        for dist in self._get_dist(req,
+                                                   ws, self._always_unzip):
+                            ws.add(dist)
+                            self._maybe_add_setuptools(ws, dist)
+            if dist not in req:
+                # Oops, the "best" so far conflicts with a dependency.
+                raise VersionConflict(
+                    pkg_resources.VersionConflict(dist, req), ws)
+            requirements.extend(dist.requires(req.extras)[::-1])
+            processed[req] = True
+            if dist.location in self._site_packages:
+                logger.debug('Egg from site-packages: %s', dist)
         return ws
 
     def build(self, spec, build_ext):
@@ -771,6 +1044,18 @@
         Installer._prefer_final = bool(setting)
     return old
 
+def include_site_packages(setting=None):
+    old = Installer._include_site_packages
+    if setting is not None:
+        Installer._include_site_packages = bool(setting)
+    return old
+
+def allowed_eggs_from_site_packages(setting=None):
+    old = Installer._allowed_eggs_from_site_packages
+    if setting is not None:
+        Installer._allowed_eggs_from_site_packages = tuple(setting)
+    return old
+
 def use_dependency_links(setting=None):
     old = Installer._use_dependency_links
     if setting is not None:
@@ -793,19 +1078,28 @@
             links=(), index=None,
             executable=sys.executable, always_unzip=None,
             path=None, working_set=None, newest=True, versions=None,
-            use_dependency_links=None, allow_hosts=('*',)):
-    installer = Installer(dest, links, index, executable, always_unzip, path,
-                          newest, versions, use_dependency_links,
-                          allow_hosts=allow_hosts)
+            use_dependency_links=None, allow_hosts=('*',),
+            include_site_packages=None, allowed_eggs_from_site_packages=None,
+            prefer_final=None):
+    installer = Installer(
+        dest, links, index, executable, always_unzip, path, newest,
+        versions, use_dependency_links, allow_hosts=allow_hosts,
+        include_site_packages=include_site_packages,
+        allowed_eggs_from_site_packages=allowed_eggs_from_site_packages,
+        prefer_final=prefer_final)
     return installer.install(specs, working_set)
 
 
 def build(spec, dest, build_ext,
           links=(), index=None,
           executable=sys.executable,
-          path=None, newest=True, versions=None, allow_hosts=('*',)):
-    installer = Installer(dest, links, index, executable, True, path, newest,
-                          versions, allow_hosts=allow_hosts)
+          path=None, newest=True, versions=None, allow_hosts=('*',),
+          include_site_packages=None, allowed_eggs_from_site_packages=None):
+    installer = Installer(
+        dest, links, index, executable, True, path, newest, versions,
+        allow_hosts=allow_hosts,
+        include_site_packages=include_site_packages,
+        allowed_eggs_from_site_packages=allowed_eggs_from_site_packages)
     return installer.build(spec, build_ext)
 
 
@@ -887,45 +1181,152 @@
                 args[1] == '-v'
         if log_level < logging.DEBUG:
             logger.debug("in: %r\n%s", directory, ' '.join(args))
-
-        if is_jython:
-            assert subprocess.Popen([_safe_arg(executable)] + args).wait() == 0
-        else:
-            assert os.spawnl(os.P_WAIT, executable, _safe_arg(executable),
-                             *args) == 0
-
+        
+        try:
+            subprocess.check_call([_safe_arg(executable)] + args)
+        except subprocess.CalledProcessError:
+            raise zc.buildout.UserError("Installing develop egg failed")
+            
         return _copyeggs(tmp3, dest, '.egg-link', undo)
 
     finally:
         undo.reverse()
         [f() for f in undo]
 
+def working_set(specs, executable, path, include_site_packages=None,
+                allowed_eggs_from_site_packages=None, prefer_final=None):
+    return install(
+        specs, None, executable=executable, path=path,
+        include_site_packages=include_site_packages,
+        allowed_eggs_from_site_packages=allowed_eggs_from_site_packages,
+        prefer_final=prefer_final)
 
-def working_set(specs, executable, path):
-    return install(specs, None, executable=executable, path=path)
+############################################################################
+# Script generation functions
 
-def scripts(reqs, working_set, executable, dest,
-            scripts=None,
-            extra_paths=(),
-            arguments='',
-            interpreter=None,
-            initialization='',
-            relative_paths=False,
-            ):
+def scripts(
+    reqs, working_set, executable, dest,
+    scripts=None,
+    extra_paths=(),
+    arguments='',
+    interpreter=None,
+    initialization='',
+    relative_paths=False,
+    ):
+    """Generate scripts and/or an interpreter.
 
+    See sitepackage_safe_scripts for a version that can be used with a Python
+    that has code installed in site-packages. It has more options and a
+    different approach.
+    """
+    path = _get_path(working_set, extra_paths)
+    if initialization:
+        initialization = '\n'+initialization+'\n'
+    generated = _generate_scripts(
+        reqs, working_set, dest, path, scripts, relative_paths,
+        initialization, executable, arguments)
+    if interpreter:
+        sname = os.path.join(dest, interpreter)
+        spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
+        generated.extend(
+            _pyscript(spath, sname, executable, rpsetup))
+    return generated
+
+# We need to give an alternate name to the ``scripts`` function so that it
+# can be referenced within sitepackage_safe_scripts, which uses ``scripts``
+# as an argument name.
+_original_scripts_function = scripts
+
+def sitepackage_safe_scripts(
+    dest, working_set, executable, site_py_dest,
+    reqs=(),
+    scripts=None,
+    interpreter=None,
+    extra_paths=(),
+    initialization='',
+    include_site_packages=False,
+    exec_sitecustomize=False,
+    relative_paths=False,
+    script_arguments='',
+    script_initialization='',
+    ):
+    """Generate scripts and/or an interpreter from a system Python.
+
+    This accomplishes the same job as the ``scripts`` function, above,
+    but it does so in an alternative way that allows safely including
+    Python site packages, if desired, and  choosing to execute the Python's
+    sitecustomize.
+    """
+    if _has_broken_dash_S(executable):
+        if not include_site_packages:
+            warnings.warn(BROKEN_DASH_S_WARNING)
+        return _original_scripts_function(
+            reqs, working_set, executable, dest, scripts, extra_paths,
+            script_arguments, interpreter, initialization, relative_paths)
+    generated = []
+    generated.append(_generate_sitecustomize(
+        site_py_dest, executable, initialization, exec_sitecustomize))
+    generated.append(_generate_site(
+        site_py_dest, working_set, executable, extra_paths,
+        include_site_packages, relative_paths))
+    script_initialization = _script_initialization_template % dict(
+        site_py_dest=site_py_dest,
+        script_initialization=script_initialization)
+    if not script_initialization.endswith('\n'):
+        script_initialization += '\n'
+    generated.extend(_generate_scripts(
+        reqs, working_set, dest, [site_py_dest], scripts, relative_paths,
+        script_initialization, executable, script_arguments, block_site=True))
+    if interpreter:
+        generated.extend(_generate_interpreter(
+            interpreter, dest, executable, site_py_dest, relative_paths))
+    return generated
+
+_script_initialization_template = '''
+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
+%(script_initialization)s'''
+
+# Utilities for the script generation functions.
+
+# These are shared by both ``scripts`` and ``sitepackage_safe_scripts``
+
+def _get_path(working_set, extra_paths=()):
+    """Given working set and extra paths, return a normalized path list."""
     path = [dist.location for dist in working_set]
     path.extend(extra_paths)
-    path = map(realpath, path)
+    # order preserving unique
+    unique_path = []
+    for p in path:
+        if p not in unique_path:
+            unique_path.append(p)
+    return map(realpath, unique_path)
 
-    generated = []
+def _generate_scripts(reqs, working_set, dest, path, scripts, relative_paths,
+                      initialization, executable, arguments,
+                      block_site=False):
+    """Generate scripts for the given requirements.
 
+    - reqs is an iterable of string requirements or entry points.
+    - The requirements must be findable in the given working_set.
+    - The dest is the directory in which the scripts should be created.
+    - The path is a list of paths that should be added to sys.path.
+    - The scripts is an optional dictionary.  If included, the keys should be
+      the names of the scripts that should be created, as identified in their
+      entry points; and the values should be the name the script should
+      actually be created with.
+    - relative_paths, if given, should be the path that is the root of the
+      buildout (the common path that should be the root of what is relative).
+    """
     if isinstance(reqs, str):
         raise TypeError('Expected iterable of requirements or entry points,'
                         ' got string.')
-
-    if initialization:
-        initialization = '\n'+initialization+'\n'
-
+    generated = []
     entry_points = []
     for req in reqs:
         if isinstance(req, str):
@@ -939,7 +1340,6 @@
                     )
         else:
             entry_points.append(req)
-
     for name, module_name, attrs in entry_points:
         if scripts is not None:
             sname = scripts.get(name)
@@ -947,40 +1347,51 @@
                 continue
         else:
             sname = name
-
         sname = os.path.join(dest, sname)
         spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
-
         generated.extend(
-            _script(module_name, attrs, spath, sname, executable, arguments,
-                    initialization, rpsetup)
-            )
+            _script(sname, executable, rpsetup, spath, initialization,
+                    module_name, attrs, arguments, block_site=block_site))
+    return generated
 
-    if interpreter:
-        sname = os.path.join(dest, interpreter)
-        spath, rpsetup = _relative_path_and_setup(sname, path, relative_paths)
-        generated.extend(_pyscript(spath, sname, executable, rpsetup))
+def _relative_path_and_setup(sname, path,
+                             relative_paths=False, indent_level=1,
+                             omit_os_import=False):
+    """Return a string of code of paths and of setup if appropriate.
 
-    return generated
-
-def _relative_path_and_setup(sname, path, relative_paths):
+    - sname is the full path to the script name to be created.
+    - path is the list of paths to be added to sys.path.
+    - relative_paths, if given, should be the path that is the root of the
+      buildout (the common path that should be the root of what is relative).
+    - indent_level is the number of four-space indents that the path should
+      insert before each element of the path.
+    """
     if relative_paths:
         relative_paths = os.path.normcase(relative_paths)
         sname = os.path.normcase(os.path.abspath(sname))
-        spath = ',\n  '.join(
+        spath = _format_paths(
             [_relativitize(os.path.normcase(path_item), sname, relative_paths)
-             for path_item in path]
-            )
+             for path_item in path], indent_level=indent_level)
         rpsetup = relative_paths_setup
+        if not omit_os_import:
+            rpsetup = '\n\nimport os\n' + rpsetup
         for i in range(_relative_depth(relative_paths, sname)):
-            rpsetup += "base = os.path.dirname(base)\n"
+            rpsetup += "\nbase = os.path.dirname(base)"
     else:
-        spath = repr(path)[1:-1].replace(', ', ',\n  ')
+        spath = _format_paths((repr(p) for p in path),
+                              indent_level=indent_level)
         rpsetup = ''
     return spath, rpsetup
 
+def _relative_depth(common, path):
+    """Return number of dirs separating ``path`` from ancestor, ``common``.
 
-def _relative_depth(common, path):
+    For instance, if path is /foo/bar/baz/bing, and common is /foo, this will
+    return 2--in UNIX, the number of ".." to get from bing's directory
+    to foo.
+
+    This is a helper for _relative_path_and_setup.
+    """
     n = 0
     while 1:
         dirname = os.path.dirname(path)
@@ -993,6 +1404,11 @@
     return n
 
 def _relative_path(common, path):
+    """Return the relative path from ``common`` to ``path``.
+
+    This is a helper for _relativitize, which is a helper to
+    _relative_path_and_setup.
+    """
     r = []
     while 1:
         dirname, basename = os.path.split(path)
@@ -1006,6 +1422,11 @@
     return os.path.join(*r)
 
 def _relativitize(path, script, relative_paths):
+    """Return a code string for the given path.
+
+    Path is relative to the base path ``relative_paths``if the common prefix
+    between ``path`` and ``script`` starts with ``relative_paths``.
+    """
     if path == script:
         raise AssertionError("path == script")
     common = os.path.dirname(os.path.commonprefix([path, script]))
@@ -1016,114 +1437,108 @@
     else:
         return repr(path)
 
-
 relative_paths_setup = """
-import os
-
 join = os.path.join
-base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
-"""
+base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))"""
 
-def _script(module_name, attrs, path, dest, executable, arguments,
-            initialization, rsetup):
+def _write_script(full_name, contents, logged_type):
+    """Write contents of script in full_name, logging the action.
+
+    The only tricky bit in this function is that it supports Windows by
+    creating exe files using a pkg_resources helper.
+    """
     generated = []
-    script = dest
+    script_name = full_name
     if is_win32:
-        dest += '-script.py'
-
-    contents = script_template % dict(
-        python = _safe_arg(executable),
-        path = path,
-        module_name = module_name,
-        attrs = attrs,
-        arguments = arguments,
-        initialization = initialization,
-        relative_paths_setup = rsetup,
-        )
-    changed = not (os.path.exists(dest) and open(dest).read() == contents)
-
-    if is_win32:
-        # generate exe file and give the script a magic name:
-        exe = script+'.exe'
+        script_name += '-script.py'
+        # Generate exe file and give the script a magic name.
+        exe = full_name + '.exe'
         new_data = pkg_resources.resource_string('setuptools', 'cli.exe')
         if not os.path.exists(exe) or (open(exe, 'rb').read() != new_data):
             # Only write it if it's different.
             open(exe, 'wb').write(new_data)
         generated.append(exe)
-
+    changed = not (os.path.exists(script_name) and
+                   open(script_name).read() == contents)
     if changed:
-        open(dest, 'w').write(contents)
-        logger.info("Generated script %r.", script)
-
+        open(script_name, 'w').write(contents)
         try:
-            os.chmod(dest, 0755)
+            os.chmod(script_name, 0755)
         except (AttributeError, os.error):
             pass
-
-    generated.append(dest)
+        logger.info("Generated %s %r.", logged_type, full_name)
+    generated.append(script_name)
     return generated
 
+def _format_paths(paths, indent_level=1):
+    """Format paths for inclusion in a script."""
+    separator = ',\n' + indent_level * '    '
+    return separator.join(paths)
+
+def _script(dest, executable, relative_paths_setup, path, initialization,
+            module_name, attrs, arguments, block_site=False):
+    if block_site:
+        dash_S = ' -S'
+    else:
+        dash_S = ''
+    contents = script_template % dict(
+        python=_safe_arg(executable),
+        dash_S=dash_S,
+        path=path,
+        module_name=module_name,
+        attrs=attrs,
+        arguments=arguments,
+        initialization=initialization,
+        relative_paths_setup=relative_paths_setup,
+        )
+    return _write_script(dest, contents, 'script')
+
 if is_jython and jython_os_name == 'linux':
-    script_header = '#!/usr/bin/env %(python)s'
+    script_header = '#!/usr/bin/env %(python)s%(dash_S)s'
 else:
-    script_header = '#!%(python)s'
+    script_header = '#!%(python)s%(dash_S)s'
 
+sys_path_template = '''\
+import sys
+sys.path[0:0] = [
+    %s,
+    ]
+'''
 
 script_template = script_header + '''\
+%(relative_paths_setup)s
 
-%(relative_paths_setup)s
 import sys
 sys.path[0:0] = [
-  %(path)s,
-  ]
+    %(path)s,
+    ]
+
 %(initialization)s
 import %(module_name)s
 
 if __name__ == '__main__':
-    %(module_name)s.%(attrs)s(%(arguments)s)
+    sys.exit(%(module_name)s.%(attrs)s(%(arguments)s))
 '''
 
+# These are used only by the older ``scripts`` function.
 
 def _pyscript(path, dest, executable, rsetup):
-    generated = []
-    script = dest
-    if is_win32:
-        dest += '-script.py'
-
     contents = py_script_template % dict(
-        python = _safe_arg(executable),
-        path = path,
-        relative_paths_setup = rsetup,
+        python=_safe_arg(executable),
+        dash_S='',
+        path=path,
+        relative_paths_setup=rsetup,
         )
-    changed = not (os.path.exists(dest) and open(dest).read() == contents)
+    return _write_script(dest, contents, 'interpreter')
 
-    if is_win32:
-        # generate exe file and give the script a magic name:
-        exe = script + '.exe'
-        open(exe, 'wb').write(
-            pkg_resources.resource_string('setuptools', 'cli.exe')
-            )
-        generated.append(exe)
-
-    if changed:
-        open(dest, 'w').write(contents)
-        try:
-            os.chmod(dest,0755)
-        except (AttributeError, os.error):
-            pass
-        logger.info("Generated interpreter %r.", script)
-
-    generated.append(dest)
-    return generated
-
 py_script_template = script_header + '''\
+%(relative_paths_setup)s
 
-%(relative_paths_setup)s
 import sys
 
 sys.path[0:0] = [
-  %(path)s,
-  ]
+    %(path)s,
+    ]
 
 _interactive = True
 if len(sys.argv) > 1:
@@ -1151,6 +1566,246 @@
     __import__("code").interact(banner="", local=globals())
 '''
 
+# These are used only by the newer ``sitepackage_safe_scripts`` function.
+
+def _get_module_file(executable, name, silent=False):
+    """Return a module's file path.
+
+    - executable is a path to the desired Python executable.
+    - name is the name of the (pure, not C) Python module.
+    """
+    cmd = [executable, "-Sc",
+           "import imp; "
+           "fp, path, desc = imp.find_module(%r); "
+           "fp.close(); "
+           "print path" % (name,)]
+    env = os.environ.copy()
+    # We need to make sure that PYTHONPATH, which will often be set to
+    # include a custom buildout-generated site.py, is not set, or else
+    # we will not get an accurate value for the "real" site.py and
+    # sitecustomize.py.
+    env.pop('PYTHONPATH', None)
+    _proc = subprocess.Popen(
+        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+    stdout, stderr = _proc.communicate();
+    if _proc.returncode:
+        if not silent:
+            logger.info(
+                'Could not find file for module %s:\n%s', name, stderr)
+        return None
+    # else: ...
+    res = stdout.strip()
+    if res.endswith('.pyc') or res.endswith('.pyo'):
+        raise RuntimeError('Cannot find uncompiled version of %s' % (name,))
+    if not os.path.exists(res):
+        raise RuntimeError(
+            'File does not exist for module %s:\n%s' % (name, res))
+    return res
+
+def _generate_sitecustomize(dest, executable, initialization='',
+                            exec_sitecustomize=False):
+    """Write a sitecustomize file with optional custom initialization.
+
+    The created script will execute the underlying Python's
+    sitecustomize if exec_sitecustomize is True.
+    """
+    sitecustomize_path = os.path.join(dest, 'sitecustomize.py')
+    sitecustomize = open(sitecustomize_path, 'w')
+    if initialization:
+        sitecustomize.write(initialization + '\n')
+    if exec_sitecustomize:
+        real_sitecustomize_path = _get_module_file(
+            executable, 'sitecustomize', silent=True)
+        if real_sitecustomize_path:
+            real_sitecustomize = open(real_sitecustomize_path, 'r')
+            sitecustomize.write(
+                '\n# The following is from\n# %s\n' %
+                (real_sitecustomize_path,))
+            sitecustomize.write(real_sitecustomize.read())
+            real_sitecustomize.close()
+    sitecustomize.close()
+    return sitecustomize_path
+
+def _generate_site(dest, working_set, executable, extra_paths=(),
+                   include_site_packages=False, relative_paths=False):
+    """Write a site.py file with eggs from working_set.
+
+    extra_paths will be added to the path.  If include_site_packages is True,
+    paths from the underlying Python will be added.
+    """
+    path = _get_path(working_set, extra_paths)
+    site_path = os.path.join(dest, 'site.py')
+    original_path_setup = preamble = ''
+    if include_site_packages:
+        stdlib, site_paths = _get_system_paths(executable)
+        # We want to make sure that paths from site-packages, such as those
+        # allowed by allowed_eggs_from_site_packages, always come last, or
+        # else site-packages paths may include packages that mask the eggs we
+        # really want.
+        path = [p for p in path if p not in site_paths]
+        # Now we set up the code we need.
+        original_path_setup = original_path_snippet % (
+            _format_paths((repr(p) for p in site_paths), 2),)
+        distribution = working_set.find(
+            pkg_resources.Requirement.parse('setuptools'))
+        if distribution is not None:
+            # We need to worry about namespace packages.
+            if relative_paths:
+                location = _relativitize(
+                    distribution.location,
+                    os.path.normcase(os.path.abspath(site_path)),
+                    relative_paths)
+            else:
+                location = repr(distribution.location)
+            preamble = namespace_include_site_packages_setup % (location,)
+            original_path_setup = (
+                addsitedir_namespace_originalpackages_snippet +
+                original_path_setup)
+        else:
+            preamble = '\n    setuptools_path = None'
+    egg_path_string, relative_preamble = _relative_path_and_setup(
+        site_path, path, relative_paths, indent_level=2, omit_os_import=True)
+    if relative_preamble:
+        relative_preamble = '\n'.join(
+            [(line and '    %s' % (line,) or line)
+             for line in relative_preamble.split('\n')])
+        preamble = relative_preamble + preamble
+    addsitepackages_marker = 'def addsitepackages('
+    enableusersite_marker = 'ENABLE_USER_SITE = '
+    successful_rewrite = False
+    real_site_path = _get_module_file(executable, 'site')
+    real_site = open(real_site_path, 'r')
+    site = open(site_path, 'w')
+    try:
+        for line in real_site.readlines():
+            if line.startswith(enableusersite_marker):
+                site.write(enableusersite_marker)
+                site.write('False # buildout does not support user sites.\n')
+            elif line.startswith(addsitepackages_marker):
+                site.write(addsitepackages_script % (
+                    preamble, egg_path_string, original_path_setup))
+                site.write(line[len(addsitepackages_marker):])
+                successful_rewrite = True
+            else:
+                site.write(line)
+    finally:
+        site.close()
+        real_site.close()
+    if not successful_rewrite:
+        raise RuntimeError(
+            'Buildout did not successfully rewrite %s to %s' %
+            (real_site_path, site_path))
+    return site_path
+
+namespace_include_site_packages_setup = '''
+    setuptools_path = %s
+    sys.path.append(setuptools_path)
+    known_paths.add(os.path.normcase(setuptools_path))
+    import pkg_resources'''
+
+addsitedir_namespace_originalpackages_snippet = '''
+            pkg_resources.working_set.add_entry(sitedir)'''
+
+original_path_snippet = '''
+    sys.__egginsert = len(buildout_paths) # Support distribute.
+    original_paths = [
+        %s
+        ]
+    for path in original_paths:
+        if path == setuptools_path or path not in known_paths:
+            addsitedir(path, known_paths)'''
+
+addsitepackages_script = '''\
+def addsitepackages(known_paths):
+    """Add site packages, as determined by zc.buildout.
+
+    See original_addsitepackages, below, for the original version."""%s
+    buildout_paths = [
+        %s
+        ]
+    for path in buildout_paths:
+        sitedir, sitedircase = makepath(path)
+        if not sitedircase in known_paths and os.path.exists(sitedir):
+            sys.path.append(sitedir)
+            known_paths.add(sitedircase)%s
+    return known_paths
+
+def original_addsitepackages('''
+
+def _generate_interpreter(name, dest, executable, site_py_dest,
+                          relative_paths=False):
+    """Write an interpreter script, using the site.py approach."""
+    full_name = os.path.join(dest, name)
+    site_py_dest_string, rpsetup = _relative_path_and_setup(
+        full_name, [site_py_dest], relative_paths, omit_os_import=True)
+    if rpsetup:
+        rpsetup += "\n"
+    if sys.platform == 'win32':
+        windows_import = '\nimport subprocess'
+        # os.exec* is a mess on Windows, particularly if the path
+        # to the executable has spaces and the Python is using MSVCRT.
+        # The standard fix is to surround the executable's path with quotes,
+        # but that has been unreliable in testing.
+        #
+        # Here's a demonstration of the problem.  Given a Python
+        # compiled with a MSVCRT-based compiler, such as the free Visual
+        # C++ 2008 Express Edition, and an executable path with spaces
+        # in it such as the below, we see the following.
+        #
+        # >>> import os
+        # >>> p0 = 'C:\\Documents and Settings\\Administrator\\My Documents\\Downloads\\Python-2.6.4\\PCbuild\\python.exe'
+        # >>> os.path.exists(p0)
+        # True
+        # >>> os.execv(p0, [])
+        # Traceback (most recent call last):
+        #  File "<stdin>", line 1, in <module>
+        # OSError: [Errno 22] Invalid argument
+        #
+        # That seems like a standard problem.  The standard solution is
+        # to quote the path (see, for instance
+        # http://bugs.python.org/issue436259).  However, this solution,
+        # and other variations, fail:
+        #
+        # >>> p1 = '"C:\\Documents and Settings\\Administrator\\My Documents\\Downloads\\Python-2.6.4\\PCbuild\\python.exe"'
+        # >>> os.execv(p1, [])
+        # Traceback (most recent call last):
+        #   File "<stdin>", line 1, in <module>
+        # OSError: [Errno 22] Invalid argument
+        #
+        # We simply use subprocess instead, since it handles everything
+        # nicely, and the transparency of exec* (that is, not running,
+        # perhaps unexpectedly, in a subprocess) is arguably not a
+        # necessity, at least for many use cases.
+        execute = 'subprocess.call(argv, env=environ)'
+    else:
+        windows_import = ''
+        execute = 'os.execve(sys.executable, argv, environ)'
+    contents = interpreter_template % dict(
+        python=_safe_arg(executable),
+        dash_S=' -S',
+        site_dest=site_py_dest_string,
+        relative_paths_setup=rpsetup,
+        windows_import=windows_import,
+        execute=execute,
+        )
+    return _write_script(full_name, contents, 'interpreter')
+
+interpreter_template = script_header + '''
+import os
+import sys%(windows_import)s
+%(relative_paths_setup)s
+argv = [sys.executable] + sys.argv[1:]
+environ = os.environ.copy()
+path = %(site_dest)s
+if environ.get('PYTHONPATH'):
+    path = os.pathsep.join([path, environ['PYTHONPATH']])
+environ['PYTHONPATH'] = path
+%(execute)s
+'''
+
+# End of script generation code.
+############################################################################
+
 runsetup_template = """
 import sys
 sys.path.insert(0, %(setupdir)r)

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/easy_install.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -89,6 +89,14 @@
    for using dependency_links in preference to other
    locations. Defaults to true.
 
+include_site_packages
+    A flag indicating whether Python's non-standard-library packages should
+    be available for finding dependencies.  Defaults to true.
+
+    Paths outside of Python's standard library--or more precisely, those that
+    are not included when Python is started with the -S argument--are loosely
+    referred to as "site-packages" here.
+
 relative_paths
    Adjust egg paths so they are relative to the script path.  This
    allows scripts to work when scripts and eggs are moved, as long as
@@ -214,7 +222,9 @@
     d  other-1.0-py2.4.egg
 
 We can request that eggs be unzipped even if they are zip safe.  This
-can be useful when debugging.
+can be useful when debugging.  (Note that Distribute will unzip eggs by
+default, so if you are using Distribute, most or all eggs will already be
+unzipped without this flag.)
 
     >>> rmdir(dest)
     >>> dest = tmpdir('sample-install')
@@ -399,6 +409,68 @@
     >>> [d.version for d in ws]
     ['0.3', '1.1']
 
+Dependencies in Site Packages
+-----------------------------
+
+Paths outside of Python's standard library--or more precisely, those that are
+not included when Python is started with the -S argument--are loosely referred
+to as "site-packages" here.  These site-packages are searched by default for
+distributions.  This can be disabled, so that, for instance, a system Python
+can be used with buildout, cleaned of any packages installed by a user or
+system package manager.
+
+The default behavior can be controlled and introspected using
+zc.buildout.easy_install.include_site_packages.
+
+    >>> zc.buildout.easy_install.include_site_packages()
+    True
+
+Here's an example of using a Python executable that includes our dependencies.
+
+Our "py_path" will have the "demoneeded," and "demo" packages available.
+ We'll simply be asking for "demoneeded" here, but without any external
+ index or links.
+
+    >>> from zc.buildout.tests import create_sample_sys_install
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None)
+    >>> [dist.project_name for dist in workingset]
+    ['demoneeded']
+
+That worked fine.  Let's try again with site packages not allowed.  We'll
+change the policy by changing the default.  Notice that the function for
+changing the default value returns the previous value.
+
+    >>> zc.buildout.easy_install.include_site_packages(False)
+    True
+
+    >>> zc.buildout.easy_install.include_site_packages()
+    False
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None)
+    Traceback (most recent call last):
+        ...
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+    >>> zc.buildout.easy_install.clear_index_cache()
+
+Now we'll reset the default.
+
+    >>> zc.buildout.easy_install.include_site_packages(True)
+    False
+
+    >>> zc.buildout.easy_install.include_site_packages()
+    True
+
 Dependency links
 ----------------
 
@@ -521,25 +593,38 @@
 Script generation
 -----------------
 
-The easy_install module provides support for creating scripts from
-eggs.  It provides a function similar to setuptools except that it
-provides facilities for baking a script's path into the script.  This
-has two advantages:
+The easy_install module provides support for creating scripts from eggs.
+It provides two competing functions.  One, ``scripts``, is a
+well-established approach to generating reliable scripts with a "clean"
+Python--e.g., one that does not have any packages in its site-packages.
+The other, ``sitepackage_safe_scripts``, is newer, a bit trickier, and is
+designed to work with a Python that has code in its site-packages, such
+as a system Python.
 
+Both are similar to setuptools except that they provides facilities for
+baking a script's path into the script.  This has two advantages:
+
 - The eggs to be used by a script are not chosen at run time, making
   startup faster and, more importantly, deterministic.
 
-- The script doesn't have to import pkg_resources because the logic
-  that pkg_resources would execute at run time is executed at
-  script-creation time.
+- The script doesn't have to import pkg_resources because the logic that
+  pkg_resources would execute at run time is executed at script-creation
+  time.  (There is an exception in ``sitepackage_safe_scripts`` if you
+  want to have your Python's site packages available, as discussed
+  below, but even in that case pkg_resources is only partially
+  activated, which can be a significant time savings.)
 
-The scripts method can be used to generate scripts. Let's create a
-destination directory for it to place them in:
 
-    >>> import tempfile
+The ``scripts`` function
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``scripts`` function is the first way to generate scripts that we'll
+examine. It is the earlier approach that the package offered.  Let's
+create a destination directory for it to place them in:
+
     >>> bin = tmpdir('bin')
 
-Now, we'll use the scripts method to generate scripts in this directory
+Now, we'll use the scripts function to generate scripts in this directory
 from the demo egg:
 
     >>> import sys
@@ -592,7 +677,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 Some things to note:
 
@@ -629,7 +714,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 Passing entry-point information directly is handy when using eggs (or
 distributions) that don't declare their entry points, such as
@@ -736,8 +821,8 @@
     >>> print system(os.path.join(bin, 'run')),
     3 1
 
-Including extra paths in scripts
---------------------------------
+The ``scripts`` function: Including extra paths in scripts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 We can pass a keyword argument, extra paths, to cause additional paths
 to be included in the a generated script:
@@ -760,10 +845,10 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
-Providing script arguments
---------------------------
+The ``scripts`` function: Providing script arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 An "argument" keyword argument can be used to pass arguments to an
 entry point.  The value passed is a source string to be placed between the
@@ -784,10 +869,10 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main(1, 2)
+        sys.exit(eggrecipedemo.main(1, 2))
 
-Passing initialization code
----------------------------
+The ``scripts`` function: Passing initialization code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 You can also pass script initialization code:
 
@@ -810,10 +895,10 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main(1, 2)
+        sys.exit(eggrecipedemo.main(1, 2))
 
-Relative paths
---------------
+The ``scripts`` function: Relative paths
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Sometimes, you want to be able to move a buildout directory around and
 have scripts still work without having to rebuild them.  We can
@@ -836,7 +921,7 @@
     ...    interpreter='py',
     ...    relative_paths=bo)
 
-    >>> cat(bo, 'bin', 'run')
+    >>> cat(bo, 'bin', 'run') # doctest: +NORMALIZE_WHITESPACE
     #!/usr/local/bin/python2.4
     <BLANKLINE>
     import os
@@ -856,7 +941,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 Note that the extra path we specified that was outside the directory
 passed as relative_paths wasn't converted to a relative path.
@@ -868,7 +953,7 @@
 
 We specified an interpreter and its paths are adjusted too:
 
-    >>> cat(bo, 'bin', 'py')
+    >>> cat(bo, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
     #!/usr/local/bin/python2.4
     <BLANKLINE>
     import os
@@ -911,7 +996,577 @@
         del _interactive
         __import__("code").interact(banner="", local=globals())
 
+The ``sitepackage_safe_scripts`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+The newer function for creating scripts is ``sitepackage_safe_scripts``.
+It has the same basic functionality as the ``scripts`` function: it can
+create scripts to run arbitrary entry points, and to run a Python
+interpreter.  The following are the differences from a user's
+perspective.
+
+- It can be used safely with a Python that has packages installed itself,
+  such as a system-installed Python.
+
+- In contrast to the interpreter generated by the ``scripts`` method, which
+  supports only a small subset of the usual Python executable's options,
+  the interpreter generated by ``sitepackage_safe_scripts`` supports all
+  of them. This makes it possible to use as full Python replacement for
+  scripts that need the distributions specified in your buildout.
+
+- Both the interpreter and the entry point scripts allow you to include the
+  site packages, and/or the sitecustomize, of the Python executable, if
+  desired.
+
+It works by creating site.py and sitecustomize.py files that set up the
+desired paths and initialization.  These must be placed within an otherwise
+empty directory.  Typically this is in a recipe's parts directory.
+
+Here's the simplest example, building an interpreter script.
+
+    >>> interpreter_dir = tmpdir('interpreter')
+    >>> interpreter_parts_dir = os.path.join(
+    ...     interpreter_dir, 'parts', 'interpreter')
+    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+    >>> mkdir(interpreter_bin_dir)
+    >>> mkdir(interpreter_dir, 'eggs')
+    >>> mkdir(interpreter_dir, 'parts')
+    >>> mkdir(interpreter_parts_dir)
+
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
+    ...     index=link_server+'index/')
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py')
+
+Depending on whether the machine being used is running Windows or not, this
+produces either three or four files.  In both cases, we have site.py and
+sitecustomize.py generated in the parts/interpreter directory.  For Windows,
+we have py.exe and py-script.py; for other operating systems, we have py.
+
+    >>> sitecustomize_path = os.path.join(
+    ...     interpreter_parts_dir, 'sitecustomize.py')
+    >>> site_path = os.path.join(interpreter_parts_dir, 'site.py')
+    >>> interpreter_path = os.path.join(interpreter_bin_dir, 'py')
+    >>> if sys.platform == 'win32':
+    ...     py_path = os.path.join(interpreter_bin_dir, 'py-script.py')
+    ...     expected = [sitecustomize_path,
+    ...                 site_path,
+    ...                 os.path.join(interpreter_bin_dir, 'py.exe'),
+    ...                 py_path]
+    ... else:
+    ...     py_path = interpreter_path
+    ...     expected = [sitecustomize_path, site_path, py_path]
+    ...
+    >>> assert generated == expected, repr((generated, expected))
+
+We didn't ask for any initialization, and we didn't ask to use the underlying
+sitecustomization, so sitecustomize.py is empty.
+
+    >>> cat(sitecustomize_path)
+
+The interpreter script is simple.  It puts the directory with the
+site.py and sitecustomize.py on the PYTHONPATH and (re)starts Python.
+
+    >>> cat(py_path)
+    #!/usr/bin/python -S
+    import os
+    import sys
+    <BLANKLINE>
+    argv = [sys.executable] + sys.argv[1:]
+    environ = os.environ.copy()
+    path = '/interpreter/parts/interpreter'
+    if environ.get('PYTHONPATH'):
+        path = os.pathsep.join([path, environ['PYTHONPATH']])
+    environ['PYTHONPATH'] = path
+    os.execve(sys.executable, argv, environ)
+
+The site.py file is a modified version of the underlying Python's site.py.
+The most important modification is that it has a different version of the
+addsitepackages function.  It sets up the Python path, similarly to the
+behavior of the function it replaces.  The following shows the part that
+buildout inserts, in the simplest case.
+
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        buildout_paths = [
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
+            ]
+        for path in buildout_paths:
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.append(sitedir)
+                known_paths.add(sitedircase)
+        return known_paths
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+Here are some examples of the interpreter in use.
+
+    >>> print call_py(interpreter_path, "print 16+26")
+    42
+    <BLANKLINE>
+    >>> res = call_py(interpreter_path, "import sys; print sys.path")
+    >>> print res # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+    <BLANKLINE>
+    >>> clean_paths = eval(res.strip()) # This is used later for comparison.
+
+If you provide initialization, it goes in sitecustomize.py.
+
+    >>> def reset_interpreter():
+    ...     # This is necessary because, in our tests, the timestamps of the
+    ...     # .pyc files are not outdated when we want them to be.
+    ...     rmdir(interpreter_bin_dir)
+    ...     mkdir(interpreter_bin_dir)
+    ...     rmdir(interpreter_parts_dir)
+    ...     mkdir(interpreter_parts_dir)
+    ...
+    >>> reset_interpreter()
+
+    >>> initialization_string = """\
+    ... import os
+    ... os.environ['FOO'] = 'bar baz bing shazam'"""
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', initialization=initialization_string)
+    >>> cat(sitecustomize_path)
+    import os
+    os.environ['FOO'] = 'bar baz bing shazam'
+    >>> print call_py(interpreter_path, "import os; print os.environ['FOO']")
+    bar baz bing shazam
+    <BLANKLINE>
+
+If you use relative paths, this affects the interpreter and site.py.  (This is
+again the UNIX version; the Windows version uses subprocess instead of
+os.execve.)
+
+    >>> reset_interpreter()
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', relative_paths=interpreter_dir)
+    >>> cat(py_path)
+    #!/usr/bin/python -S
+    import os
+    import sys
+    <BLANKLINE>
+    join = os.path.join
+    base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+    base = os.path.dirname(base)
+    <BLANKLINE>
+    argv = [sys.executable] + sys.argv[1:]
+    environ = os.environ.copy()
+    path = join(base, 'parts/interpreter')
+    if environ.get('PYTHONPATH'):
+        path = os.pathsep.join([path, environ['PYTHONPATH']])
+    environ['PYTHONPATH'] = path
+    os.execve(sys.executable, argv, environ)
+
+For site.py, we again show only the pertinent parts.  Notice that the egg
+paths join a base to a path, as with the use of this argument in the
+``scripts`` function.
+
+    >>> sys.stdout.write('#\n'); cat(site_path) # doctest: +ELLIPSIS
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        join = os.path.join
+        base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+        base = os.path.dirname(base)
+        base = os.path.dirname(base)
+        buildout_paths = [
+            join(base, 'eggs/demo-0.3-pyN.N.egg'),
+            join(base, 'eggs/demoneeded-1.1-pyN.N.egg')
+            ]...
+
+The paths resolve in practice as you would expect.
+
+    >>> print call_py(interpreter_path,
+    ...               "import sys, pprint; pprint.pprint(sys.path)")
+    ... # doctest: +ELLIPSIS
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+    <BLANKLINE>
+
+The ``extra_paths`` argument affects the path in site.py.  Notice that
+/interpreter/other is added after the eggs.
+
+    >>> reset_interpreter()
+    >>> mkdir(interpreter_dir, 'other')
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', extra_paths=[join(interpreter_dir, 'other')])
+    >>> sys.stdout.write('#\n'); cat(site_path) # doctest: +ELLIPSIS
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        buildout_paths = [
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+            '/interpreter/other'
+            ]...
+
+    >>> print call_py(interpreter_path,
+    ...               "import sys, pprint; pprint.pprint(sys.path)")
+    ... # doctest: +ELLIPSIS
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+     '/interpreter/other']
+    <BLANKLINE>
+
+The ``sitepackage_safe_scripts`` function: using site-packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``sitepackage_safe_scripts`` function supports including site
+packages.  This has some advantages and some serious dangers.
+
+A typical reason to include site-packages is that it is easier to
+install one or more dependencies in your Python than it is with
+buildout.  Some packages, such as lxml or Python PostgreSQL integration,
+have dependencies that can be much easier to build and/or install using
+other mechanisms, such as your operating system's package manager.  By
+installing some core packages into your Python's site-packages, this can
+significantly simplify some application installations.
+
+However, doing this has a significant danger.  One of the primary goals
+of buildout is to provide repeatability.  Some packages (one of the
+better known Python openid packages, for instance) change their behavior
+depending on what packages are available.  If Python curl bindings are
+available, these may be preferred by the library.  If a certain XML
+package is installed, it may be preferred by the library.  These hidden
+choices may cause small or large behavior differences.  The fact that
+they can be rarely encountered can actually make it worse: you forget
+that this might be a problem, and debugging the differences can be
+difficult.  If you allow site-packages to be included in your buildout,
+and the Python you use is not managed precisely by your application (for
+instance, it is a system Python), you open yourself up to these
+possibilities.  Don't be unaware of the dangers.
+
+That explained, let's see how it works.  If you don't use namespace packages,
+this is very straightforward.
+
+    >>> reset_interpreter()
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', include_site_packages=True)
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        setuptools_path = None
+        buildout_paths = [
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
+            ]
+        for path in buildout_paths:
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.append(sitedir)
+                known_paths.add(sitedircase)
+        sys.__egginsert = len(buildout_paths) # Support distribute.
+        original_paths = [
+            ...
+            ]
+        for path in original_paths:
+            if path == setuptools_path or path not in known_paths:
+                addsitedir(path, known_paths)
+        return known_paths
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+It simply adds the original paths using addsitedir after the code to add the
+buildout paths.
+
+Here's an example of the new script in use.  Other documents and tests in
+this package give the feature a more thorough workout, but this should
+give you an idea of the feature.
+
+    >>> res = call_py(interpreter_path, "import sys; print sys.path")
+    >>> print res # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '/interpreter/eggs/demo-0.3-py2.4.egg',
+     '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
+     ...]
+    <BLANKLINE>
+
+The clean_paths gathered earlier is a subset of this full list of paths.
+
+    >>> full_paths = eval(res.strip())
+    >>> len(clean_paths) < len(full_paths)
+    True
+    >>> set(os.path.normpath(p) for p in clean_paths).issubset(
+    ...     os.path.normpath(p) for p in full_paths)
+    True
+
+Unfortunately, because of how setuptools namespace packages are implemented
+differently for operating system packages (debs or rpms) as opposed to
+standard setuptools installation, there's a slightly trickier dance if you
+use them.  To show this we'll needs some extra eggs that use namespaces.
+We'll use the ``tellmy.fortune`` package, which we'll need to make an initial
+call to another text fixture to create.
+
+    >>> from zc.buildout.tests import create_sample_namespace_eggs
+    >>> namespace_eggs = tmpdir('namespace_eggs')
+    >>> create_sample_namespace_eggs(namespace_eggs)
+
+    >>> reset_interpreter()
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo', 'tellmy.fortune'], join(interpreter_dir, 'eggs'),
+    ...     links=[link_server, namespace_eggs], index=link_server+'index/')
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', include_site_packages=True)
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        setuptools_path = '...setuptools...'
+        sys.path.append(setuptools_path)
+        known_paths.add(os.path.normcase(setuptools_path))
+        import pkg_resources
+        buildout_paths = [
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
+            '...setuptools...',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
+            ]
+        for path in buildout_paths:
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.append(sitedir)
+                known_paths.add(sitedircase)
+                pkg_resources.working_set.add_entry(sitedir)
+        sys.__egginsert = len(buildout_paths) # Support distribute.
+        original_paths = [
+            ...
+            ]
+        for path in original_paths:
+            if path == setuptools_path or path not in known_paths:
+                addsitedir(path, known_paths)
+        return known_paths
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+    >>> print call_py(interpreter_path, "import sys; print sys.path")
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '...setuptools...',
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+     ...]
+
+As you can see, the script now first imports pkg_resources.  Then we
+need to process egg files specially to look for namespace packages there
+*before* we process process lines in .pth files that use the "import"
+feature--lines that might be part of the setuptools namespace package
+implementation for system packages, as mentioned above, and that must
+come after processing egg namespaces.
+
+The most complex that this function gets is if you use namespace packages,
+include site-packages, and use relative paths.  For completeness, we'll look
+at that result.
+
+    >>> reset_interpreter()
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', include_site_packages=True,
+    ...     relative_paths=interpreter_dir)
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
+    <BLANKLINE>
+        See original_addsitepackages, below, for the original version."""
+        join = os.path.join
+        base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
+        base = os.path.dirname(base)
+        base = os.path.dirname(base)
+        setuptools_path = '...setuptools...'
+        sys.path.append(setuptools_path)
+        known_paths.add(os.path.normcase(setuptools_path))
+        import pkg_resources
+        buildout_paths = [
+            join(base, 'eggs/demo-0.3-pyN.N.egg'),
+            join(base, 'eggs/tellmy.fortune-1.0-pyN.N.egg'),
+            '...setuptools...',
+            join(base, 'eggs/demoneeded-1.1-pyN.N.egg')
+            ]
+        for path in buildout_paths:
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.append(sitedir)
+                known_paths.add(sitedircase)
+                pkg_resources.working_set.add_entry(sitedir)
+        sys.__egginsert = len(buildout_paths) # Support distribute.
+        original_paths = [
+            ...
+            ]
+        for path in original_paths:
+            if path == setuptools_path or path not in known_paths:
+                addsitedir(path, known_paths)
+        return known_paths
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+    >>> print call_py(interpreter_path, "import sys; print sys.path")
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    ['',
+     '/interpreter/parts/interpreter',
+     ...,
+     '...setuptools...',
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+     ...]
+
+The ``exec_sitecustomize`` argument does the same thing for the
+sitecustomize module--it allows you to include the code from the
+sitecustomize module in the underlying Python if you set the argument to
+True.  The z3c.recipe.scripts package sets up the full environment necessary
+to demonstrate this piece.
+
+The ``sitepackage_safe_scripts`` function: writing scripts for entry points
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+All of the examples so far for this function have been creating
+interpreters.  The function can also write scripts for entry
+points.  They are almost identical to the scripts that we saw for the
+``scripts`` function except that they ``import site`` after setting the
+sys.path to include our custom site.py and sitecustomize.py files.  These
+files then initialize the Python environment as we have already seen.  Let's
+see a simple example.
+
+    >>> reset_interpreter()
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
+    ...     index=link_server+'index/')
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     reqs=['demo'])
+
+As before, in Windows, 2 files are generated for each script.  A script
+file, ending in '-script.py', and an exe file that allows the script
+to be invoked directly without having to specify the Python
+interpreter and without having to provide a '.py' suffix.  This is in addition
+to the site.py and sitecustomize.py files that are generated as with our
+interpreter examples above.
+
+    >>> if sys.platform == 'win32':
+    ...     demo_path = os.path.join(interpreter_bin_dir, 'demo-script.py')
+    ...     expected = [sitecustomize_path,
+    ...                 site_path,
+    ...                 os.path.join(interpreter_bin_dir, 'demo.exe'),
+    ...                 demo_path]
+    ... else:
+    ...     demo_path = os.path.join(interpreter_bin_dir, 'demo')
+    ...     expected = [sitecustomize_path, site_path, demo_path]
+    ...
+    >>> assert generated == expected, repr((generated, expected))
+
+The demo script runs the entry point defined in the demo egg:
+
+    >>> cat(demo_path) # doctest: +NORMALIZE_WHITESPACE
+    #!/usr/local/bin/python2.4 -S
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+        '/interpreter/parts/interpreter',
+        ]
+    <BLANKLINE>
+    <BLANKLINE>
+    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
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        sys.exit(eggrecipedemo.main())
+
+    >>> demo_call = join(interpreter_bin_dir, 'demo')
+    >>> if sys.platform == 'win32':
+    ...     demo_call = '"%s"' % demo_call
+    >>> print system(demo_call)
+    3 1
+    <BLANKLINE>
+
+There are a few differences from the ``scripts`` function.  First, the
+``reqs`` argument (an iterable of string requirements or entry point
+tuples) is a keyword argument here.  We see that in the example above.
+Second, the ``arguments`` argument is now named ``script_arguments`` to
+try and clarify that it does not affect interpreters. While the
+``initialization`` argument continues to affect both the interpreters
+and the entry point scripts, if you have initialization that is only
+pertinent to the entry point scripts, you can use the
+``script_initialization`` argument.
+
+Let's see ``script_arguments`` and ``script_initialization`` in action.
+
+    >>> reset_interpreter()
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     reqs=['demo'], script_arguments='1, 2',
+    ...     script_initialization='import os\nos.chdir("foo")')
+
+    >>> cat(demo_path) # doctest: +NORMALIZE_WHITESPACE
+    #!/usr/local/bin/python2.4 -S
+    import sys
+    sys.path[0:0] = [
+      '/interpreter/parts/interpreter',
+      ]
+    <BLANKLINE>
+    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 os
+    os.chdir("foo")
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        sys.exit(eggrecipedemo.main(1, 2))
+
 Handling custom build options for extensions provided in source distributions
 -----------------------------------------------------------------------------
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/extends-cache.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/extends-cache.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/extends-cache.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -383,10 +383,127 @@
 While:
   Installing.
   Checking for upgrades.
-An internal error occured ...
+An internal error occurred ...
 ValueError: install_from_cache set to true with no download cache
 
+>>> rmdir('home', '.buildout')
 
+Newest and non-newest behaviour for extends cache
+-------------------------------------------------
+
+While offline mode forbids network access completely, 'newest' mode determines
+whether to look for updated versions of a resource even if some version of it
+is already present locally. If we run buildout in newest mode
+(``newest = true``), the configuration files are updated with each run:
+
+>>> mkdir("cache")
+>>> write(server_data, 'base.cfg', """\
+... [buildout]
+... parts =
+... """)
+>>> write('buildout.cfg', """\
+... [buildout]
+... extends-cache = cache
+... extends = %sbase.cfg
+... """ % server_url)
+>>> print system(buildout)
+>>> ls('cache')
+-  5aedc98d7e769290a29d654a591a3a45
+>>> cat('cache', os.listdir(cache)[0])
+[buildout]
+parts =
+
+A change to ``base.cfg`` is picked up on the next buildout run:
+
+>>> write(server_data, 'base.cfg', """\
+... [buildout]
+... parts =
+... foo = bar
+... """)
+>>> print system(buildout + " -n")
+Unused options for buildout: 'foo'.
+>>> cat('cache', os.listdir(cache)[0])
+[buildout]
+parts =
+foo = bar
+
+In contrast, when not using ``newest`` mode (``newest = false``), the files
+already present in the extends cache will not be updated:
+
+>>> write(server_data, 'base.cfg', """\
+... [buildout]
+... parts =
+... """)
+>>> print system(buildout + " -N")
+Unused options for buildout: 'foo'.
+>>> cat('cache', os.listdir(cache)[0])
+[buildout]
+parts =
+foo = bar
+
+Even when updating base configuration files with a buildout run, any given
+configuration file will be downloaded only once during that particular run. If
+some base configuration file is extended more than once, its cached copy is
+used:
+
+>>> write(server_data, 'baseA.cfg', """\
+... [buildout]
+... extends = %sbase.cfg
+... foo = bar
+... """ % server_url)
+>>> write(server_data, 'baseB.cfg', """\
+... [buildout]
+... extends-cache = cache
+... extends = %sbase.cfg
+... bar = foo
+... """ % server_url)
+>>> write('buildout.cfg', """\
+... [buildout]
+... extends-cache = cache
+... newest = true
+... extends = %sbaseA.cfg %sbaseB.cfg
+... """ % (server_url, server_url))
+>>> print system(buildout + " -n")
+Unused options for buildout: 'bar' 'foo'.
+
+(XXX We patch download utility's API to produce readable output for the test;
+a better solution would utilise the logging already done by the utility.)
+
+>>> import zc.buildout
+>>> old_download = zc.buildout.download.Download.download
+>>> def wrapper_download(self, url, md5sum=None, path=None):
+...   print "The URL %s was downloaded." % url
+...   return old_download(url, md5sum, path)
+>>> zc.buildout.download.Download.download = wrapper_download
+
+>>> zc.buildout.buildout.main([])
+The URL http://localhost/baseA.cfg was downloaded.
+The URL http://localhost/base.cfg was downloaded.
+The URL http://localhost/baseB.cfg was downloaded.
+Unused options for buildout: 'bar' 'foo'.
+
+>>> zc.buildout.download.Download.download = old_download
+
+
+The deprecated ``extended-by`` option
+-------------------------------------
+
+The ``buildout`` section used to recognise an option named ``extended-by``
+that was deprecated at some point and removed in the 1.5 line. Since ignoring
+this option silently was considered harmful as a matter of principle, a
+UserError is raised if that option is encountered now:
+
+>>> write(server_data, 'base.cfg', """\
+... [buildout]
+... parts =
+... extended-by = foo.cfg
+... """)
+>>> print system(buildout)
+While:
+  Initializing.
+Error: No-longer supported "extended-by" option found in http://localhost/base.cfg.
+
+
 Clean up
 --------
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/rmtree.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/rmtree.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/rmtree.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,10 +1,10 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,10 +1,10 @@
 #############################################################################
 #
-# Copyright (c) 2004-2009 Zope Corporation and Contributors.
+# Copyright (c) 2004-2009 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
@@ -28,6 +28,7 @@
 import subprocess
 import sys
 import tempfile
+import textwrap
 import threading
 import time
 import urllib2
@@ -39,6 +40,18 @@
 fsync = getattr(os, 'fsync', lambda fileno: None)
 is_win32 = sys.platform == 'win32'
 
+# Only some unixes allow scripts in shebang lines:
+script_in_shebang = is_win32
+if sys.platform == 'linux2':
+    f = subprocess.Popen('uname -r', shell=True, stdout=subprocess.PIPE).stdout
+    r = f.read().strip()
+    f.close()
+    r = tuple(map(int, re.match(r'\d+(\.\d+)*', r).group(0).split('.')))
+    if r >= (2, 6, 27, 9):
+        # http://www.in-ulm.de/~mascheck/various/shebang/
+        script_in_shebang = True
+
+
 setuptools_location = pkg_resources.working_set.find(
     pkg_resources.Requirement.parse('setuptools')).location
 
@@ -90,12 +103,16 @@
 MUST_CLOSE_FDS = not sys.platform.startswith('win')
 
 def system(command, input=''):
+    env = dict(os.environ)
+    env['COLUMNS'] = '80'
     p = subprocess.Popen(command,
                          shell=True,
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE,
-                         close_fds=MUST_CLOSE_FDS)
+                         close_fds=MUST_CLOSE_FDS,
+                         env=env,
+                         )
     i, o, e = (p.stdin, p.stdout, p.stderr)
     if input:
         i.write(input)
@@ -105,6 +122,16 @@
     e.close()
     return result
 
+def call_py(interpreter, cmd, flags=None):
+    if sys.platform == 'win32':
+        args = ['"%s"' % arg for arg in (interpreter, flags, cmd) if arg]
+        args.insert(-1, '"-c"')
+        return system('"%s"' % ' '.join(args))
+    else:
+        cmd = repr(cmd)
+        return system(
+            ' '.join(arg for arg in (interpreter, flags, '-c', cmd) if arg))
+
 def get(url):
     return urllib2.urlopen(url).read()
 
@@ -116,14 +143,22 @@
     args = [zc.buildout.easy_install._safe_arg(arg)
             for arg in args]
     args.insert(0, '-q')
-    args.append(dict(os.environ, PYTHONPATH=setuptools_location))
+    env = dict(os.environ)
+    if executable == sys.executable:
+        env['PYTHONPATH'] = setuptools_location
+    # else pass an executable that has setuptools! See testselectingpython.py.
 
     here = os.getcwd()
     try:
         os.chdir(d)
-        os.spawnle(os.P_WAIT, executable,
-                   zc.buildout.easy_install._safe_arg(executable),
-                   setup, *args)
+        p = subprocess.Popen(
+            [zc.buildout.easy_install._safe_arg(executable), setup] + args,
+            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+            close_fds=True, env=env)
+        out = p.stdout.read()
+        if p.wait():
+            print out
+
         if os.path.exists('build'):
             rmtree('build')
     finally:
@@ -135,12 +170,19 @@
 def bdist_egg(setup, executable, dest):
     _runsetup(setup, executable, 'bdist_egg', '-d', dest)
 
+def sys_install(setup, dest):
+    _runsetup(setup, sys.executable, 'install', '--install-purelib', dest,
+              '--record', os.path.join(dest, '__added_files__'),
+              '--single-version-externally-managed')
+
 def find_python(version):
-    e = os.environ.get('PYTHON%s' % version)
+    env_friendly_version = ''.join(version.split('.'))
+    
+    e = os.environ.get('PYTHON%s' % env_friendly_version)
     if e is not None:
         return e
     if is_win32:
-        e = '\Python%s%s\python.exe' % tuple(version.split('.'))
+        e = '\Python%s\python.exe' % env_friendly_version
         if os.path.exists(e):
             return e
     else:
@@ -185,9 +227,10 @@
 
     raise ValueError(
         "Couldn't figure out the executable for Python %(version)s.\n"
-        "Set the environment variable PYTHON%(version)s to the location\n"
+        "Set the environment variable PYTHON%(envversion)s to the location\n"
         "of the Python %(version)s executable before running the tests."
-        % {'version': version})
+        % {'version': version,
+           'envversion': env_friendly_version})
 
 def wait_until(label, func, *args, **kw):
     if 'timeout' in kw:
@@ -202,14 +245,64 @@
         time.sleep(0.01)
     raise ValueError('Timed out waiting for: '+label)
 
+def get_installer_values():
+    """Get the current values for the easy_install module.
+
+    This is necessary because instantiating a Buildout will force the
+    Buildout's values on the installer.
+
+    Returns a dict of names-values suitable for set_installer_values."""
+    names = ('default_versions', 'download_cache', 'install_from_cache',
+             'prefer_final', 'include_site_packages',
+             'allowed_eggs_from_site_packages', 'use_dependency_links',
+             'allow_picked_versions', 'always_unzip'
+            )
+    values = {}
+    for name in names:
+        values[name] = getattr(zc.buildout.easy_install, name)()
+    return values
+
+def set_installer_values(values):
+    """Set the given values on the installer."""
+    for name, value in values.items():
+        getattr(zc.buildout.easy_install, name)(value)
+
+def make_buildout(executable=None):
+    """Make a buildout that uses this version of zc.buildout."""
+    # Create a basic buildout.cfg to avoid a warning from buildout.
+    open('buildout.cfg', 'w').write(
+        "[buildout]\nparts =\n"
+        )
+    # Get state of installer defaults so we can reinstate them (instantiating
+    # a Buildout will force the Buildout's defaults on the installer).
+    installer_values = get_installer_values()
+    # Use the buildout bootstrap command to create a buildout
+    config = [
+        ('buildout', 'log-level', 'WARNING'),
+        # trick bootstrap into putting the buildout develop egg
+        # in the eggs dir.
+        ('buildout', 'develop-eggs-directory', 'eggs'),
+        ]
+    if executable is not None:
+        config.append(('buildout', 'executable', executable))
+    zc.buildout.buildout.Buildout(
+        'buildout.cfg', config,
+        user_defaults=False,
+        ).bootstrap([])
+    # Create the develop-eggs dir, which didn't get created the usual
+    # way due to the trick above:
+    os.mkdir('develop-eggs')
+    # Reinstate the default values of the installer.
+    set_installer_values(installer_values)
+
 def buildoutSetUp(test):
 
     test.globs['__tear_downs'] = __tear_downs = []
     test.globs['register_teardown'] = register_teardown = __tear_downs.append
 
-    prefer_final = zc.buildout.easy_install.prefer_final()
+    installer_values = get_installer_values()
     register_teardown(
-        lambda: zc.buildout.easy_install.prefer_final(prefer_final)
+        lambda: set_installer_values(installer_values)
         )
 
     here = os.getcwd()
@@ -246,6 +339,7 @@
     zc.buildout.easy_install.default_index_url = 'file://'+tmp
     os.environ['buildout-testing-index-url'] = (
         zc.buildout.easy_install.default_index_url)
+    os.environ.pop('PYTHONPATH', None)
 
     def tmpdir(name):
         path = os.path.join(base, name)
@@ -255,34 +349,69 @@
     sample = tmpdir('sample-buildout')
 
     os.chdir(sample)
+    make_buildout()
 
-    # Create a basic buildout.cfg to avoid a warning from buildout:
-    open('buildout.cfg', 'w').write(
-        "[buildout]\nparts =\n"
-        )
-
-    # Use the buildout bootstrap command to create a buildout
-    zc.buildout.buildout.Buildout(
-        'buildout.cfg',
-        [('buildout', 'log-level', 'WARNING'),
-         # trick bootstrap into putting the buildout develop egg
-         # in the eggs dir.
-         ('buildout', 'develop-eggs-directory', 'eggs'),
-         ]
-        ).bootstrap([])
-
-
-
-    # Create the develop-eggs dir, which didn't get created the usual
-    # way due to the trick above:
-    os.mkdir('develop-eggs')
-
     def start_server(path):
         port, thread = _start_server(path, name=path)
         url = 'http://localhost:%s/' % port
         register_teardown(lambda: stop_server(url, thread))
         return url
 
+    def make_py(initialization=''):
+        """Returns paths to new executable and to its site-packages.
+        """
+        buildout = tmpdir('executable_buildout')
+        site_packages_dir = os.path.join(buildout, 'site-packages')
+        mkdir(site_packages_dir)
+        old_wd = os.getcwd()
+        os.chdir(buildout)
+        make_buildout()
+        # Normally we don't process .pth files in extra-paths.  We want to
+        # in this case so that we can test with setuptools system installs
+        # (--single-version-externally-managed), which use .pth files.
+        initialization = (
+            ('import sys\n'
+             'import site\n'
+             'known_paths = set(sys.path)\n'
+             'site_packages_dir = %r\n'
+             'site.addsitedir(site_packages_dir, known_paths)\n'
+            ) % (site_packages_dir,)) + initialization
+        initialization = '\n'.join(
+            '  ' + line for line in initialization.split('\n'))
+        install_develop(
+            'zc.recipe.egg', os.path.join(buildout, 'develop-eggs'))
+        install_develop(
+            'z3c.recipe.scripts', os.path.join(buildout, 'develop-eggs'))
+        write('buildout.cfg', textwrap.dedent('''\
+            [buildout]
+            parts = py
+            include-site-packages = false
+            exec-sitecustomize = false
+
+            [py]
+            recipe = z3c.recipe.scripts
+            interpreter = py
+            initialization =
+            %(initialization)s
+            extra-paths = %(site-packages)s
+            eggs = setuptools
+            ''') % {
+                'initialization': initialization,
+                'site-packages': site_packages_dir})
+        system(os.path.join(buildout, 'bin', 'buildout'))
+        os.chdir(old_wd)
+        return (
+            os.path.join(buildout, 'bin', 'py'), site_packages_dir)
+
+    cdpaths = []
+    def cd(*path):
+        path = os.path.join(*path)
+        cdpaths.append(os.path.abspath(os.getcwd()))
+        os.chdir(path)
+
+    def uncd():
+        os.chdir(cdpaths.pop())
+
     test.globs.update(dict(
         sample_buildout = sample,
         ls = ls,
@@ -293,18 +422,18 @@
         tmpdir = tmpdir,
         write = write,
         system = system,
+        call_py = call_py,
         get = get,
-        cd = (lambda *path: os.chdir(os.path.join(*path))),
+        cd = cd, uncd = uncd,
         join = os.path.join,
         sdist = sdist,
         bdist_egg = bdist_egg,
         start_server = start_server,
         buildout = os.path.join(sample, 'bin', 'buildout'),
         wait_until = wait_until,
+        make_py = make_py
         ))
 
-    zc.buildout.easy_install.prefer_final(prefer_final)
-
 def buildoutTearDown(test):
     for f in test.globs['__tear_downs']:
         f()
@@ -492,10 +621,14 @@
             path = path[1:]
     return '/' + path.replace(os.path.sep, '/')
 
+if sys.platform == 'win32':
+    sep = r'[\\/]' # Windows uses both sometimes.
+else:
+    sep = re.escape(os.path.sep)
 normalize_path = (
     re.compile(
-        r'''[^'" \t\n\r]+\%(sep)s_[Tt][Ee][Ss][Tt]_\%(sep)s([^"' \t\n\r]+)'''
-        % dict(sep=os.path.sep)),
+        r'''[^'" \t\n\r!]+%(sep)s_[Tt][Ee][Ss][Tt]_%(sep)s([^"' \t\n\r]+)'''
+        % dict(sep=sep)),
     _normalize_path,
     )
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -79,6 +79,11 @@
 
     The directory will be reset at the end of the test.
 
+``uncd()``
+    Change to the directory that was current prior to the previous
+    call to ``cd``. You can call ``cd`` multiple times and then
+    ``uncd`` the same number of times to return to the same location.
+
 ``join(*path)``
     A convenient reference to os.path.join.
 

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing_bugfix.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing_bugfix.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testing_bugfix.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -12,10 +12,9 @@
 The handers before calling set up are:
 
     >>> import logging
-    >>> len(logging.getLogger().handlers)
-    1
+    >>> oldn = len(logging.getLogger().handlers)
     >>> logging.getLogger().handlers # doctest: +ELLIPSIS
-    [<zope.testing.testrunner.logsupport.NullHandler instance at ...>]
+    [<zope...testrunner.logsupport.NullHandler instance at ...>]
 
 After calling it, a ``logging.StreamHandler`` was added:
 
@@ -24,16 +23,18 @@
     >>> test = doctest.DocTestParser().get_doctest(
     ...     '>>> x', {}, 'foo', 'foo.py', 0)
     >>> zc.buildout.testing.buildoutSetUp(test)
-    >>> len(logging.getLogger().handlers)
-    2
+    >>> len(logging.getLogger().handlers) == oldn + 1
+    True
+
     >>> logging.getLogger().handlers # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
-    [<zope.testing.testrunner.logsupport.NullHandler instance at ...>,
+    [<zope...testrunner.logsupport.NullHandler instance at ...>,
      <logging.StreamHandler instance at ...>]
 
 But tear down removes the new logging handler:
 
     >>> zc.buildout.testing.buildoutTearDown(test)
-    >>> len(logging.getLogger().handlers)
-    1
+    >>> len(logging.getLogger().handlers) == oldn
+    True
+
     >>> logging.getLogger().handlers # doctest: +ELLIPSIS
-    [<zope.testing.testrunner.logsupport.NullHandler instance at ...>]
+    [<zope...testrunner.logsupport.NullHandler instance at ...>]

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/tests.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/tests.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,17 +1,17 @@
 ##############################################################################
 #
-# Copyright (c) 2004-2009 Zope Corporation and Contributors.
+# Copyright (c) 2004-2009 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-from zope.testing import doctest
+import doctest
 from zope.testing import renormalizing
 import os
 import pkg_resources
@@ -29,7 +29,6 @@
 if os_path_sep == '\\':
     os_path_sep *= 2
 
-
 def develop_w_non_setuptools_setup_scripts():
     """
 We should be able to deal with setup scripts that aren't setuptools based.
@@ -53,6 +52,7 @@
 
     >>> ls('develop-eggs')
     -  foo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     """
@@ -84,6 +84,7 @@
 
     >>> ls('develop-eggs')
     -  foo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     >>> print system(join('bin', 'buildout')+' -vvv'), # doctest: +ELLIPSIS
@@ -383,7 +384,64 @@
     Error: Couldn't find a distribution for 'demoneeded'.
     """
 
+def show_eggs_from_site_packages():
+    """
+Sometimes you want to know what eggs are coming from site-packages.  This
+might be for a diagnostic, or so that you can get a starting value for the
+allowed-eggs-from-site-packages option.  The -v flag will also include this
+information.
 
+Our "py_path" has the "demoneeded," "demo"
+packages available.  We'll ask for "bigdemo," which will get both of them.
+
+Here's our set up.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = eggs
+    ... prefer-final = true
+    ... find-links = %(link_server)s
+    ...
+    ... [primed_python]
+    ... executable = %(py_path)s
+    ...
+    ... [eggs]
+    ... recipe = zc.recipe.egg:eggs
+    ... python = primed_python
+    ... eggs = bigdemo
+    ... ''' % globals())
+
+Now here is the output.  The lines that begin with "Egg from site-packages:"
+indicate the eggs from site-packages that have been selected.  You'll see
+we have two: demo 0.3 and demoneeded 1.1.
+
+    >>> print system(buildout+" -v"),
+    Installing 'zc.buildout', 'setuptools'.
+    We have a develop egg: zc.buildout V
+    We have the best distribution that satisfies 'setuptools'.
+    Picked: setuptools = V
+    Installing 'zc.recipe.egg'.
+    We have a develop egg: zc.recipe.egg V
+    Installing eggs.
+    Installing 'bigdemo'.
+    We have no distributions for bigdemo that satisfies 'bigdemo'.
+    Getting distribution for 'bigdemo'.
+    Got bigdemo 0.1.
+    Picked: bigdemo = 0.1
+    Getting required 'demo'
+      required by bigdemo 0.1.
+    We have the best distribution that satisfies 'demo'.
+    Egg from site-packages: demo 0.3
+    Getting required 'demoneeded'
+      required by demo 0.3.
+    We have the best distribution that satisfies 'demoneeded'.
+    Egg from site-packages: demoneeded 1.1
+    """
+
 def test_comparing_saved_options_with_funny_characters():
     """
 If an option has newlines, extra/odd spaces or a %, we need to make sure
@@ -668,6 +726,7 @@
 
     >>> ls('develop-eggs')
     -  foox.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Create another:
@@ -692,6 +751,7 @@
     >>> ls('develop-eggs')
     -  foox.egg-link
     -  fooy.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Remove one:
@@ -709,6 +769,7 @@
 
     >>> ls('develop-eggs')
     -  fooy.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Remove the other:
@@ -723,6 +784,7 @@
 All gone
 
     >>> ls('develop-eggs')
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
     '''
 
@@ -797,9 +859,11 @@
     ...            + join(sample_buildout, 'eggs'))
 
     >>> ls('develop-eggs')
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
-    >>> ls('eggs') # doctest: +ELLIPSIS
+    >>> print 'START ->'; ls('eggs') # doctest: +ELLIPSIS
+    START...
     -  foox-0.0.0-py2.4.egg
     ...
 
@@ -1376,7 +1440,7 @@
       Getting section data-dir.
       Initializing part data-dir.
     <BLANKLINE>
-    An internal error occured due to a bug in either zc.buildout or in a
+    An internal error occurred due to a bug in either zc.buildout or in a
     recipe being used:
     Traceback (most recent call last):
     ...
@@ -1769,76 +1833,836 @@
     1 2
     """
 
-if sys.version_info > (2, 4):
-    def test_exit_codes():
-        """
-        >>> import subprocess
-        >>> def call(s):
-        ...     p = subprocess.Popen(s, stdin=subprocess.PIPE,
-        ...                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        ...     p.stdin.close()
-        ...     print p.stdout.read()
-        ...     print 'Exit:', bool(p.wait())
+def versions_section_ignored_for_dependency_in_favor_of_site_packages():
+    r"""
+This is a test for a bugfix.
 
-        >>> call(buildout)
-        <BLANKLINE>
-        Exit: False
+The error showed itself when at least two dependencies were in a shared
+location like site-packages, and the first one met the "versions" setting.  The
+first dependency would be added, but subsequent dependencies from the same
+location (e.g., site-packages) would use the version of the package found in
+the shared location, ignoring the version setting.
 
-        >>> write('buildout.cfg',
-        ... '''
-        ... [buildout]
-        ... parts = x
-        ... ''')
+We begin with a Python that has demoneeded version 1.1 installed and a
+demo version 0.3, all in a site-packages-like shared directory.  We need
+to create this.  ``eggrecipedemo.main()`` shows the number after the dot
+(that is, ``X`` in ``1.X``), for the demo package and the demoneeded
+package, so this demonstrates that our Python does in fact have demo
+version 0.3 and demoneeded version 1.1.
 
-        >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE
-        While:
-          Installing.
-          Getting section x.
-        Error: The referenced section, 'x', was not defined.
-        <BLANKLINE>
-        Exit: True
+    >>> py_path = make_py_with_system_install(make_py, sample_eggs)
+    >>> print call_py(
+    ...     py_path,
+    ...     "import tellmy.version; print tellmy.version.__version__"),
+    1.1
 
-        >>> write('setup.py',
-        ... '''
-        ... from setuptools import setup
-        ... setup(name='zc.buildout.testexit', entry_points={
-        ...    'zc.buildout': ['default = testexitrecipe:x']})
-        ... ''')
+Now here's a setup that would expose the bug, using the
+zc.buildout.easy_install API.
 
-        >>> write('testexitrecipe.py',
-        ... '''
-        ... x y
-        ... ''')
+    >>> example_dest = tmpdir('example_dest')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['tellmy.version'], example_dest, links=[sample_eggs],
+    ...     executable=py_path,
+    ...     index=None,
+    ...     versions={'tellmy.version': '1.0'})
+    >>> for dist in workingset:
+    ...     res = str(dist)
+    ...     if res.startswith('tellmy.version'):
+    ...         print res
+    ...         break
+    tellmy.version 1.0
 
-        >>> write('buildout.cfg',
-        ... '''
-        ... [buildout]
-        ... parts = x
-        ... develop = .
+Before the bugfix, the desired tellmy.version distribution would have
+been blocked the one in site-packages.
+"""
+
+def handle_namespace_package_in_both_site_packages_and_buildout_eggs():
+    r"""
+If you have the same namespace package in both site-packages and in
+buildout, we need to be very careful that faux-Python-executables and
+scripts generated by easy_install.sitepackage_safe_scripts correctly
+combine the two. We show this with the local recipe that uses the
+function, z3c.recipe.scripts.
+
+To demonstrate this, we will create three packages: tellmy.version 1.0,
+tellmy.version 1.1, and tellmy.fortune 1.0.  tellmy.version 1.1 is installed.
+
+    >>> py_path = make_py_with_system_install(make_py, sample_eggs)
+    >>> print call_py(
+    ...     py_path,
+    ...     "import tellmy.version; print tellmy.version.__version__")
+    1.1
+    <BLANKLINE>
+
+Now we will create a buildout that creates a script and a faux-Python script.
+We want to see that both can successfully import the specified versions of
+tellmy.version and tellmy.fortune.
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = eggs
+    ... find-links = %(link_server)s
+    ...
+    ... [primed_python]
+    ... executable = %(py_path)s
+    ...
+    ... [eggs]
+    ... recipe = z3c.recipe.scripts
+    ... python = primed_python
+    ... interpreter = py
+    ... include-site-packages = true
+    ... eggs = tellmy.version == 1.0
+    ...        tellmy.fortune == 1.0
+    ...        demo
+    ... script-initialization =
+    ...     import tellmy.version
+    ...     print tellmy.version.__version__
+    ...     import tellmy.fortune
+    ...     print tellmy.fortune.__version__
+    ... ''' % globals())
+
+    >>> print system(buildout)
+    Installing eggs.
+    Getting distribution for 'tellmy.version==1.0'.
+    Got tellmy.version 1.0.
+    Getting distribution for 'tellmy.fortune==1.0'.
+    Got tellmy.fortune 1.0.
+    Getting distribution for 'demo'.
+    Got demo 0.4c1.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated script '/sample-buildout/bin/demo'.
+    Generated interpreter '/sample-buildout/bin/py'.
+    <BLANKLINE>
+
+Finally, we are ready to see if it worked.  Prior to the bug fix that
+this tests, the results of both calls below was the following::
+
+    1.1
+    Traceback (most recent call last):
+      ...
+    ImportError: No module named fortune
+    <BLANKLINE>
+
+In other words, we got the site-packages version of tellmy.version, and
+we could not import tellmy.fortune at all.  The following are the correct
+results for the interpreter and for the script.
+
+    >>> print call_py(
+    ...     join('bin', 'py'),
+    ...     "import tellmy.version; " +
+    ...     "print tellmy.version.__version__; " +
+    ...     "import tellmy.fortune; " +
+    ...     "print tellmy.fortune.__version__") # doctest: +ELLIPSIS
+    1.0
+    1.0...
+
+    >>> print system(join('bin', 'demo'))
+    1.0
+    1.0
+    4 2
+    <BLANKLINE>
+    """
+
+if not zc.buildout.testing.script_in_shebang:
+    del handle_namespace_package_in_both_site_packages_and_buildout_eggs
+
+
+def handle_sys_path_version_hack():
+    r"""
+This is a test for a bugfix.
+
+If you use a Python that has a different version of one of your
+dependencies, and the new package tries to do sys.path tricks in the
+setup.py to get a __version__, and it uses namespace packages, the older
+package will be loaded first, making the setup version the wrong number.
+While very arguably packages simply shouldn't do this, some do, and we
+don't want buildout to fall over when they do.
+
+To demonstrate this, we will need to create a distribution that has one of
+these unpleasant tricks, and a Python that has an older version installed.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> for version in ('1.0', '1.1'):
+    ...     tmp = tempfile.mkdtemp()
+    ...     try:
+    ...         write(tmp, 'README.txt', '')
+    ...         mkdir(tmp, 'src')
+    ...         mkdir(tmp, 'src', 'tellmy')
+    ...         write(tmp, 'src', 'tellmy', '__init__.py',
+    ...             "__import__("
+    ...             "'pkg_resources').declare_namespace(__name__)\n")
+    ...         mkdir(tmp, 'src', 'tellmy', 'version')
+    ...         write(tmp, 'src', 'tellmy', 'version',
+    ...               '__init__.py', '__version__=%r\n' % version)
+    ...         write(
+    ...             tmp, 'setup.py',
+    ...             "from setuptools import setup\n"
+    ...             "import sys\n"
+    ...             "sys.path.insert(0, 'src')\n"
+    ...             "from tellmy.version import __version__\n"
+    ...             "setup(\n"
+    ...             " name='tellmy.version',\n"
+    ...             " package_dir = {'': 'src'},\n"
+    ...             " packages = ['tellmy', 'tellmy.version'],\n"
+    ...             " install_requires = ['setuptools'],\n"
+    ...             " namespace_packages=['tellmy'],\n"
+    ...             " zip_safe=True, version=__version__,\n"
+    ...             " author='bob', url='bob', author_email='bob')\n"
+    ...             )
+    ...         zc.buildout.testing.sdist(tmp, sample_eggs)
+    ...         if version == '1.0':
+    ...             # We install the 1.0 version in site packages the way a
+    ...             # system packaging system (debs, rpms) would do it.
+    ...             zc.buildout.testing.sys_install(tmp, site_packages_path)
+    ...     finally:
+    ...         shutil.rmtree(tmp)
+    >>> print call_py(
+    ...     py_path,
+    ...     "import tellmy.version; print tellmy.version.__version__")
+    1.0
+    <BLANKLINE>
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = eggs
+    ... find-links = %(sample_eggs)s
+    ...
+    ... [primed_python]
+    ... executable = %(py_path)s
+    ...
+    ... [eggs]
+    ... recipe = zc.recipe.egg:eggs
+    ... python = primed_python
+    ... eggs = tellmy.version == 1.1
+    ... ''' % globals())
+
+Before the bugfix, running this buildout would generate this error:
+
+    Installing eggs.
+    Getting distribution for 'tellmy.version==1.1'.
+    Installing tellmy.version 1.1
+    Caused installation of a distribution:
+    tellmy.version 1.0
+    with a different version.
+    Got None.
+    While:
+      Installing eggs.
+    Error: There is a version conflict.
+    We already have: tellmy.version 1.0
+    <BLANKLINE>
+
+You can see the copiously commented fix for this in easy_install.py (see
+zc.buildout.easy_install.Installer._call_easy_install and particularly
+the comment leading up to zc.buildout.easy_install._easy_install_cmd).
+Now the install works correctly, as seen here.
+
+    >>> print system(buildout)
+    Installing eggs.
+    Getting distribution for 'tellmy.version==1.1'.
+    Got tellmy.version 1.1.
+    <BLANKLINE>
+
+    """
+
+def isolated_include_site_packages():
+    """
+
+This is an isolated test of the include_site_packages functionality, passing
+the argument directly to install, overriding a default.
+
+Our "py_path" has the "demoneeded" and "demo" packages available.  We'll
+simply be asking for "demoneeded" here.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+    >>> zc.buildout.easy_install.include_site_packages(False)
+    True
+
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None, include_site_packages=True)
+    >>> [dist.project_name for dist in workingset]
+    ['demoneeded']
+
+That worked fine.  Let's try again with site packages not allowed (and
+reversing the default).
+
+    >>> zc.buildout.easy_install.include_site_packages(True)
+    False
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None, include_site_packages=False)
+    Traceback (most recent call last):
         ...
-        ... [x]
-        ... recipe = zc.buildout.testexit
-        ... ''')
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
-        >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
-        Develop: '/sample-buildout/.'
-        While:
-          Installing.
-          Getting section x.
-          Initializing section x.
-          Loading zc.buildout recipe entry zc.buildout.testexit:default.
-        <BLANKLINE>
-        An internal error occured due to a bug in either zc.buildout or in a
-        recipe being used:
-        Traceback (most recent call last):
+That's a failure, as expected.
+
+Now we explore an important edge case.
+
+Some system Pythons include setuptools (and other Python packages) in their
+site-packages (or equivalent) using a .egg-info directory.  The pkg_resources
+module (from setuptools) considers a package installed using .egg-info to be a
+develop egg.
+
+zc.buildout.buildout.Buildout.bootstrap will make setuptools and zc.buildout
+available to the buildout via the eggs directory, for normal eggs; or the
+develop-eggs directory, for develop-eggs.
+
+If setuptools or zc.buildout is found in site-packages and considered by
+pkg_resources to be a develop egg, then the bootstrap code will use a .egg-link
+in the local develop-eggs, pointing to site-packages, in its entirety.  Because
+develop-eggs must always be available for searching for distributions, this
+indirectly brings site-packages back into the search path for distributions.
+
+Because of this, we have to take special care that we still exclude
+site-packages even in this case.  See the comments about site packages in the
+Installer._satisfied and Installer._obtain methods for the implementation
+(as of this writing).
+
+In this demonstration, we insert a link to the "demoneeded" distribution
+in our develop-eggs, which would bring the package back in, except for
+the special care we have taken to exclude it.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> mkdir(example_dest, 'develop-eggs')
+    >>> write(example_dest, 'develop-eggs', 'demoneeded.egg-link',
+    ...       site_packages_path)
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[],
+    ...     path=[join(example_dest, 'develop-eggs')],
+    ...     executable=py_path,
+    ...     index=None, include_site_packages=False)
+    Traceback (most recent call last):
         ...
-             x y
-               ^
-         SyntaxError: invalid syntax
-        <BLANKLINE>
-        Exit: True
-        """
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
 
+The MissingDistribution error shows that buildout correctly excluded the
+"site-packages" source even though it was indirectly included in the path
+via a .egg-link file.
+
+    """
+
+def include_site_packages_bug_623590():
+    """
+As mentioned in isolated_include_site_packages, some system Pythons
+include various Python packages in their site-packages (or equivalent)
+using a .egg-info directory.  The pkg_resources module (from setuptools)
+considers a package installed using .egg-info to be a develop egg
+
+We generally prefer develop eggs when we are selecting dependencies, because
+we expect them to be eggs that buildout has been told to develop.  However,
+we should not consider these site-packages eggs as develop eggs--they should
+not have automatic precedence over eggs available elsewhere.
+
+We have specific code to handle this case, as identified in bug 623590.
+See zc.buildout.easy_install.Installer._satisfied, as of this writing,
+for the pertinent code. Here's the test for the bugfix.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+    >>> zc.buildout.easy_install.include_site_packages(False)
+    True
+
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demo'], example_dest, links=[sample_eggs], executable=py_path,
+    ...     index=None, include_site_packages=True, prefer_final=False)
+    >>> [(dist.project_name, dist.version) for dist in workingset]
+    [('demo', '0.4c1'), ('demoneeded', '1.2c1')]
+"""
+
+def 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.
+
+This option interacts with include-site-packages 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.  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, allowed-eggs-from-site-packages is
+irrelevant.
+
+This test shows the interaction with the zc.buildout.easy_install API.  Another
+test below (allow_site_package_eggs_option) shows using it with a buildout.cfg.
+
+Our "py_path" has the "demoneeded" and "demo" packages available.  We'll
+simply be asking for "demoneeded" here.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None,
+    ...     allowed_eggs_from_site_packages=['demoneeded', 'other'])
+    >>> [dist.project_name for dist in workingset]
+    ['demoneeded']
+
+That worked fine.  It would work fine for a glob too.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None,
+    ...     allowed_eggs_from_site_packages=['?emon*', 'other'])
+    >>> [dist.project_name for dist in workingset]
+    ['demoneeded']
+
+But now let's try again with 'demoneeded' not allowed.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None,
+    ...     allowed_eggs_from_site_packages=['demo'])
+    Traceback (most recent call last):
+        ...
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+Here's the same, but with an empty list.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None,
+    ...     allowed_eggs_from_site_packages=[])
+    Traceback (most recent call last):
+        ...
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+Of course, this doesn't stop us from getting a package from elsewhere.  Here,
+we add a link server.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, executable=py_path,
+    ...     links=[link_server], index=link_server+'index/',
+    ...     allowed_eggs_from_site_packages=['other'])
+    >>> [dist.project_name for dist in workingset]
+    ['demoneeded']
+    >>> [dist.location for dist in workingset]
+    ['/site-packages-example-install/demoneeded-1.1-py2.6.egg']
+
+Finally, here's an example of an interaction: we say that it is OK to
+allow the "demoneeded" egg to come from site-packages, but we don't
+include-site-packages.
+
+    >>> zc.buildout.easy_install.clear_index_cache()
+    >>> rmdir(example_dest)
+    >>> example_dest = tmpdir('site-packages-example-install')
+    >>> workingset = zc.buildout.easy_install.install(
+    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
+    ...     index=None, include_site_packages=False,
+    ...     allowed_eggs_from_site_packages=['demoneeded'])
+    Traceback (most recent call last):
+        ...
+    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+    """
+
+def allowed_eggs_from_site_packages_dependencies_bugfix():
+    """
+If you specify that a package with a dependency may come from site-packages,
+that doesn't mean that the dependency may come from site-packages.  This
+is a test for a bug fix to verify that this is true.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+    >>> interpreter_dir = tmpdir('interpreter')
+    >>> interpreter_parts_dir = os.path.join(
+    ...     interpreter_dir, 'parts', 'interpreter')
+    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+    >>> mkdir(interpreter_bin_dir)
+    >>> mkdir(interpreter_dir, 'eggs')
+    >>> mkdir(interpreter_dir, 'parts')
+    >>> mkdir(interpreter_parts_dir)
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo'], join(interpreter_dir, 'eggs'), executable=py_path,
+    ...     links=[link_server], index=link_server+'index/',
+    ...     allowed_eggs_from_site_packages=['demo'])
+    >>> [dist.project_name for dist in ws]
+    ['demo', 'demoneeded']
+    >>> from pprint import pprint
+    >>> pprint([dist.location for dist in ws])
+    ['/executable_buildout/site-packages',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+    """
+
+def allowed_eggs_from_site_packages_bug_592524():
+    """
+When we use allowed_eggs_from_site_packages, we need to make sure that the
+site-packages paths are not inserted with the normal egg paths.  They already
+included at the end, and including them along with the normal egg paths will
+possibly mask subsequent egg paths.  This affects interpreters and scripts
+generated by sitepackage_safe_scripts.
+
+Our "py_path" has the "demoneeded" and "demo" packages available.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+    >>> interpreter_dir = tmpdir('interpreter')
+    >>> interpreter_parts_dir = os.path.join(
+    ...     interpreter_dir, 'parts', 'interpreter')
+    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+    >>> mkdir(interpreter_bin_dir)
+    >>> mkdir(interpreter_dir, 'eggs')
+    >>> mkdir(interpreter_dir, 'parts')
+    >>> mkdir(interpreter_parts_dir)
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo', 'other'], join(interpreter_dir, 'eggs'), executable=py_path,
+    ...     links=[link_server], index=link_server+'index/',
+    ...     allowed_eggs_from_site_packages=['demo'])
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, py_path, interpreter_parts_dir,
+    ...     interpreter='py', include_site_packages=True)
+
+Now we will look at the paths in the site.py we generated.  Notice that the
+site-packages are at the end.  They were not before this bugfix.
+
+    >>> test = 'import pprint, sys; pprint.pprint(sys.path[-4:])'
+    >>> print call_py(join(interpreter_bin_dir, 'py'), test)
+    ['/interpreter/eggs/other-1.0-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+     '/executable_buildout/eggs/setuptools-0.0-pyN.N.egg',
+     '/executable_buildout/site-packages']
+    <BLANKLINE>
+    """
+
+if not zc.buildout.testing.script_in_shebang:
+    del allowed_eggs_from_site_packages_bug_592524
+
+def subprocesses_have_same_environment_by_default():
+    """
+The scripts generated by sitepackage_safe_scripts set the PYTHONPATH so that,
+if the environment is maintained (the default behavior), subprocesses get
+the same Python packages.
+
+First, we set up a script and an interpreter.
+
+    >>> interpreter_dir = tmpdir('interpreter')
+    >>> interpreter_parts_dir = os.path.join(
+    ...     interpreter_dir, 'parts', 'interpreter')
+    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+    >>> mkdir(interpreter_bin_dir)
+    >>> mkdir(interpreter_dir, 'eggs')
+    >>> mkdir(interpreter_dir, 'parts')
+    >>> mkdir(interpreter_parts_dir)
+    >>> ws = zc.buildout.easy_install.install(
+    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
+    ...     index=link_server+'index/')
+    >>> test = (
+    ...     "import subprocess, sys; subprocess.call("
+    ...     "[sys.executable, '-c', "
+    ...     "'import eggrecipedemo; print eggrecipedemo.x'])")
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     reqs=['demo'], interpreter='py',
+    ...     script_initialization=test + '; sys.exit(0)')
+
+This works for the script.
+
+    >>> print system(join(interpreter_bin_dir, 'demo'))
+    3
+    <BLANKLINE>
+
+This also works for the generated interpreter.
+
+    >>> print call_py(join(interpreter_bin_dir, 'py'), test)
+    3
+    <BLANKLINE>
+
+If you have a PYTHONPATH in your environment, it will be honored, after
+the buildout-generated path.
+
+    >>> original_pythonpath = os.environ.get('PYTHONPATH')
+    >>> os.environ['PYTHONPATH'] = 'foo'
+    >>> test = (
+    ...     "import subprocess, sys; subprocess.call("
+    ...     "[sys.executable, '-c', "
+    ...     "'import sys, pprint; pprint.pprint(sys.path)'])")
+    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     reqs=['demo'], interpreter='py',
+    ...     script_initialization=test + '; sys.exit(0)')
+
+This works for the script.  As you can see, /sample_buildout/foo is included
+right after the "parts" directory that contains site.py and sitecustomize.py.
+You can also see, actually more easily than in the other example, that we
+have the desired eggs available.
+
+    >>> print system(join(interpreter_bin_dir, 'demo')), # doctest: +ELLIPSIS
+    ['',
+     '/interpreter/parts/interpreter',
+     '/sample-buildout/foo',
+     ...
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+This also works for the generated interpreter, with identical results.
+
+    >>> print call_py(join(interpreter_bin_dir, 'py'), test),
+    ... # doctest: +ELLIPSIS
+    ['',
+     '/interpreter/parts/interpreter',
+     '/sample-buildout/foo',
+     ...
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+    >>> # Cleanup
+    >>> if original_pythonpath:
+    ...     os.environ['PYTHONPATH'] = original_pythonpath
+    ... else:
+    ...     del os.environ['PYTHONPATH']
+    ...
+
+    """
+
+def bootstrap_makes_buildout_that_works_with_system_python():
+    r"""
+In order to work smoothly with a system Python, bootstrapping creates
+the buildout script with
+zc.buildout.easy_install.sitepackage_safe_scripts. If it did not, a
+variety of problems might happen.  For instance, if another version of
+buildout or setuptools is installed in the site-packages than is
+desired, it may cause a problem.
+
+A problem actually experienced in the field is when
+a recipe wants a different version of a dependency that is installed in
+site-packages.  We will create a similar situation, and show that it is now
+handled.
+
+First let's write a dummy recipe.
+
+    >>> mkdir(sample_buildout, 'recipes')
+    >>> write(sample_buildout, 'recipes', 'dummy.py',
+    ... '''
+    ... import logging, os, zc.buildout
+    ... class Dummy:
+    ...     def __init__(self, buildout, name, options):
+    ...         pass
+    ...     def install(self):
+    ...         return ()
+    ...     def update(self):
+    ...         pass
+    ... ''')
+    >>> write(sample_buildout, 'recipes', 'setup.py',
+    ... '''
+    ... from setuptools import setup
+    ...
+    ... setup(
+    ...     name = "recipes",
+    ...     entry_points = {'zc.buildout': ['dummy = dummy:Dummy']},
+    ...     install_requires = 'demoneeded==1.2c1',
+    ...     )
+    ... ''')
+    >>> write(sample_buildout, 'recipes', 'README.txt', " ")
+
+Now we'll try to use it with a Python that has a different version of
+demoneeded installed.
+
+    >>> py_path, site_packages_path = make_py()
+    >>> create_sample_sys_install(site_packages_path)
+    >>> rmdir('develop-eggs')
+    >>> from zc.buildout.testing import make_buildout
+    >>> make_buildout(executable=py_path)
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = recipes
+    ... parts = dummy
+    ... find-links = %(link_server)s
+    ... executable = %(py_path)s
+    ...
+    ... [dummy]
+    ... recipe = recipes:dummy
+    ... ''' % globals())
+
+Now we actually run the buildout.  Before the change, we got the following
+error:
+
+    Develop: '/sample-buildout/recipes'
+    While:
+      Installing.
+      Getting section dummy.
+      Initializing section dummy.
+      Installing recipe recipes.
+    Error: There is a version conflict.
+    We already have: demoneeded 1.1
+    but recipes 0.0.0 requires 'demoneeded==1.2c1'.
+
+Now, it is handled smoothly.
+
+    >>> print system(buildout)
+    Develop: '/sample-buildout/recipes'
+    Getting distribution for 'demoneeded==1.2c1'.
+    Got demoneeded 1.2c1.
+    Installing dummy.
+    <BLANKLINE>
+
+Here's the same story with a namespace package, which has some additional
+complications behind the scenes.  First, a recipe, in the "tellmy" namespace.
+
+    >>> mkdir(sample_buildout, 'ns')
+    >>> mkdir(sample_buildout, 'ns', 'tellmy')
+    >>> write(sample_buildout, 'ns', 'tellmy', '__init__.py',
+    ...       "__import__('pkg_resources').declare_namespace(__name__)\n")
+    >>> mkdir(sample_buildout, 'ns', 'tellmy', 'recipes')
+    >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', '__init__.py', ' ')
+    >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', 'dummy.py',
+    ... '''
+    ... import logging, os, zc.buildout
+    ... class Dummy:
+    ...     def __init__(self, buildout, name, options):
+    ...         pass
+    ...     def install(self):
+    ...         return ()
+    ...     def update(self):
+    ...         pass
+    ... ''')
+    >>> write(sample_buildout, 'ns', 'setup.py',
+    ... '''
+    ... from setuptools import setup
+    ... setup(
+    ...     name="tellmy.recipes",
+    ...     packages=['tellmy', 'tellmy.recipes'],
+    ...     install_requires=['setuptools'],
+    ...     namespace_packages=['tellmy'],
+    ...     entry_points = {'zc.buildout':
+    ...                     ['dummy = tellmy.recipes.dummy:Dummy']},
+    ...     )
+    ... ''')
+
+Now, a buildout that uses it.
+
+    >>> create_sample_namespace_eggs(sample_eggs, site_packages_path)
+    >>> rmdir('develop-eggs')
+    >>> from zc.buildout.testing import make_buildout
+    >>> make_buildout(executable=py_path)
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... develop = ns
+    ...           recipes
+    ... parts = dummy
+    ... find-links = %(link_server)s
+    ... executable = %(py_path)s
+    ...
+    ... [dummy]
+    ... recipe = tellmy.recipes:dummy
+    ... ''' % globals())
+
+Now we actually run the buildout.
+
+    >>> print system(buildout)
+    Develop: '/sample-buildout/ns'
+    Develop: '/sample-buildout/recipes'
+    Uninstalling dummy.
+    Installing dummy.
+    <BLANKLINE>
+
+    """
+
+if not zc.buildout.testing.script_in_shebang:
+    del bootstrap_makes_buildout_that_works_with_system_python
+
+
+def test_exit_codes():
+    """
+    >>> import subprocess
+    >>> def call(s):
+    ...     p = subprocess.Popen(s, stdin=subprocess.PIPE,
+    ...                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    ...     p.stdin.close()
+    ...     print p.stdout.read()
+    ...     print 'Exit:', bool(p.wait())
+
+    >>> call(buildout)
+    <BLANKLINE>
+    Exit: False
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = x
+    ... ''')
+
+    >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE
+    While:
+      Installing.
+      Getting section x.
+    Error: The referenced section, 'x', was not defined.
+    <BLANKLINE>
+    Exit: True
+
+    >>> write('setup.py',
+    ... '''
+    ... from setuptools import setup
+    ... setup(name='zc.buildout.testexit', entry_points={
+    ...    'zc.buildout': ['default = testexitrecipe:x']})
+    ... ''')
+
+    >>> write('testexitrecipe.py',
+    ... '''
+    ... x y
+    ... ''')
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = x
+    ... develop = .
+    ...
+    ... [x]
+    ... recipe = zc.buildout.testexit
+    ... ''')
+
+    >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    Develop: '/sample-buildout/.'
+    While:
+      Installing.
+      Getting section x.
+      Initializing section x.
+      Loading zc.buildout recipe entry zc.buildout.testexit:default.
+    <BLANKLINE>
+    An internal error occurred due to a bug in either zc.buildout or in a
+    recipe being used:
+    Traceback (most recent call last):
+    ...
+         x y
+           ^
+     SyntaxError: invalid syntax
+    <BLANKLINE>
+    Exit: True
+    """
+
 def bug_59270_recipes_always_start_in_buildout_dir():
     """
     Recipes can rely on running from buildout directory
@@ -2339,6 +3163,107 @@
 
     """
 
+def buildout_prefer_final_build_system_option():
+    """
+The accept-buildout-test-releases buildout option can be used for overriding
+the default preference for final distributions for recipes, buildout
+extensions, and buildout itself.  It is usually controlled via the bootstrap
+script rather than in the configuration file, but we will test the machinery
+using the file.
+
+Set up.  This creates sdists for demorecipe 1.0 and 1.1b1, and for
+demoextension 1.0 and 1.1b1.
+
+    >>> create_sample_recipe_sdists(sample_eggs)
+    >>> create_sample_extension_sdists(sample_eggs)
+
+The default is accept-buildout-test-releases = false:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = demo
+    ... find-links = %(link_server)s
+    ... extensions = demoextension
+    ...
+    ... [demo]
+    ... recipe = demorecipe
+    ... ''' % globals())
+
+    >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+    Installing ...
+    Picked: demoextension = 1.0
+    ...
+    Picked: demorecipe = 1.0
+    ...
+
+Here we see that the final versions of demorecipe and demoextension were used.
+
+We get the same behavior if we explicitly state that
+accept-buildout-test-releases = false.
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = demo
+    ... find-links = %(link_server)s
+    ... extensions = demoextension
+    ... accept-buildout-test-releases = false
+    ...
+    ... [demo]
+    ... recipe = demorecipe
+    ... ''' % globals())
+
+    >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+    Installing ...
+    Picked: demoextension = 1.0
+    ...
+    Picked: demorecipe = 1.0
+    ...
+
+If we specify accept-buildout-test-releases = true, we'll get the newest
+distributions in the build system:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = demo
+    ... find-links = %(link_server)s
+    ... extensions = demoextension
+    ... accept-buildout-test-releases = true
+    ...
+    ... [demo]
+    ... recipe = demorecipe
+    ... ''' % globals())
+
+    >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+    Installing ...
+    Picked: demoextension = 1.1b1
+    ...
+    Picked: demorecipe = 1.1b1
+    ...
+
+We get an error if we specify anything but true or false:
+
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = demo
+    ... find-links = %(link_server)s
+    ... extensions = demoextension
+    ... accept-buildout-test-releases = no
+    ...
+    ... [demo]
+    ... recipe = demorecipe
+    ... ''' % globals())
+
+    >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+    While:
+      Initializing.
+    Error: Invalid value for accept-buildout-test-releases option: no
+
+    """
+
 def develop_with_modules():
     """
 Distribution setup scripts can import modules in the distribution directory:
@@ -2367,6 +3292,7 @@
 
     >>> ls('develop-eggs')
     -  foo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     """
@@ -2385,6 +3311,13 @@
     >>> write('foo.py', '')
     >>> _ = system(buildout+' setup . sdist')
 
+    >>> if zc.buildout.easy_install.is_distribute:
+    ...     distribute_version = 'distribute = %s' % (
+    ...         pkg_resources.working_set.find(
+    ...             pkg_resources.Requirement.parse('distribute')).version,)
+    ... else:
+    ...     distribute_version = ''
+    ...
     >>> write('buildout.cfg',
     ... '''
     ... [buildout]
@@ -2396,12 +3329,14 @@
     ... [versions]
     ... setuptools = %s
     ... foo = 1
+    ... %s
     ...
     ... [foo]
     ... recipe = zc.recipe.egg
     ... eggs = foo
-    ... ''' % pkg_resources.working_set.find(
-    ...    pkg_resources.Requirement.parse('setuptools')).version)
+    ... ''' % (pkg_resources.working_set.find(
+    ...         pkg_resources.Requirement.parse('setuptools')).version,
+    ...        distribute_version))
 
     >>> print system(buildout),
     Installing foo.
@@ -2654,26 +3589,164 @@
 
 ######################################################################
 
-def create_sample_eggs(test, executable=sys.executable):
-    write = test.globs['write']
-    dest = test.globs['sample_eggs']
-    tmp = tempfile.mkdtemp()
-    try:
-        write(tmp, 'README.txt', '')
+def make_py_with_system_install(make_py, sample_eggs):
+    py_path, site_packages_path = make_py()
+    create_sample_namespace_eggs(sample_eggs, site_packages_path)
+    return py_path
 
-        for i in (0, 1, 2):
-            write(tmp, 'eggrecipedemoneeded.py', 'y=%s\ndef f():\n  pass' % i)
-            c1 = i==2 and 'c1' or ''
+def create_sample_namespace_eggs(dest, site_packages_path=None):
+    from zc.buildout.testing import write, mkdir
+    for pkg, version in (('version', '1.0'), ('version', '1.1'),
+                         ('fortune', '1.0')):
+        tmp = tempfile.mkdtemp()
+        try:
+            write(tmp, 'README.txt', '')
+            mkdir(tmp, 'src')
+            mkdir(tmp, 'src', 'tellmy')
+            write(tmp, 'src', 'tellmy', '__init__.py',
+                "__import__("
+                "'pkg_resources').declare_namespace(__name__)\n")
+            mkdir(tmp, 'src', 'tellmy', pkg)
+            write(tmp, 'src', 'tellmy', pkg,
+                  '__init__.py', '__version__=%r\n' % version)
             write(
                 tmp, 'setup.py',
                 "from setuptools import setup\n"
-                "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
-                " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
-                "author_email='bob')\n"
-                % (i, c1)
+                "setup(\n"
+                " name='tellmy.%(pkg)s',\n"
+                " package_dir = {'': 'src'},\n"
+                " packages = ['tellmy', 'tellmy.%(pkg)s'],\n"
+                " install_requires = ['setuptools'],\n"
+                " namespace_packages=['tellmy'],\n"
+                " zip_safe=True, version=%(version)r,\n"
+                " author='bob', url='bob', author_email='bob')\n"
+                % locals()
                 )
             zc.buildout.testing.sdist(tmp, dest)
+            if (site_packages_path and pkg == 'version' and version == '1.1'):
+                # We install the 1.1 version in site packages the way a
+                # system packaging system (debs, rpms) would do it.
+                zc.buildout.testing.sys_install(tmp, site_packages_path)
+        finally:
+            shutil.rmtree(tmp)
 
+def create_sample_extension_sdists(dest):
+    from zc.buildout.testing import write, mkdir
+    name = 'demoextension'
+    for version in ('1.0', '1.1b1'):
+        tmp = tempfile.mkdtemp()
+        try:
+            write(tmp, 'README.txt', '')
+            write(tmp, name + '.py',
+                  "def ext(buildout):\n"
+                  "    pass\n"
+                  "def unload(buildout):\n"
+                  "    pass\n"
+                  % locals())
+            write(tmp, 'setup.py',
+                  "from setuptools import setup\n"
+                  "setup(\n"
+                  "    name = %(name)r,\n"
+                  "    py_modules = [%(name)r],\n"
+                  "    entry_points = {\n"
+                  "       'zc.buildout.extension': "
+                              "['ext = %(name)s:ext'],\n"
+                  "       'zc.buildout.unloadextension': "
+                              "['ext = %(name)s:unload'],\n"
+                  "       },\n"
+                  "    zip_safe=True, version=%(version)r,\n"
+                  "    author='bob', url='bob', author_email='bob')\n"
+                  % locals())
+            zc.buildout.testing.sdist(tmp, dest)
+        finally:
+            shutil.rmtree(tmp)
+
+def create_sample_recipe_sdists(dest):
+    from zc.buildout.testing import write, mkdir
+    name = 'demorecipe'
+    for version in ('1.0', '1.1b1'):
+        tmp = tempfile.mkdtemp()
+        try:
+            write(tmp, 'README.txt', '')
+            write(tmp, name + '.py',
+                  "import logging, os, zc.buildout\n"
+                  "class Demorecipe:\n"
+                  "    def __init__(self, buildout, name, options):\n"
+                  "        self.name, self.options = name, options\n"
+                  "    def install(self):\n"
+                  "        return ()\n"
+                  "    def update(self):\n"
+                  "        pass\n"
+                  % locals())
+            write(tmp, 'setup.py',
+                  "from setuptools import setup\n"
+                  "setup(\n"
+                  "    name = %(name)r,\n"
+                  "    py_modules = [%(name)r],\n"
+                  "    entry_points = {'zc.buildout': "
+                                       "['default = %(name)s:Demorecipe']},\n"
+                  "    zip_safe=True, version=%(version)r,\n"
+                  "    author='bob', url='bob', author_email='bob')\n"
+                  % locals())
+            zc.buildout.testing.sdist(tmp, dest)
+        finally:
+            shutil.rmtree(tmp)
+
+def _write_eggrecipedemoneeded(tmp, minor_version, suffix=''):
+    from zc.buildout.testing import write
+    write(tmp, 'README.txt', '')
+    write(tmp, 'eggrecipedemoneeded.py',
+          'y=%s\ndef f():\n  pass' % minor_version)
+    write(
+        tmp, 'setup.py',
+        "from setuptools import setup\n"
+        "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
+        " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
+        "author_email='bob')\n"
+        % (minor_version, suffix)
+        )
+
+def _write_eggrecipedemo(tmp, minor_version, suffix=''):
+    from zc.buildout.testing import write
+    write(tmp, 'README.txt', '')
+    write(
+        tmp, 'eggrecipedemo.py',
+        'import eggrecipedemoneeded\n'
+        'x=%s\n'
+        'def main(): print x, eggrecipedemoneeded.y\n'
+        % minor_version)
+    write(
+        tmp, 'setup.py',
+        "from setuptools import setup\n"
+        "setup(name='demo', py_modules=['eggrecipedemo'],"
+        " install_requires = 'demoneeded',"
+        " entry_points={'console_scripts': "
+             "['demo = eggrecipedemo:main']},"
+        " zip_safe=True, version='0.%s%s')\n" % (minor_version, suffix)
+        )
+
+def create_sample_sys_install(site_packages_path):
+    for creator, minor_version in (
+        (_write_eggrecipedemoneeded, 1),
+        (_write_eggrecipedemo, 3)):
+        # Write the files and install in site_packages_path.
+        tmp = tempfile.mkdtemp()
+        try:
+            creator(tmp, minor_version)
+            zc.buildout.testing.sys_install(tmp, site_packages_path)
+        finally:
+            shutil.rmtree(tmp)
+
+def create_sample_eggs(test, executable=sys.executable):
+    from zc.buildout.testing import write
+    dest = test.globs['sample_eggs']
+    tmp = tempfile.mkdtemp()
+    try:
+        for i in (0, 1, 2):
+            suffix = i==2 and 'c1' or ''
+            _write_eggrecipedemoneeded(tmp, i, suffix)
+            zc.buildout.testing.sdist(tmp, dest)
+
         write(
             tmp, 'setup.py',
             "from setuptools import setup\n"
@@ -2685,22 +3758,8 @@
         os.remove(os.path.join(tmp, 'eggrecipedemoneeded.py'))
 
         for i in (1, 2, 3, 4):
-            write(
-                tmp, 'eggrecipedemo.py',
-                'import eggrecipedemoneeded\n'
-                'x=%s\n'
-                'def main(): print x, eggrecipedemoneeded.y\n'
-                % i)
-            c1 = i==4 and 'c1' or ''
-            write(
-                tmp, 'setup.py',
-                "from setuptools import setup\n"
-                "setup(name='demo', py_modules=['eggrecipedemo'],"
-                " install_requires = 'demoneeded',"
-                " entry_points={'console_scripts': "
-                     "['demo = eggrecipedemo:main']},"
-                " zip_safe=True, version='0.%s%s')\n" % (i, c1)
-                )
+            suffix = i==4 and 'c1' or ''
+            _write_eggrecipedemo(tmp, i, suffix)
             zc.buildout.testing.bdist_egg(tmp, executable, dest)
 
         write(tmp, 'eggrecipebigdemo.py', 'import eggrecipedemo')
@@ -2776,40 +3835,60 @@
         test.globs['sample_eggs'])
     test.globs['update_extdemo'] = lambda : add_source_dist(test, 1.5)
     zc.buildout.testing.install_develop('zc.recipe.egg', test)
+    zc.buildout.testing.install_develop('z3c.recipe.scripts', test)
 
+def buildout_txt_setup(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    mkdir = test.globs['mkdir']
+    eggs = os.environ['buildout-testing-index-url'][7:]
+    test.globs['sample_eggs'] = eggs
+    create_sample_eggs(test)
+
+    for name in os.listdir(eggs):
+        if '-' in name:
+            pname = name.split('-')[0]
+            if not os.path.exists(os.path.join(eggs, pname)):
+                mkdir(eggs, pname)
+            shutil.move(os.path.join(eggs, name),
+                        os.path.join(eggs, pname, name))
+
+    dist = pkg_resources.working_set.find(
+        pkg_resources.Requirement.parse('zc.recipe.egg'))
+    mkdir(eggs, 'zc.recipe.egg')
+    zc.buildout.testing.sdist(
+        os.path.dirname(dist.location),
+        os.path.join(eggs, 'zc.recipe.egg'),
+        )
+
 egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$'
                        ).match
-def makeNewRelease(project, ws, dest):
+def makeNewRelease(project, ws, dest, version='99.99'):
     dist = ws.find(pkg_resources.Requirement.parse(project))
     eggname, oldver, pyver = egg_parse(
         os.path.basename(dist.location)
         ).groups()
-    dest = os.path.join(dest, "%s-99.99-py%s.egg" % (eggname, pyver))
+    dest = os.path.join(dest, "%s-%s-py%s.egg" % (eggname, version, pyver))
     if os.path.isfile(dist.location):
         shutil.copy(dist.location, dest)
         zip = zipfile.ZipFile(dest, 'a')
         zip.writestr(
             'EGG-INFO/PKG-INFO',
             zip.read('EGG-INFO/PKG-INFO').replace("Version: %s" % oldver,
-                                                  "Version: 99.99")
+                                                  "Version: %s" % version)
             )
         zip.close()
     else:
         shutil.copytree(dist.location, dest)
         info_path = os.path.join(dest, 'EGG-INFO', 'PKG-INFO')
         info = open(info_path).read().replace("Version: %s" % oldver,
-                                              "Version: 99.99")
+                                              "Version: %s" % version)
         open(info_path, 'w').write(info)
 
-
-def updateSetup(test):
-    zc.buildout.testing.buildoutSetUp(test)
-    new_releases = test.globs['tmpdir']('new_releases')
-    test.globs['new_releases'] = new_releases
+def getWorkingSetWithBuildoutEgg(test):
     sample_buildout = test.globs['sample_buildout']
     eggs = os.path.join(sample_buildout, 'eggs')
 
-    # If the zc.buildout dist is a develo dist, convert it to a
+    # If the zc.buildout dist is a develop dist, convert it to a
     # regular egg in the sample buildout
     req = pkg_resources.Requirement.parse('zc.buildout')
     dist = pkg_resources.working_set.find(req)
@@ -2818,7 +3897,8 @@
         here = os.getcwd()
         os.chdir(os.path.dirname(dist.location))
         assert os.spawnle(
-            os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable),
+            os.P_WAIT, sys.executable,
+            zc.buildout.easy_install._safe_arg(sys.executable),
             os.path.join(os.path.dirname(dist.location), 'setup.py'),
             '-q', 'bdist_egg', '-d', eggs,
             dict(os.environ,
@@ -2838,65 +3918,134 @@
             os.path.join(sample_buildout, 'bin'))
     else:
         ws = pkg_resources.working_set
+    return ws
 
+def updateSetup(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    new_releases = test.globs['tmpdir']('new_releases')
+    test.globs['new_releases'] = new_releases
+    ws = getWorkingSetWithBuildoutEgg(test)
     # now let's make the new releases
     makeNewRelease('zc.buildout', ws, new_releases)
-    makeNewRelease('setuptools', ws, new_releases)
-
+    makeNewRelease('zc.buildout', ws, new_releases, '100.0b1')
     os.mkdir(os.path.join(new_releases, 'zc.buildout'))
-    os.mkdir(os.path.join(new_releases, 'setuptools'))
+    if zc.buildout.easy_install.is_distribute:
+        makeNewRelease('distribute', ws, new_releases)
+        os.mkdir(os.path.join(new_releases, 'distribute'))
+    else:
+        makeNewRelease('setuptools', ws, new_releases)
+        os.mkdir(os.path.join(new_releases, 'setuptools'))
 
+def bootstrapSetup(test):
+    easy_install_SetUp(test)
+    sample_eggs = test.globs['sample_eggs']
+    ws = getWorkingSetWithBuildoutEgg(test)
+    makeNewRelease('zc.buildout', ws, sample_eggs)
+    makeNewRelease('zc.buildout', ws, sample_eggs, '100.0b1')
+    os.environ['bootstrap-testing-find-links'] = test.globs['link_server']
 
-
 normalize_bang = (
     re.compile(re.escape('#!'+
                          zc.buildout.easy_install._safe_arg(sys.executable))),
     '#!/usr/local/bin/python2.4',
     )
 
+hide_distribute_additions = (re.compile('install_dir .+\n'), '')
+hide_zip_safe_message = (
+    # This comes in a different place in the output in Python 2.7.  It's not
+    # important to our tests.  Hide it.
+    re.compile(
+        '((?<=\n)\n)?zip_safe flag not set; analyzing archive contents...\n'),
+    '')
+hide_first_index_page_message = (
+    # This comes in a different place in the output in Python 2.7.  It's not
+    # important to our tests.  Hide it.
+    re.compile(
+        "Couldn't find index page for '[^']+' \(maybe misspelled\?\)\n"),
+    '')
 def test_suite():
     test_suite = [
         doctest.DocFileSuite(
-            'buildout.txt', 'runsetup.txt', 'repeatable.txt', 'setup.txt',
+            'buildout.txt',
+            setUp=buildout_txt_setup,
+            tearDown=zc.buildout.testing.buildoutTearDown,
+            checker=renormalizing.RENormalizing([
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.hide_distribute_additions,
+                hide_zip_safe_message,
+                (re.compile('__buildout_signature__ = recipes-\S+'),
+                 '__buildout_signature__ = recipes-SSSSSSSSSSS'),
+                (re.compile('executable = [\S ]+python\S*', re.I),
+                 'executable = python'),
+                (re.compile('[-d]  (setuptools|distribute)-\S+[.]egg'),
+                 'setuptools.egg'),
+                (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+                 'zc.buildout.egg'),
+                (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
+                (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
+                (re.compile('Picked: (\S+) = \S+'),
+                 'Picked: \\1 = V.V'),
+                (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
+                 'We have a develop egg: zc.buildout X.X.'),
+                (re.compile(r'\\[\\]?'), '/'),
+                (re.compile('WindowsError'), 'OSError'),
+                (re.compile(r'\[Error \d+\] Cannot create a file '
+                            r'when that file already exists: '),
+                 '[Errno 17] File exists: '
+                 ),
+                (re.compile('distribute'), 'setuptools'),
+                (re.compile('Got zc.recipe.egg \S+'), 'Got zc.recipe.egg'),
+                ])
+            ),
+        doctest.DocFileSuite(
+            'runsetup.txt', 'repeatable.txt', 'setup.txt',
             setUp=zc.buildout.testing.buildoutSetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               (re.compile('__buildout_signature__ = recipes-\S+'),
-                '__buildout_signature__ = recipes-SSSSSSSSSSS'),
-               (re.compile('executable = [\S ]+python\S*', re.I),
-                'executable = python'),
-               (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
-               (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
-                'zc.buildout.egg'),
-               (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
-               (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
-               (re.compile('Picked: (\S+) = \S+'),
-                'Picked: \\1 = V.V'),
-               (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
-                'We have a develop egg: zc.buildout X.X.'),
-               (re.compile(r'\\[\\]?'), '/'),
-               (re.compile('WindowsError'), 'OSError'),
-               (re.compile(r'\[Error 17\] Cannot create a file '
-                           r'when that file already exists: '),
-                '[Errno 17] File exists: '
-                ),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.hide_distribute_additions,
+                hide_zip_safe_message,
+                (re.compile('__buildout_signature__ = recipes-\S+'),
+                 '__buildout_signature__ = recipes-SSSSSSSSSSS'),
+                (re.compile('executable = [\S ]+python\S*', re.I),
+                 'executable = python'),
+                (re.compile('[-d]  (setuptools|distribute)-\S+[.]egg'),
+                 'setuptools.egg'),
+                (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+                 'zc.buildout.egg'),
+                (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
+                (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
+                (re.compile('Picked: (\S+) = \S+'),
+                 'Picked: \\1 = V.V'),
+                (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
+                 'We have a develop egg: zc.buildout X.X.'),
+                (re.compile(r'\\[\\]?'), '/'),
+                (re.compile('WindowsError'), 'OSError'),
+                (re.compile(r'\[Error \d+\] Cannot create a file '
+                            r'when that file already exists: '),
+                 '[Errno 17] File exists: '
+                 ),
+                (re.compile('distribute'), 'setuptools'),
+                ])
             ),
         doctest.DocFileSuite(
             'debugging.txt',
             setUp=zc.buildout.testing.buildoutSetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               (re.compile(r'\S+buildout.py'), 'buildout.py'),
-               (re.compile(r'line \d+'), 'line NNN'),
-               (re.compile(r'py\(\d+\)'), 'py(NNN)'),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile(r'\S+buildout.py'), 'buildout.py'),
+                (re.compile(r'line \d+'), 'line NNN'),
+                (re.compile(r'py\(\d+\)'), 'py(NNN)'),
+                ])
             ),
 
         doctest.DocFileSuite(
@@ -2904,19 +4053,29 @@
             setUp=updateSetup,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               normalize_bang,
-               (re.compile('99[.]99'), 'NINETYNINE.NINETYNINE'),
-               (re.compile('(zc.buildout|setuptools)-\d+[.]\d+\S*'
-                           '-py\d.\d.egg'),
-                '\\1.egg'),
-               (re.compile('(zc.buildout|setuptools)( version)? \d+[.]\d+\S*'),
-                '\\1 V.V'),
-               (re.compile('[-d]  setuptools'), '-  setuptools'),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                normalize_bang,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('99[.]99'), 'NINETYNINE.NINETYNINE'),
+                (re.compile('(zc.buildout|setuptools)-\d+[.]\d+\S*'
+                            '-py\d.\d.egg'),
+                 '\\1.egg'),
+                (re.compile('distribute-\d+[.]\d+\S*'
+                            '-py\d.\d.egg'),
+                 'setuptools.egg'),
+                (re.compile('(zc.buildout|setuptools)( version)? \d+[.]\d+\S*'),
+                 '\\1 V.V'),
+                (re.compile('distribute( version)? \d+[.]\d+\S*'),
+                 'setuptools V.V'),
+                (re.compile('[-d]  (setuptools|distribute)'), '-  setuptools'),
+                (re.compile('distribute'), 'setuptools'),
+                (re.compile("\nUnused options for buildout: "
+                            "'(distribute|setuptools)\-version'\."),
+                 '')
+                ])
             ),
 
         doctest.DocFileSuite(
@@ -2925,19 +4084,30 @@
             setUp=easy_install_SetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               normalize_bang,
-               (re.compile('extdemo[.]pyd'), 'extdemo.so'),
-               (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
-               (re.compile(r'\\[\\]?'), '/'),
-               ]+(sys.version_info < (2, 5) and [
-                  (re.compile('.*No module named runpy.*', re.S), ''),
-                  (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),
-                  (re.compile('.*Error: what does not exist.*', re.S), ''),
-                  ] or [])),
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                normalize_bang,
+                hide_first_index_page_message,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('extdemo[.]pyd'), 'extdemo.so'),
+                (re.compile('[-d]  (setuptools|distribute)-\S+[.]egg'),
+                 'setuptools.egg'),
+                (re.compile(r'\\[\\]?'), '/'),
+                (re.compile(r'\#!\S+\bpython\S*'), '#!/usr/bin/python'),
+                # Normalize generate_script's Windows interpreter to UNIX:
+                (re.compile(r'\nimport subprocess\n'), '\n'),
+                (re.compile('subprocess\\.call\\(argv, env=environ\\)'),
+                 'os.execve(sys.executable, argv, environ)'),
+                (re.compile('distribute'), 'setuptools'),
+                # Distribute unzips eggs by default.
+                (re.compile('\-  demoneeded'), 'd  demoneeded'),
+                ]+(sys.version_info < (2, 5) and [
+                   (re.compile('.*No module named runpy.*', re.S), ''),
+                   (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),
+                   (re.compile('.*Error: what does not exist.*', re.S), ''),
+                   ] or [])),
             ),
 
         doctest.DocFileSuite(
@@ -2958,64 +4128,86 @@
             setUp=easy_install_SetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               (re.compile("buildout: Running \S*setup.py"),
-                'buildout: Running setup.py'),
-               (re.compile('setuptools-\S+-'),
-                'setuptools.egg'),
-               (re.compile('zc.buildout-\S+-'),
-                'zc.buildout.egg'),
-               (re.compile('File "\S+one.py"'),
-                'File "one.py"'),
-               (re.compile(r'We have a develop egg: (\S+) (\S+)'),
-                r'We have a develop egg: \1 V'),
-               (re.compile('Picked: setuptools = \S+'),
-                'Picked: setuptools = V'),
-               (re.compile(r'\\[\\]?'), '/'),
-               (re.compile(
-                   '-q develop -mxN -d "/sample-buildout/develop-eggs'),
-                   '-q develop -mxN -d /sample-buildout/develop-eggs'
-                ),
-               (re.compile(r'^[*]...'), '...'),
-               ]),
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.hide_distribute_additions,
+                hide_first_index_page_message,
+                (re.compile("buildout: Running \S*setup.py"),
+                 'buildout: Running setup.py'),
+                (re.compile('(setuptools|distribute)-\S+-'),
+                 'setuptools.egg'),
+                (re.compile('zc.buildout-\S+-'),
+                 'zc.buildout.egg'),
+                (re.compile('File "\S+one.py"'),
+                 'File "one.py"'),
+                (re.compile(r'We have a develop egg: (\S+) (\S+)'),
+                 r'We have a develop egg: \1 V'),
+                (re.compile('Picked: (setuptools|distribute) = \S+'),
+                 'Picked: setuptools = V'),
+                (re.compile(r'\\[\\]?'), '/'),
+                (re.compile(
+                    '-q develop -mxN -d "/sample-buildout/develop-eggs'),
+                    '-q develop -mxN -d /sample-buildout/develop-eggs'
+                 ),
+                (re.compile(r'^[*]...'), '...'),
+                # for bug_92891_bootstrap_crashes_with_egg_recipe_in_buildout_section
+                (re.compile(r"Unused options for buildout: 'eggs' 'scripts'\."),
+                 "Unused options for buildout: 'scripts' 'eggs'."),
+                (re.compile('distribute'), 'setuptools'),
+                # Distribute unzips eggs by default.
+                (re.compile('\-  demoneeded'), 'd  demoneeded'),
+                ]),
             ),
-        zc.buildout.testselectingpython.test_suite(),
         zc.buildout.rmtree.test_suite(),
         doctest.DocFileSuite(
             'windows.txt',
             setUp=zc.buildout.testing.buildoutSetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               (re.compile('__buildout_signature__ = recipes-\S+'),
-                '__buildout_signature__ = recipes-SSSSSSSSSSS'),
-               (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
-               (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
-                'zc.buildout.egg'),
-               (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
-               (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
-               (re.compile('Picked: (\S+) = \S+'),
-                'Picked: \\1 = V.V'),
-               (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
-                'We have a develop egg: zc.buildout X.X.'),
-               (re.compile(r'\\[\\]?'), '/'),
-               (re.compile('WindowsError'), 'OSError'),
-               (re.compile(r'\[Error 17\] Cannot create a file '
-                           r'when that file already exists: '),
-                '[Errno 17] File exists: '
-                ),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('__buildout_signature__ = recipes-\S+'),
+                 '__buildout_signature__ = recipes-SSSSSSSSSSS'),
+                (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
+                (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+                 'zc.buildout.egg'),
+                (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
+                (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
+                (re.compile('Picked: (\S+) = \S+'),
+                 'Picked: \\1 = V.V'),
+                (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
+                 'We have a develop egg: zc.buildout X.X.'),
+                (re.compile(r'\\[\\]?'), '/'),
+                (re.compile('WindowsError'), 'OSError'),
+                (re.compile(r'\[Error 17\] Cannot create a file '
+                            r'when that file already exists: '),
+                 '[Errno 17] File exists: '
+                 ),
+                ])
             ),
         doctest.DocFileSuite(
-            'testing_bugfix.txt'),
+            'testing_bugfix.txt',
+            checker=renormalizing.RENormalizing([
+                # Python 2.7
+                (re.compile(
+                    re.escape(
+                        'testrunner.logsupport.NullHandler instance at')),
+                 'testrunner.logsupport.NullHandler object at'),
+                (re.compile(re.escape('logging.StreamHandler instance at')),
+                 'logging.StreamHandler object at'),
+                ])
+            ),
     ]
 
+
+    if zc.buildout.testing.script_in_shebang:
+        test_suite.append(zc.buildout.testselectingpython.test_suite())
+
     # adding bootstrap.txt doctest to the suite
     # only if bootstrap.py is present
     bootstrap_py = os.path.join(
@@ -3031,15 +4223,38 @@
     if os.path.exists(bootstrap_py):
         test_suite.append(doctest.DocFileSuite(
             'bootstrap.txt',
+            setUp=bootstrapSetup,
+            tearDown=zc.buildout.testing.buildoutTearDown,
+            checker=renormalizing.RENormalizing([
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                normalize_bang,
+                (re.compile('Downloading.*setuptools.*egg\n'), ''),
+                (re.compile('options:'), 'Options:'),
+                (re.compile('usage:'), 'Usage:'),
+                ]),
+            ))
+        test_suite.append(doctest.DocFileSuite(
+            'virtualenv.txt',
             setUp=easy_install_SetUp,
             tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               normalize_bang,
-               (re.compile('Downloading.*setuptools.*egg\n'), ''),
-               ]),
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('(setuptools|distribute)-\S+-'),
+                 'setuptools.egg'),
+                (re.compile('zc.buildout-\S+-'),
+                 'zc.buildout.egg'),
+                (re.compile(re.escape('#!"/executable_buildout/bin/py"')),
+                 '#!/executable_buildout/bin/py'), # Windows.
+                (re.compile(re.escape('/broken_s/')),
+                 '/broken_S/'), # Windows.
+                ]),
             ))
 
     return unittest.TestSuite(test_suite)

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testselectingpython.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testselectingpython.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/testselectingpython.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,25 +1,26 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-import os, re, sys, unittest
-from zope.testing import doctest, renormalizing
+import os, re, subprocess, sys, textwrap, unittest, doctest
+from zope.testing import renormalizing
 import zc.buildout.tests
 import zc.buildout.testing
 
-if sys.version_info[:2] == (2, 4):
+
+if sys.version_info[:2] == (2, 5):
+    other_version = "2.6"
+else:
     other_version = "2.5"
-else:
-    other_version = "2.4"
 
 __test__ = dict(
     test_selecting_python_via_easy_install=
@@ -42,6 +43,34 @@
 
 def multi_python(test):
     other_executable = zc.buildout.testing.find_python(other_version)
+    command = textwrap.dedent('''\
+        try:
+            import setuptools
+        except ImportError:
+            import sys
+            sys.exit(1)
+        ''')
+    env = dict(os.environ)
+    env.pop('PYTHONPATH', None)
+    if subprocess.call([other_executable, '-c', command], env=env):
+        # the other executable does not have setuptools.  Get setuptools.
+        # We will do this using the same tools we are testing, for better or
+        # worse.  Alternatively, we could try using bootstrap.
+        executable_dir = test.globs['tmpdir']('executable_dir')
+        executable_parts = os.path.join(executable_dir, 'parts')
+        test.globs['mkdir'](executable_parts)
+        ws = zc.buildout.easy_install.install(
+            ['setuptools'], executable_dir,
+            index='http://www.python.org/pypi/',
+            always_unzip=True, executable=other_executable)
+        zc.buildout.easy_install.sitepackage_safe_scripts(
+            executable_dir, ws, other_executable, executable_parts,
+            reqs=['setuptools'], interpreter='py')
+        original_executable = other_executable
+        other_executable = os.path.join(executable_dir, 'py')
+        assert not subprocess.call(
+            [other_executable, '-c', command], env=env), (
+            'test set up failed')
     sample_eggs = test.globs['tmpdir']('sample_eggs')
     os.mkdir(os.path.join(sample_eggs, 'index'))
     test.globs['sample_eggs'] = sample_eggs

Modified: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/update.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/update.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/update.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -3,13 +3,14 @@
 
 When a buildout is run, one of the first steps performed is to check
 for updates to either zc.buildout or setuptools.  To demonstrate this,
-we've creates some "new releases" of buildout and setuptools in a
+we've created some "new releases" of buildout and setuptools in a
 new_releases folder:
 
     >>> ls(new_releases)
     d  setuptools
     -  setuptools-99.99-py2.4.egg
     d  zc.buildout
+    -  zc.buildout-100.0b1-pyN.N.egg
     -  zc.buildout-99.99-py2.4.egg
 
 Let's update the sample buildout.cfg to look in this area:
@@ -78,25 +79,49 @@
     zc.buildout 99.99
     setuptools 99.99
 
-Our buildout script has been updated to use the new eggs:
+Notice that, even though we have a newer beta version of zc.buildout
+available, the final "99.99" was selected.  If you want to get non-final
+versions, specify a specific version in your buildout's versions
+section, you typically want to use the --accept-buildout-test-releases
+option to the bootstrap script, which internally uses the
+``accept-buildout-test-releases = true`` discussed below.
 
-    >>> cat(sample_buildout, 'bin', 'buildout')
-    #!/usr/local/bin/python2.4
+Our buildout script's site.py has been updated to use the new eggs:
+
+    >>> cat(sample_buildout, 'parts', 'buildout', 'site.py')
+    ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    "...
+    def addsitepackages(known_paths):
+        """Add site packages, as determined by zc.buildout.
     <BLANKLINE>
-    import sys
-    sys.path[0:0] = [
-      '/sample-buildout/eggs/zc.buildout-99.99-py2.4.egg',
-      '/sample-buildout/eggs/setuptools-99.99-py2.4.egg',
-      ]
-    <BLANKLINE>
-    import zc.buildout.buildout
-    <BLANKLINE>
-    if __name__ == '__main__':
-        zc.buildout.buildout.main()
+        See original_addsitepackages, below, for the original version."""
+        setuptools_path = '/sample-buildout/eggs/setuptools-99.99-pyN.N.egg'
+        sys.path.append(setuptools_path)
+        known_paths.add(os.path.normcase(setuptools_path))
+        import pkg_resources
+        buildout_paths = [
+            '/sample-buildout/eggs/zc.buildout-99.99-pyN.N.egg',
+            '/sample-buildout/eggs/setuptools-99.99-pyN.N.egg'
+            ]
+        for path in buildout_paths:
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.append(sitedir)
+                known_paths.add(sitedircase)
+                pkg_resources.working_set.add_entry(sitedir)
+        sys.__egginsert = len(buildout_paths) # Support setuptools.
+        original_paths = [
+            ...
+            ]
+        for path in original_paths:
+            if path == setuptools_path or path not in known_paths:
+                addsitedir(path, known_paths)
+        return known_paths
+    ...
 
 Now, let's recreate the sample buildout. If we specify constraints on
-the versions of zc.buildout and setuptools to use, running the
-buildout will install earlier versions of these packages:
+the versions of zc.buildout and setuptools (or distribute) to use,
+running the buildout will install earlier versions of these packages:
 
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
@@ -107,6 +132,7 @@
     ... develop = showversions
     ... zc.buildout-version = < 99
     ... setuptools-version = < 99
+    ... distribute-version = < 99
     ...
     ... [show-versions]
     ... recipe = showversions
@@ -119,7 +145,6 @@
       zc.buildout version 1.0.0,
       setuptools version 0.6;
     restarting.
-    Generated script '/sample-buildout/bin/buildout'.
     Develop: '/sample-buildout/showversions'
     Updating show-versions.
     zc.buildout 1.0.0
@@ -157,7 +182,7 @@
     setuptools 0.6
 
 We also won't upgrade if the buildout script being run isn't in the
-buildouts bin directory.  To see this we'll create a new buildout
+buildout's bin directory.  To see this we'll create a new buildout
 directory:
 
     >>> sample_buildout2 = tmpdir('sample_buildout2')
@@ -182,3 +207,90 @@
     Not upgrading because not running a local buildout command.
 
     >>> ls('bin')
+
+As mentioned above, the ``accept-buildout-test-releases = true`` means that
+newer non-final versions of these dependencies are preferred.  Typically
+users are not expected to actually manipulate this value.  Instead, the
+bootstrap script creates a buildout buildout script that passes in the
+value as a command line override. This then results in the buildout
+script being rewritten to remember the decision.
+
+We'll mimic this by passing the argument actually in the command line.
+
+    >>> cd(sample_buildout)
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... find-links = %(new_releases)s
+    ... index = %(new_releases)s
+    ... parts = show-versions
+    ... develop = showversions
+    ...
+    ... [show-versions]
+    ... recipe = showversions
+    ... """ % dict(new_releases=new_releases))
+
+    >>> print system(buildout +
+    ...              ' buildout:accept-buildout-test-releases=true'),
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Getting distribution for 'zc.buildout'.
+    Got zc.buildout 100.0b1.
+    Upgraded:
+      zc.buildout version 100.0b1,
+      setuptools version 99.99;
+    restarting.
+    Generated script '/sample-buildout/bin/buildout'.
+    NOTE: Accepting early releases of build system packages.  Rerun bootstrap
+          without --accept-buildout-test-releases (-t) to return to default
+          behavior.
+    Develop: '/sample-buildout/showversions'
+    Updating show-versions.
+    zc.buildout 100.0b1
+    setuptools 99.99
+
+The buildout script shows the change.
+
+    >>> buildout_script = join(sample_buildout, 'bin', 'buildout')
+    >>> import sys
+    >>> if sys.platform.startswith('win'):
+    ...     buildout_script += '-script.py'
+    >>> print open(buildout_script).read() # doctest: +ELLIPSIS
+    #...
+    sys.argv.insert(1, 'buildout:accept-buildout-test-releases=true')
+    print ('NOTE: Accepting early releases of build system packages.  Rerun '
+           'bootstrap without --accept-buildout-test-releases (-t) to return to '
+           'default behavior.')
+    ...
+
+If the update process for buildout or setuptools fails the error should be
+caught (displaying a warning) and the rest of the buildout update process
+should continue.
+
+    >>> version = sys.version_info[0:2]
+    >>> egg = new_releases + '/zc.buildout-99.99-py%s.%s.egg ' % version
+    >>> copy_egg = new_releases + '/zc.buildout-1000-py%s.%s.egg ' % version
+    >>> system('cp ' + egg  + copy_egg)
+    ''
+
+Create a broken egg
+
+    >>> mkdir(sample_buildout, 'broken')
+    >>> write(sample_buildout, 'broken', 'setup.py', "import broken_egg\n")
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... find-links = %(new_releases)s
+    ... index = %(new_releases)s
+    ... parts = show-versions
+    ... develop =
+    ...   broken
+    ...
+    ... [broken]
+    ... recipe = zc.recipe.egg
+    ... eggs = broken
+    ... """ % dict(new_releases=new_releases))
+    >>> import subprocess
+    >>> subprocess.check_call([buildout])
+    Traceback (most recent call last):
+        ...
+    CalledProcessError: Command '['/sample-buildout/bin/buildout']' returned non-zero exit status 1

Copied: zc.buildout/branches/encolpe-escape-command/src/zc/buildout/virtualenv.txt (from rev 124424, zc.buildout/trunk/src/zc/buildout/virtualenv.txt)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/src/zc/buildout/virtualenv.txt	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/src/zc/buildout/virtualenv.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,257 @@
+Version 1.5.0 of buildout (and higher) provides the ability to use
+buildout directly with a system Python if you use z3c.recipe.scripts or
+other isolation-aware recipes that use the sitepackage_safe_scripts function.
+
+Some people use virtualenv to provide similar functionality.
+Unfortunately, a problem with the virtualenv executable as of this
+writing means that -S will not work properly with it (see
+https://bugs.launchpad.net/virtualenv/+bug/572545). This breaks
+buildout's approach to providing isolation.
+
+Because of this, if buildout detects an executable with a broken -S
+option, it will revert to its pre-1.5.0 behavior.  If buildout has been
+asked to provide isolation, it will warn the user that isolation will
+not be provided by buildout, but proceed.  This should give full
+backwards compatibility to virtualenv users.
+
+The only minor annoyance in the future may be recipes that explicitly
+use the new buildout functionality to provide isolation: as described
+above, the builds will proceed, but users will receive warnings that
+buildout is not providing isolation itself.  The warnings themselves can
+be squelched when running bin/buildout with the ``-s`` option or with a
+lower verbosity than usual (e.g., one or more ``-q`` options).
+
+For tests, then, we can examine several things.  We'll focus on four.
+
+- Running bootstrap with an executable broken in this way will not try to do
+  any -S tricks.
+
+- Running sitepackage_safe_scripts with a virtualenv will create an
+  old-style script.  This will affect the bin/buildout script that is
+  created, for instance.  If the sitepackage_safe_scripts function is asked
+  to provide isolation under these circumstances, it will warn that isolation
+  will not be available, but still create the desired script.
+
+- Using the easy_install Installer or install or build functions and trying
+  to request isolation will generate a warning and then the isolation request
+  will be ignored as it proceeds.
+
+- Passing -s (or -q) to the bin/buildout script will squelch warnings.
+
+Testing these involves first creating a Python that exhibits the same
+behavior as the problematic one we care about from virtualenv.  Let's do that
+first.
+
+    >>> import os, sys
+    >>> from zc.buildout.easy_install import _safe_arg
+    >>> py_path, site_packages_path = make_py()
+    >>> if sys.platform == 'win32':
+    ...     py_script_path = py_path + '-script.py'
+    ... else:
+    ...     py_script_path = py_path
+    ...
+    >>> py_file = open(py_script_path)
+    >>> py_lines = py_file.readlines()
+    >>> py_file.close()
+    >>> py_file = open(py_script_path, 'w')
+    >>> extra = '''\
+    ... new_argv = argv[:1]
+    ... for ix, val in enumerate(argv[1:]):
+    ...     if val.startswith('--'):
+    ...         new_argv.append(val)
+    ...     if val.startswith('-') and len(val) > 1:
+    ...         if 'S' in val:
+    ...             val = val.replace('S', '')
+    ...             environ['BROKEN_DASH_S'] = 'Y'
+    ...         if val != '-':
+    ...             new_argv.append(val)
+    ...         if 'c' in val:
+    ...             new_argv.extend(argv[ix+2:])
+    ...             break
+    ...     else:
+    ...         new_argv.extend(argv[ix+1:])
+    ... argv = new_argv
+    ... '''
+    >>> for line in py_lines:
+    ...     py_file.write(line)
+    ...     if line.startswith('environ = os.environ.copy()'):
+    ...         py_file.write(extra)
+    ...         print 'Rewritten.'
+    ...
+    Rewritten.
+    >>> py_file.close()
+    >>> sitecustomize_path = join(os.path.dirname(site_packages_path),
+    ...                           'parts', 'py', 'sitecustomize.py')
+    >>> sitecustomize_file = open(sitecustomize_path, 'a')
+    >>> sitecustomize_file.write('''
+    ... import os, sys
+    ... sys.executable = %r
+    ... if 'BROKEN_DASH_S' in os.environ:
+    ...     class ImportHook:
+    ...         site = None
+    ...
+    ...         @classmethod
+    ...         def find_module(klass, fullname, path=None):
+    ...             if klass.site is None and 'site' in sys.modules:
+    ...                 # Pop site out of sys.modules. This will be a
+    ...                 # close-enough approximation of site not being
+    ...                 # loaded for our tests--it lets us provoke the
+    ...                 # right errors when the fixes are absent, and
+    ...                 # works well enough when the fixes are present.
+    ...                 klass.site = sys.modules.pop('site')
+    ...             if fullname == 'ConfigParser':
+    ...                 raise ImportError(fullname)
+    ...             elif fullname == 'site':
+    ...                 # Keep the site module from being processed twice.
+    ...                 return klass
+    ...
+    ...         @classmethod
+    ...         def load_module(klass, fullname):
+    ...             if fullname == 'site':
+    ...                 return klass.site
+    ...             raise ImportError(fullname)
+    ...
+    ...     sys.meta_path.append(ImportHook)
+    ... ''' % (py_path,))
+    >>> sitecustomize_file.close()
+    >>> print call_py(
+    ...     _safe_arg(py_path),
+    ...     "import ConfigParser")
+    <BLANKLINE>
+    >>> print 'X'; print call_py(
+    ...     _safe_arg(py_path),
+    ...     "import ConfigParser",
+    ...     '-S') # doctest: +ELLIPSIS
+    X...Traceback (most recent call last):
+      ...
+    ImportError: No module named ConfigParser
+    <BLANKLINE>
+    >>> from zc.buildout.easy_install import _has_broken_dash_S
+    >>> _has_broken_dash_S(py_path)
+    True
+
+Well, that was ugly, but it seems to have done the trick.  The
+executable represented by py_path has the same problematic
+characteristic as the virtualenv one: -S results in a Python that does
+not allow the import of some packages from the standard library.  We'll
+test with this.
+
+First, let's try running bootstrap.
+
+    >>> from os.path import dirname, join
+    >>> import zc.buildout
+    >>> bootstrap_py = join(
+    ...    dirname(
+    ...     dirname(
+    ...      dirname(
+    ...       dirname(zc.buildout.__file__)
+    ...        )
+    ...      )
+    ...    ),
+    ...   'bootstrap', 'bootstrap.py')
+    >>> broken_S_buildout = tmpdir('broken_S')
+    >>> os.chdir(broken_S_buildout)
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts =
+    ... ''')
+    >>> write('bootstrap.py', open(bootstrap_py).read())
+    >>> print 'X'; print system(
+    ...     _safe_arg(py_path)+' '+
+    ...     'bootstrap.py'); print 'X' # doctest: +ELLIPSIS
+    X...
+    Generated script '/broken_S/bin/buildout'.
+    ...
+
+If bootstrap didn't look out for a broken -S, that would have failed.  Moreover,
+take a look at bin/buildout:
+
+    >>> cat('bin', 'buildout') # doctest: +NORMALIZE_WHITESPACE
+    #!/executable_buildout/bin/py
+    <BLANKLINE>
+    import sys
+    sys.path[0:0] = [
+      '/broken_S/eggs/setuptools-0.0-pyN.N.egg',
+      '/broken_S/eggs/zc.buildout-0.0-pyN.N.egg',
+      ]
+    <BLANKLINE>
+    import zc.buildout.buildout
+    <BLANKLINE>
+    if __name__ == '__main__':
+        zc.buildout.buildout.main()
+
+That's the old-style buildout script: no changes for users with this issue.
+
+Of course, they don't get the new features either, presumably because
+they don't need or want them.  This means that if they use a recipe that
+tries to use a new feature, the behavior needs to degrade gracefully.
+
+Here's an example.  We'll switch to another buildout in which it is easier to
+use local dev versions of zc.buildout and z3c.recipe.scripts.
+
+    >>> os.chdir(dirname(dirname(buildout)))
+    >>> write('buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = eggs
+    ... find-links = %(link_server)s
+    ... include-site-packages = false
+    ...
+    ... [primed_python]
+    ... executable = %(py_path)s
+    ...
+    ... [eggs]
+    ... recipe = z3c.recipe.scripts
+    ... python = primed_python
+    ... interpreter = py
+    ... eggs = demo
+    ... ''' % globals())
+
+    >>> print system(buildout), # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    Installing eggs.
+    Getting distribution for 'demo'.
+    Got demo 0.4c1.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated script '/sample-buildout/bin/demo'.
+    Generated interpreter '/sample-buildout/bin/py'.
+    ...UserWarning: Buildout has been asked to exclude or limit site-packages
+       so that builds can be repeatable when using a system Python.  However,
+       the chosen Python executable has a broken implementation of -S (see
+       https://bugs.launchpad.net/virtualenv/+bug/572545 for an example
+       problem) and this breaks buildout's ability to isolate site-packages.
+       If the executable already has a clean site-packages (e.g., using
+       virtualenv's ``--no-site-packages`` option) you may be getting
+       equivalent repeatability.  To silence this warning, use the -s argument
+       to the buildout script.  Alternatively, use a Python executable with a
+       working -S (such as a standard Python binary).
+      warnings.warn(BROKEN_DASH_S_WARNING)
+
+So, it did what we asked as best it could, but gave a big warning.  If
+you don't want those warnings for those particular recipes that use the
+new features, you can use the "-s" option to squelch the warnings.
+
+    >>> print system(buildout + ' -s'),
+    Updating eggs.
+
+A lower verbosity (one or more -q options) also quiets the warning.
+
+    >>> print system(buildout + ' -q'),
+
+Notice that, as we saw before with bin/buildout, the generated scripts
+are old-style, because the new-style feature gracefully degrades to the
+previous implementation when it encounters an executable with a broken
+dash-S.
+
+    >>> print 'X'; cat('bin', 'py') # doctest: +ELLIPSIS
+    X...
+    <BLANKLINE>
+    import sys
+    <BLANKLINE>
+    sys.path[0:0] = [
+        '/sample-buildout/eggs/demo-0.4c1-pyN.N.egg',
+        '/sample-buildout/eggs/demoneeded-1.2c1-pyN.N.egg',
+        ]
+    ...
+

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/CHANGES.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/CHANGES.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/CHANGES.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,34 @@
 Change History
 **************
 
+1.3.3 (unreleased)
+==================
+
+(No changes yet.)
+
+1.3.2 (2010-08-23)
+==================
+
+- Bugfix for the change introduced in 1.3.1.
+
+1.3.1 (2010-08-23)
+==================
+
+- Support recipes that are using zc.recipe.egg by passing in a dict, rather
+  than a zc.buildout.buildout.Options object as was expected/tested.
+
+1.3.0 (2010-08-23)
+==================
+
+- Small further refactorings past 1.2.3b1 to be compatible with zc.buildout
+  1.5.0.
+
+1.2.3b1 (2010-04-29)
+====================
+
+- Refactored to be used with z3c.recipe.scripts and zc.buildout 1.5.0.  No
+  new user-visible features.
+
 1.2.2 (2009-03-18)
 ==================
 
@@ -76,7 +104,7 @@
 
   - Cause develop eggs to be created after other parts.
 
-- The develop and build recipes now return the paths created, so that 
+- The develop and build recipes now return the paths created, so that
   created eggs or egg links are removed when a part is removed (or
   changed).
 

Copied: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/MANIFEST.in (from rev 124424, zc.buildout/trunk/zc.recipe.egg_/MANIFEST.in)
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/MANIFEST.in	                        (rev 0)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/MANIFEST.in	2012-03-06 22:53:00 UTC (rev 124531)
@@ -0,0 +1,3 @@
+include *.txt
+recursive-include src *.txt
+exclude MANIFEST.in buildout.cfg .bzrignore

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/setup.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/setup.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/setup.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2007 Zope Corporation and Contributors.
+# Copyright (c) 2007 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -12,11 +12,9 @@
 #
 ##############################################################################
 """Setup for zc.recipe.egg package
-
-$Id$
 """
 
-version = '0'
+version = '1.3.3dev'
 
 import os
 from setuptools import setup, find_packages
@@ -66,7 +64,7 @@
     package_dir = {'':'src'},
     namespace_packages = ['zc', 'zc.recipe'],
     install_requires = [
-        'zc.buildout >=1.2.0',
+        'zc.buildout >=1.5.0',
         'setuptools'],
     tests_require = ['zope.testing'],
     test_suite = name+'.tests.test_suite',

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/README.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/README.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/README.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -46,7 +46,7 @@
     <a href="other-1.0-py2.3.egg">other-1.0-py2.3.egg</a><br>
     </body></html>
 
-We have a sample buildout.  Let's update it's configuration file to
+We have a sample buildout.  Let's update its configuration file to
 install the demo package.
 
     >>> write(sample_buildout, 'buildout.cfg',
@@ -154,6 +154,8 @@
 interpreter
    The name of a script to generate that allows access to a Python
    interpreter that has the path set based on the eggs installed.
+   (See the ``z3c.recipe.scripts`` recipe for a more full-featured
+   interpreter.)
 
 extra-paths
    Extra paths to include in a generated script.
@@ -187,7 +189,7 @@
     ... interpreter = py-demo
     ... """ % dict(server=link_server))
 
-Note that we ommitted the entry point name from the recipe
+Note that we omitted the entry point name from the recipe
 specification. We were able to do this because the scripts recipe is
 the default entry point for the zc.recipe.egg egg.
 
@@ -386,7 +388,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 Relative egg paths
 ------------------
@@ -439,7 +441,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 You can specify relative paths in the buildout section, rather than in
 each individual script section:
@@ -486,7 +488,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
 Specifying initialialization code and arguments
 -----------------------------------------------
@@ -536,7 +538,7 @@
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main(a, 2)
+        sys.exit(eggrecipedemo.main(a, 2))
 
 Here we see that the initialization code we specified was added after
 setting the path.  Note, as mentioned above, that leading whitespace
@@ -577,7 +579,7 @@
     -  demo
     -  other
 
-    >>> cat(sample_buildout, 'bin', 'other')
+    >>> cat(sample_buildout, 'bin', 'other') # doctest: +NORMALIZE_WHITESPACE
     #!/usr/local/bin/python2.4
     <BLANKLINE>
     import sys
@@ -591,7 +593,7 @@
     import foo.bar
     <BLANKLINE>
     if __name__ == '__main__':
-        foo.bar.a.b.c()
+        sys.exit(foo.bar.a.b.c())
 
 Generating all scripts
 ----------------------
@@ -640,3 +642,4 @@
     Uninstalling bigdemo.
     Installing demo.
     Generated script '/sample-buildout/bin/foo'.
+

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/api.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -15,7 +15,7 @@
 around the egg recipe:
 
     >>> mkdir(sample_buildout, 'sample')
-    >>> write(sample_buildout, 'sample', 'sample.py', 
+    >>> write(sample_buildout, 'sample', 'sample.py',
     ... """
     ... import logging, os
     ... import zc.recipe.egg
@@ -53,7 +53,7 @@
     >>> write(sample_buildout, 'sample', 'setup.py',
     ... """
     ... from setuptools import setup
-    ... 
+    ...
     ... setup(
     ...     name = "sample",
     ...     entry_points = {'zc.buildout': ['default = sample:Sample']},
@@ -95,12 +95,13 @@
 computed by the egg recipe by looking at .installed.cfg:
 
     >>> cat(sample_buildout, '.installed.cfg')
+    ... # doctest: +NORMALIZE_WHITESPACE
     [buildout]
     installed_develop_eggs = /sample-buildout/develop-eggs/sample.egg-link
     parts = sample-part
     <BLANKLINE>
     [sample-part]
-    __buildout_installed__ = 
+    __buildout_installed__ =
     __buildout_signature__ = sample-6aWMvV2EJ9Ijq+bR8ugArQ==
             zc.recipe.egg-cAsnudgkduAa/Fd+WJIM6Q==
             setuptools-0.6-py2.4.egg
@@ -116,11 +117,11 @@
     extras = other
     find-links = http://localhost:27071/
     index = http://localhost:27071/index
+    python = buildout
     recipe = sample
 
 If we use the extra-paths option:
 
-
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
     ... [buildout]
@@ -149,4 +150,3 @@
     other 1.0
     demoneeded 1.2c1
     extra paths: ['/foo/bar', '/spam/eggs']
-

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/custom.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -50,7 +50,7 @@
 swig
    The path to the swig executable
 
-swig-cpp           
+swig-cpp
    Make SWIG create C++ files (default is C)
 
 swig-opts
@@ -73,14 +73,14 @@
    alternate index with this option.  If you use the links option and
    if the links point to the needed distributions, then the index can
    be anything and will be largely ignored.  In the examples, here,
-   we'll just point to an empty directory on our link server.  This 
+   we'll just point to an empty directory on our link server.  This
    will make our examples run a little bit faster.
 
 python
    The name of a section to get the Python executable from.
    If not specified, then the buildout python option is used.  The
    Python executable is found in the executable option of the named
-   section. 
+   section.
 
 environment
    The name of a section with additional environment variables. The
@@ -150,6 +150,7 @@
 
     >>> ls(sample_buildout, 'develop-eggs')
     d  extdemo-1.4-py2.4-unix-i686.egg
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Note that no scripts or dependencies are installed.  To install
@@ -188,7 +189,7 @@
     ...
     ... [demo]
     ... recipe = zc.recipe.egg
-    ... eggs = demo 
+    ... eggs = demo
     ...        extdemo
     ... entry-points = demo=demo:main
     ... """ % dict(server=link_server))
@@ -231,6 +232,7 @@
     >>> ls(sample_buildout, 'develop-eggs')
     -  demo.egg-link
     d  extdemo-1.4-py2.4-unix-i686.egg
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 But if we run the buildout in the default on-line and newest modes, we
@@ -248,6 +250,7 @@
     -  demo.egg-link
     d  extdemo-1.4-py2.4-linux-i686.egg
     d  extdemo-1.5-py2.4-linux-i686.egg
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Controlling the version used
@@ -270,7 +273,7 @@
     ...
     ... [demo]
     ... recipe = zc.recipe.egg
-    ... eggs = demo 
+    ... eggs = demo
     ...        extdemo ==1.4
     ... entry-points = demo=demo:main
     ... """ % dict(server=link_server))
@@ -287,6 +290,7 @@
     >>> ls(sample_buildout, 'develop-eggs')
     -  demo.egg-link
     d  extdemo-1.4-py2.4-linux-i686.egg
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 
@@ -440,7 +444,7 @@
     Uninstalling extdemo.
     Installing extdemo.
     zip_safe flag not set; analyzing archive contents...
-    
+
     >>> rmdir(sample_buildout, 'recipes')
 
 
@@ -496,7 +500,7 @@
 swig
    The path to the swig executable
 
-swig-cpp           
+swig-cpp
    Make SWIG create C++ files (default is C)
 
 swig-opts
@@ -506,7 +510,7 @@
    The name of a section to get the Python executable from.
    If not specified, then the buildout python option is used.  The
    Python executable is found in the executable option of the named
-   section. 
+   section.
 
 To illustrate this, we'll use a directory containing the extdemo
 example from the earlier section:
@@ -532,7 +536,7 @@
     ...
     ... [demo]
     ... recipe = zc.recipe.egg
-    ... eggs = demo 
+    ... eggs = demo
     ...        extdemo
     ... entry-points = demo=demo:main
     ... """ % dict(extdemo=extdemo))
@@ -553,6 +557,7 @@
     >>> ls('develop-eggs')
     -  demo.egg-link
     -  extdemo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 and the extdemo now has a built extension:

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/egg.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/egg.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/egg.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -16,17 +16,26 @@
 $Id$
 """
 
-import logging, os, re, zipfile
+import UserDict, logging, os, re, zipfile
+import zc.buildout
 import zc.buildout.easy_install
 
+
 class Eggs(object):
 
+    include_site_packages = allowed_eggs = None
+
     def __init__(self, buildout, name, options):
         self.buildout = buildout
-        self.name = name
+        self.name = self.default_eggs = name
+        if getattr(options, 'query_bool', None) is None:
+            # Someone is not passing us a zc.buildout.buildout.Options
+            # object.  Maybe we should have a deprecation warning.
+            # Whatever.
+            options = _BackwardsSupportOption(options)
         self.options = options
-        links = options.get('find-links',
-                            buildout['buildout'].get('find-links'))
+        b_options = buildout['buildout']
+        links = options.get('find-links', b_options['find-links'])
         if links:
             links = links.split()
             options['find-links'] = '\n'.join(links)
@@ -34,25 +43,22 @@
             links = ()
         self.links = links
 
-        index = options.get('index', buildout['buildout'].get('index'))
+        index = options.get('index', b_options.get('index'))
         if index is not None:
             options['index'] = index
         self.index = index
 
-        allow_hosts = buildout['buildout'].get('allow-hosts', '*')
+        allow_hosts = b_options['allow-hosts']
         allow_hosts = tuple([host.strip() for host in allow_hosts.split('\n')
                                if host.strip()!=''])
         self.allow_hosts = allow_hosts
 
-        options['eggs-directory'] = buildout['buildout']['eggs-directory']
+        options['eggs-directory'] = b_options['eggs-directory']
         options['_e'] = options['eggs-directory'] # backward compat.
-        options['develop-eggs-directory'
-                ] = buildout['buildout']['develop-eggs-directory']
+        options['develop-eggs-directory'] = b_options['develop-eggs-directory']
         options['_d'] = options['develop-eggs-directory'] # backward compat.
 
-        assert options.get('unzip') in ('true', 'false', None)
-
-        python = options.get('python', buildout['buildout']['python'])
+        python = options.setdefault('python', b_options['python'])
         options['executable'] = buildout[python]['executable']
 
     def working_set(self, extra=()):
@@ -61,31 +67,36 @@
         This is intended for reuse by similar recipes.
         """
         options = self.options
+        b_options = self.buildout['buildout']
 
         distributions = [
             r.strip()
-            for r in options.get('eggs', self.name).split('\n')
+            for r in options.get('eggs', self.default_eggs).split('\n')
             if r.strip()]
         orig_distributions = distributions[:]
         distributions.extend(extra)
 
-        if self.buildout['buildout'].get('offline') == 'true':
+        if b_options.get('offline') == 'true':
             ws = zc.buildout.easy_install.working_set(
                 distributions, options['executable'],
-                [options['develop-eggs-directory'], options['eggs-directory']]
+                [options['develop-eggs-directory'],
+                 options['eggs-directory']],
+                include_site_packages=self.include_site_packages,
+                allowed_eggs_from_site_packages=self.allowed_eggs,
                 )
         else:
             kw = {}
-            if options.get('unzip'):
-                kw['always_unzip'] = get_bool(options, 'unzip')
-
+            if 'unzip' in options:
+                kw['always_unzip'] = options.query_bool('unzip', None)
             ws = zc.buildout.easy_install.install(
                 distributions, options['eggs-directory'],
                 links=self.links,
                 index=self.index,
                 executable=options['executable'],
                 path=[options['develop-eggs-directory']],
-                newest=self.buildout['buildout'].get('newest') == 'true',
+                newest=b_options.get('newest') == 'true',
+                include_site_packages=self.include_site_packages,
+                allowed_eggs_from_site_packages=self.allowed_eggs,
                 allow_hosts=self.allow_hosts,
                 **kw)
 
@@ -97,16 +108,19 @@
 
     update = install
 
-class Scripts(Eggs):
 
+class ScriptBase(Eggs):
+
     def __init__(self, buildout, name, options):
-        super(Scripts, self).__init__(buildout, name, options)
+        super(ScriptBase, self).__init__(buildout, name, options)
 
-        options['bin-directory'] = buildout['buildout']['bin-directory']
+        b_options = buildout['buildout']
+
+        options['bin-directory'] = b_options['bin-directory']
         options['_b'] = options['bin-directory'] # backward compat.
 
         self.extra_paths = [
-            os.path.join(buildout['buildout']['directory'], p.strip())
+            os.path.join(b_options['directory'], p.strip())
             for p in options.get('extra-paths', '').split('\n')
             if p.strip()
             ]
@@ -115,11 +129,9 @@
 
 
         relative_paths = options.get(
-            'relative-paths',
-            buildout['buildout'].get('relative-paths', 'false')
-            )
+            'relative-paths', b_options.get('relative-paths', 'false'))
         if relative_paths == 'true':
-            options['buildout-directory'] = buildout['buildout']['directory']
+            options['buildout-directory'] = b_options['directory']
             self._relative_paths = options['buildout-directory']
         else:
             self._relative_paths = ''
@@ -128,12 +140,13 @@
     parse_entry_point = re.compile(
         '([^=]+)=(\w+(?:[.]\w+)*):(\w+(?:[.]\w+)*)$'
         ).match
+
     def install(self):
         reqs, ws = self.working_set()
         options = self.options
 
         scripts = options.get('scripts')
-        if scripts or scripts is None:
+        if scripts or scripts is None or options.get('interpreter'):
             if scripts is not None:
                 scripts = scripts.split()
                 scripts = dict([
@@ -149,7 +162,7 @@
                     raise zc.buildout.UserError("Invalid entry point")
                 reqs.append(parsed.groups())
 
-            if get_bool(options, 'dependent-scripts'):
+            if options.query_bool('dependent-scripts', 'false'):
                 # Generate scripts for all packages in the working set,
                 # except setuptools.
                 reqs = list(reqs)
@@ -157,32 +170,61 @@
                     name = dist.project_name
                     if name != 'setuptools' and name not in reqs:
                         reqs.append(name)
-
-            return zc.buildout.easy_install.scripts(
-                reqs, ws, options['executable'],
-                options['bin-directory'],
-                scripts=scripts,
-                extra_paths=self.extra_paths,
-                interpreter=options.get('interpreter'),
-                initialization=options.get('initialization', ''),
-                arguments=options.get('arguments', ''),
-                relative_paths=self._relative_paths,
-                )
-
+            return self._install(reqs, ws, scripts)
         return ()
 
     update = install
 
-def get_bool(options, name, default=False):
-    value = options.get(name)
-    if not value:
-        return default
-    if value == 'true':
-        return True
-    elif value == 'false':
-        return False
-    else:
-        raise zc.buildout.UserError(
-            "Invalid value for %s option: %s" % (name, value))
+    def _install(self, reqs, ws, scripts):
+        # Subclasses implement this.
+        raise NotImplementedError()
 
+
+class Scripts(ScriptBase):
+
+    def _install(self, reqs, ws, scripts):
+        options = self.options
+        return zc.buildout.easy_install.scripts(
+            reqs, ws, options['executable'],
+            options['bin-directory'],
+            scripts=scripts,
+            extra_paths=self.extra_paths,
+            interpreter=options.get('interpreter'),
+            initialization=options.get('initialization', ''),
+            arguments=options.get('arguments', ''),
+            relative_paths=self._relative_paths
+            )
+
 Egg = Scripts
+
+
+class _BackwardsSupportOption(UserDict.UserDict):
+
+    def __init__(self, data):
+        self.data = data # We want to show mutations to the underlying dict.
+
+    def query_bool(self, name, default=None):
+        """Given a name, return a boolean value for that name.
+
+        ``default``, if given, should be 'true', 'false', or None.
+        """
+        if default is not None:
+            value = self.setdefault(name, default)
+        else:
+            value = self.get(name)
+            if value is None:
+                return value
+        return _convert_bool(name, value)
+
+    def get_bool(self, name):
+        """Given a name, return a boolean value for that name.
+        """
+        return _convert_bool(name, self[name])
+
+
+def _convert_bool(name, value):
+    if value not in ('true', 'false'):
+        raise zc.buildout.UserError(
+            'Invalid value for %s option: %s' % (name, value))
+    else:
+        return value == 'true'

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,7 +1,7 @@
 Controlling which Python to use
 -------------------------------
 
-The following assumes that you have Python 2.4 installed.
+The following assumes that you have Python 2.5 installed.
 
 We can specify the python to use by specifying the name of a section
 to read the Python executable from.  The default is the section
@@ -11,21 +11,21 @@
 
     >>> print get(link_server),
     <html><body>
-    <a href="bigdemo-0.1-py2.4.egg">bigdemo-0.1-py2.4.egg</a><br>
-    <a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
-    <a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
-    <a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
-    <a href="demo-0.4c1-py2.4.egg">demo-0.4c1-py2.4.egg</a><br>
+    <a href="bigdemo-0.1-py2.5.egg">bigdemo-0.1-py2.5.egg</a><br>
+    <a href="demo-0.1-py2.5.egg">demo-0.1-py2.5.egg</a><br>
+    <a href="demo-0.2-py2.5.egg">demo-0.2-py2.5.egg</a><br>
+    <a href="demo-0.3-py2.5.egg">demo-0.3-py2.5.egg</a><br>
+    <a href="demo-0.4c1-py2.5.egg">demo-0.4c1-py2.5.egg</a><br>
     <a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
     <a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
     <a href="demoneeded-1.2c1.zip">demoneeded-1.2c1.zip</a><br>
     <a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
     <a href="index/">index/</a><br>
-    <a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
+    <a href="other-1.0-py2.5.egg">other-1.0-py2.5.egg</a><br>
     </body></html>
 
-We have a sample buildout.  Let's update it's configuration file to
-install the demo package using Python 2.4. 
+We have a sample buildout.  Let's update its configuration file to
+install the demo package using Python 2.5.
 
     >>> write(sample_buildout, 'buildout.cfg',
     ... """
@@ -34,16 +34,16 @@
     ... eggs-directory = eggs
     ... index = http://www.python.org/pypi/
     ...
-    ... [python2.4]
-    ... executable = %(python23)s
+    ... [python2.5]
+    ... executable = %(python24)s
     ...
     ... [demo]
     ... recipe = zc.recipe.egg
     ... eggs = demo <0.3
     ... find-links = %(server)s
-    ... python = python2.4
+    ... python = python2.5
     ... interpreter = py-demo
-    ... """ % dict(server=link_server, python23=other_executable))
+    ... """ % dict(server=link_server, python24=other_executable))
 
 Now, if we run the buildout:
 
@@ -55,23 +55,20 @@
    Getting distribution for 'demo<0.3'.
    Got demo 0.2.
    Getting distribution for 'demoneeded'.
-   Getting distribution for 'setuptools'.
-   Got setuptools 0.6.
    Got demoneeded 1.2c1.
    Generated script '/sample-buildout/bin/demo'.
    Generated interpreter '/sample-buildout/bin/py-demo'.
 
-we'll get the Python 2.4 eggs for demo and demoneeded:
+we'll get the Python 2.5 eggs for demo and demoneeded:
 
     >>> ls(sample_buildout, 'eggs')
-    -  demo-0.2-py2.4.egg
-    -  demoneeded-1.2c1-py2.4.egg
-    d  setuptools-0.6-py2.4.egg
+    -  demo-0.2-py2.5.egg
+    -  demoneeded-1.2c1-py2.5.egg
     d  setuptools-0.6-py2.5.egg
     -  zc.buildout-1.0-py2.5.egg
- 
-And the generated scripts invoke Python 2.4:
 
+And the generated scripts invoke Python 2.5:
+
     >>> import sys
     >>> if sys.platform == 'win32':
     ...    script_name = 'demo-script.py'
@@ -87,14 +84,14 @@
     <BLANKLINE>
     import sys
     sys.path[0:0] = [
-      '/sample-buildout/eggs/demo-0.2-py2.4.egg',
-      '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
+      '/sample-buildout/eggs/demo-0.2-py2.5.egg',
+      '/sample-buildout/eggs/demoneeded-1.2c1-py2.5.egg',
       ]
     <BLANKLINE>
     import eggrecipedemo
     <BLANKLINE>
     if __name__ == '__main__':
-        eggrecipedemo.main()
+        sys.exit(eggrecipedemo.main())
 
     >>> if sys.platform == 'win32':
     ...     f = open(os.path.join(sample_buildout, 'bin', 'py-demo-script.py'))
@@ -111,8 +108,8 @@
     import sys
     <BLANKLINE>
     sys.path[0:0] = [
-      '/sample-buildout/eggs/demo-0.2-py2.4.egg',
-      '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
+      '/sample-buildout/eggs/demo-0.2-py2.5.egg',
+      '/sample-buildout/eggs/demoneeded-1.2c1-py2.5.egg',
       ]
     <BLANKLINE>
     _interactive = True

Modified: zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/tests.py
===================================================================
--- zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2012-03-06 20:43:27 UTC (rev 124530)
+++ zc.buildout/branches/encolpe-escape-command/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2012-03-06 22:53:00 UTC (rev 124531)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) 2006 Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -17,8 +17,8 @@
 import zc.buildout.testselectingpython
 import zc.buildout.testing
 
-import unittest
-from zope.testing import doctest, renormalizing
+import unittest, doctest
+from zope.testing import renormalizing
 
 os_path_sep = os.path.sep
 if os_path_sep == '\\':
@@ -29,6 +29,39 @@
         return d
     return dirname(os.path.dirname(d), level-1)
 
+def testUsingDictAsOptions():
+    """
+Some recipes using zc.recipe.egg have been passing dictionaries rather than
+zc.buildout.buildout.Options objects.  That's unexpected, but to save
+complaints, we'll support it.
+
+Note that this test intends to show that a dictionary can be used as an
+options object.  It also uses a dictionary for the buildout object, which is
+not intended.
+
+    >>> import zc.buildout.buildout
+    >>> import zc.recipe.egg
+    >>> faux_egg_options = {'find-links': 'example.com'}
+    >>> faux_buildout_options = zc.buildout.buildout._unannotate_section(
+    ...     zc.buildout.buildout._buildout_default_options.copy())
+    >>> faux_buildout_options['bin-directory'] = '/somewhere/over/rainbow'
+    >>> faux_buildout = {
+    ...     'faux': faux_egg_options, 'buildout': faux_buildout_options}
+    >>> scripts = zc.recipe.egg.Scripts(
+    ...     faux_buildout, 'faux', faux_egg_options)
+    >>> scripts.links
+    ['example.com']
+    >>> import zc.buildout.easy_install
+    >>> old_install = zc.buildout.easy_install.install
+    >>> old_scripts = zc.buildout.easy_install.scripts
+    >>> def whatever(*args, **kwargs): pass
+    >>> zc.buildout.easy_install.install = whatever
+    >>> zc.buildout.easy_install.scripts = whatever
+    >>> scripts.install() # This used to fail!
+    >>> zc.buildout.easy_install.install = old_install
+    >>> zc.buildout.easy_install.scripts = old_scripts
+"""
+
 def setUp(test):
     zc.buildout.tests.easy_install_SetUp(test)
     zc.buildout.testing.install_develop('zc.recipe.egg', test)
@@ -36,88 +69,102 @@
 def setUpSelecting(test):
     zc.buildout.testselectingpython.setup(test)
     zc.buildout.testing.install_develop('zc.recipe.egg', test)
-    
+
 def test_suite():
     suite = unittest.TestSuite((
         doctest.DocFileSuite(
             'README.txt',
             setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               zc.buildout.testing.normalize_script,
-               zc.buildout.testing.normalize_egg_py,
-               zc.buildout.tests.normalize_bang,
-               (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
-                'zc.buildout.egg'),
-               (re.compile('[-d]  setuptools-[^-]+-'), 'setuptools-X-'),
-               (re.compile(r'eggs\\\\demo'), 'eggs/demo'),
-               (re.compile(r'[a-zA-Z]:\\\\foo\\\\bar'), '/foo/bar'),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_script,
+                zc.buildout.testing.normalize_egg_py,
+                zc.buildout.tests.normalize_bang,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+                 'zc.buildout.egg'),
+                (re.compile('[-d]  (setuptools|distribute)-[^-]+-'),
+                 'setuptools-X-'),
+                (re.compile(r'eggs\\\\demo'), 'eggs/demo'),
+                (re.compile(r'[a-zA-Z]:\\\\foo\\\\bar'), '/foo/bar'),
+                # Distribute unzips eggs by default.
+                (re.compile('\-  demoneeded'), 'd  demoneeded'),
+                ])
             ),
         doctest.DocFileSuite(
             'api.txt',
             setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               (re.compile('__buildout_signature__ = '
-                           'sample-\S+\s+'
-                           'zc.recipe.egg-\S+\s+'
-                           'setuptools-\S+\s+'
-                           'zc.buildout-\S+\s*'
-                           ),
-                '__buildout_signature__ = sample- zc.recipe.egg-'),
-               (re.compile('executable = [\S ]+python\S*', re.I),
-                'executable = python'),
-               (re.compile('find-links = http://localhost:\d+/'),
-                'find-links = http://localhost:8080/'),
-               (re.compile('index = http://localhost:\d+/index'),
-                'index = http://localhost:8080/index'),
-               ])
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.tests.hide_distribute_additions,
+                (re.compile('__buildout_signature__ = '
+                            'sample-\S+\s+'
+                            'zc.recipe.egg-\S+\s+'
+                            '(setuptools|distribute)-\S+\s+'
+                            'zc.buildout-\S+\s*'
+                            ),
+                 '__buildout_signature__ = sample- zc.recipe.egg-\n'),
+                (re.compile('executable = [\S ]+python\S*', re.I),
+                 'executable = python'),
+                (re.compile('find-links = http://localhost:\d+/'),
+                 'find-links = http://localhost:8080/'),
+                (re.compile('index = http://localhost:\d+/index'),
+                 'index = http://localhost:8080/index'),
+                ])
             ),
         doctest.DocFileSuite(
             'custom.txt',
             setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
             checker=renormalizing.RENormalizing([
-               zc.buildout.testing.normalize_path,
-               zc.buildout.testing.normalize_endings,
-               (re.compile("(d  ((ext)?demo(needed)?|other)"
-                           "-\d[.]\d-py)\d[.]\d(-\S+)?[.]egg"),
-                '\\1V.V.egg'),
-               (re.compile('extdemo.c\n.+\\extdemo.exp\n'), ''),
-               (re.compile('extdemo[.]pyd'), 'extdemo.so')
-               ]),
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.tests.hide_distribute_additions,
+                zc.buildout.tests.hide_zip_safe_message,
+                (re.compile("(d  ((ext)?demo(needed)?|other)"
+                            "-\d[.]\d-py)\d[.]\d(-\S+)?[.]egg"),
+                 '\\1V.V.egg'),
+                (re.compile('extdemo.c\n.+\\extdemo.exp\n'), ''),
+                (re.compile('extdemo[.]pyd'), 'extdemo.so'),
+                (re.compile("extdemo[.]c\n"
+                            "extdemo[.]obj : warning LNK4197: export 'initextdemo' specified multiple times; using first specification\n"
+                            "   Creating library build\\\\temp[.]win-amd64-2[.][4567]\\\\Release\\\\extdemo[.]lib and object build\\\\temp[.]win-amd64-2[.][4567]\\\\Release\\\\extdemo[.]exp\n"),
+                            ''),
+                ]),
             ),
-        
+        doctest.DocTestSuite(
+            setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
+            ),
         ))
 
-    if sys.version_info[:2] == (2, 5):
-        # Only run selecting python tests if not 2.4, since
-        # 2.4 is the alternate python used in the tests.
+    if zc.buildout.testing.script_in_shebang:
         suite.addTest(
             doctest.DocFileSuite(
                 'selecting-python.txt',
                 setUp=setUpSelecting,
                 tearDown=zc.buildout.testing.buildoutTearDown,
                 checker=renormalizing.RENormalizing([
-                   zc.buildout.testing.normalize_path,
-                   zc.buildout.testing.normalize_endings,
-                   zc.buildout.testing.normalize_script,
-                   (re.compile('Got setuptools \S+'), 'Got setuptools V'),
-                   (re.compile('([d-]  )?setuptools-\S+-py'),
-                    'setuptools-V-py'),
-                   (re.compile('-py2[.][0-35-9][.]'), 'py2.5.'),
-                   (re.compile('zc.buildout-\S+[.]egg'),
-                    'zc.buildout.egg'),
-                   (re.compile('zc.buildout[.]egg-link'),
-                    'zc.buildout.egg'),
-                   ]),
+                    zc.buildout.testing.normalize_path,
+                    zc.buildout.testing.normalize_endings,
+                    zc.buildout.testing.normalize_script,
+                    zc.buildout.tests.hide_distribute_additions,
+                    (re.compile('Got (setuptools|distribute) \S+'),
+                     'Got setuptools V'),
+                    (re.compile('([d-]  )?(setuptools|distribute)-\S+-py'),
+                     'setuptools-V-py'),
+                    (re.compile('-py2[.][0-35-9][.]'), 'py2.5.'),
+                    (re.compile('zc.buildout-\S+[.]egg'),
+                     'zc.buildout.egg'),
+                    (re.compile('zc.buildout[.]egg-link'),
+                     'zc.buildout.egg'),
+                    # Distribute unzips eggs by default.
+                    (re.compile('\-  demoneeded'), 'd  demoneeded'),
+                    ]),
                 ),
             )
-    
+
     return suite
 
 if __name__ == '__main__':
     unittest.main(defaultTest='test_suite')
-



More information about the checkins mailing list