[Checkins] SVN: zc.buildout/branches/gary-4/ Implementation complete to my knowledge. Tests pass locally.

Gary Poster gary.poster at canonical.com
Wed Feb 3 10:41:05 EST 2010


Log message for revision 108736:
  Implementation complete to my knowledge.  Tests pass locally.
  
  Need to check if tests are complete/sufficient, and test with multiple Pythons
  and on Windows.
  
  Changed zc.recipe.egg and the easy_install.scripts function into only adding
  -S to scripts.
  
  New package z3c.recipe.scripts and the easy_install.generate_scripts now
  contains the more robust interpreter generation and the ability to
  include site-packages.
  
  Pertinent tests from the previous approach (from 3+ months ago) are
  ported.  In the course of this port I discovered that big changes like
  the ones in this checkin were needed.
  
  

Changed:
  U   zc.buildout/branches/gary-4/CHANGES.txt
  U   zc.buildout/branches/gary-4/README.txt
  U   zc.buildout/branches/gary-4/buildout.cfg
  U   zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py
  U   zc.buildout/branches/gary-4/src/zc/buildout/easy_install.txt
  U   zc.buildout/branches/gary-4/src/zc/buildout/testing.py
  U   zc.buildout/branches/gary-4/src/zc/buildout/tests.py
  U   zc.buildout/branches/gary-4/src/zc/buildout/testselectingpython.py
  U   zc.buildout/branches/gary-4/src/zc/buildout/update.txt
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/CHANGES.txt
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/README.txt
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/setup.py
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/__init__.py
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/__init__.py
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/README.txt
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/__init__.py
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/scripts.py
  A   zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/tests.py
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/api.txt
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/custom.txt
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py
  U   zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/tests.py

-=-
Modified: zc.buildout/branches/gary-4/CHANGES.txt
===================================================================
--- zc.buildout/branches/gary-4/CHANGES.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/CHANGES.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -1,11 +1,30 @@
 Change History
 **************
 
-1.?.? (200?-??-??)
+1.?.? (201?-??-??)
 ==================
 
 New Features:
 
+- Buildout can be safely used with a system Python.  Note that the Python is
+  always used, by default, with -S: that is, site-packages are not
+  included. If you would like to have access to your site-packages, see
+  the next bullet point.
+
+  A limitation: in no cases are distributions in your site-packages used
+  to satisfy buildout dependencies.  The site-packages can be used in
+  addition to the dependencies specified in your buildout, and buildout
+  dependencies can override code in your site-packages, but even if your
+  Python's site-packages has the same exact version as specified in your
+  buildout configuration, buildout will still use its own copy.
+
+- Added new function 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 (see
+  z3c.recipe.scripts).  In this case a functioning site.py also is available
+  (and namespace packages are handled somewhat more efficiently than standard
+  pkg_resources usage).
+
 - Improve bootstrap.
 
   * New options let you specify where to find ez_setup.py and where to find
@@ -28,6 +47,16 @@
   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) in some cases.
+
+- 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.
+
 1.4.1 (2009-08-27)
 ==================
 

Modified: zc.buildout/branches/gary-4/README.txt
===================================================================
--- zc.buildout/branches/gary-4/README.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/README.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -37,6 +37,11 @@
    dependencies.  It installs their console-script entry points with
    the needed eggs included in their paths.
 
+`z3c.recipe.scripts <http://pypi.python.org/pypi/z3c.recipe.scripts>`_
+  This scripts recipe builds interpreter scripts and entry point scripts
+  based on eggs.  These scripts have more features and flexibility than the
+  ones 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 +131,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.
 

Modified: zc.buildout/branches/gary-4/buildout.cfg
===================================================================
--- zc.buildout/branches/gary-4/buildout.cfg	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/buildout.cfg	2010-02-03 15:41:04 UTC (rev 108736)
@@ -1,5 +1,5 @@
 [buildout]
-develop = zc.recipe.egg_ .
+develop = zc.recipe.egg_ z3c.recipe.scripts_ .
 parts = test oltest py
 
 [py]
@@ -13,6 +13,7 @@
 eggs = 
   zc.buildout
   zc.recipe.egg
+  z3c.recipe.scripts
 
 # Tests that can be run wo a network
 [oltest]
@@ -20,6 +21,7 @@
 eggs = 
   zc.buildout
   zc.recipe.egg
+  z3c.recipe.scripts
 defaults =
   [
   '-t',

Modified: zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/easy_install.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -304,7 +304,7 @@
                 ws, False,
                 )[0].location
 
-            args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(tmp))
+            args = ('-Sc', _easy_install_cmd, '-mUNxd', _safe_arg(tmp))
             if self._always_unzip:
                 args += ('-Z', )
             level = logger.getEffectiveLevel()
@@ -498,7 +498,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
@@ -631,9 +631,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]
@@ -664,7 +664,7 @@
             except pkg_resources.DistributionNotFound, err:
                 [requirement] = err
                 requirement = self._constrain(requirement)
-                if dest:
+                if destination:
                     logger.debug('Getting required %r', str(requirement))
                 else:
                     logger.debug('Adding required %r', str(requirement))
@@ -907,6 +907,9 @@
 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=(),
@@ -914,22 +917,85 @@
             interpreter=None,
             initialization='',
             relative_paths=False,
-            import_site=False,
             ):
+    """Generate scripts and/or an interpreter.
 
+    See generate_scripts for a newer version with 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
+
+def generate_scripts(
+    dest, working_set, executable, site_py_dest,
+    reqs=(), scripts=None, interpreter=None, extra_paths=(),
+    initialization='', add_site_packages=False, exec_sitecustomize=False,
+    relative_paths=False, script_arguments='', script_initialization=''):
+    """Generate scripts and/or an interpreter.
+
+    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.
+    """
+    generated = []
+    generated.append(_generate_sitecustomize(
+        site_py_dest, executable, initialization, exec_sitecustomize))
+    generated.append(_generate_site(
+        site_py_dest, working_set, executable, extra_paths,
+        add_site_packages, relative_paths))
+    script_initialization = (
+        '\nimport site # imports custom buildbot-generated site.py\n%s' % (
+            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))
+    if interpreter:
+        generated.extend(_generate_interpreter(
+            interpreter, dest, executable, site_py_dest, relative_paths))
+    return generated
+
+# Utilities for the script generation functions.
+
+# These are shared by both ``scripts`` and ``generate_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)
+    return map(realpath, path)
 
-    generated = []
+def _generate_scripts(reqs, working_set, dest, path, scripts, relative_paths,
+                      initialization, executable, arguments):
+    """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):
@@ -943,7 +1009,6 @@
                     )
         else:
             entry_points.append(req)
-
     for name, module_name, attrs in entry_points:
         if scripts is not None:
             sname = scripts.get(name)
@@ -951,126 +1016,48 @@
                 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, import_site)
-            )
-
-    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, import_site))
-
+            _script(sname, executable, rpsetup, spath,
+                    initialization, module_name, attrs, arguments))
     return generated
 
-def interpreter(name, working_set, executable, dest, site_py_dest,
-                extra_paths=(),
-                initialization='',
-                relative_paths=False,
-                import_site=False,
-                import_sitecustomize=False
-                ):
-    generated = []
-    # Write sitecustomize.py.
-    sitecustomize_path = os.path.join(site_py_dest, 'sitecustomize.py')
-    sitecustomize = open(sitecustomize_path, 'w')
-    if initialization:
-        sitecustomize.write(initialization + '\n')
-    if import_sitecustomize:
-        real_sitecustomize_path = _get_module_file(
-            executable, 'sitecustomize')
-        if real_sitecustomize_path:
-            sitecustomize.write('execfile(%r)\n' % (real_sitecustomize_path,))
-    sitecustomize.close()
-    generated.append(sitecustomize_path)
-    # Write site.py.
-    path = [dist.location for dist in working_set]
-    path.extend(extra_paths)
-    path = map(realpath, path)
-    site_path = os.path.join(site_py_dest, 'site.py')
-    path_string, rpsetup = _relative_path_and_setup(
-        site_path, path, relative_paths)
-    site = open(site_path, 'w')
-    site.write(rpsetup)
-    site.write(sys_path_template % (path_string,))
-    if import_site:
-        real_site_path = _get_module_file(executable, 'site')
-        if real_site_path:
-            site.write(import_site_template % (real_site_path,))
-    else:
-        site.write('import sitecustomize\n')
-    generated.append(site_path)
-    # Write interpreter script.
-    script_name = full_name = os.path.join(dest, name)
-    site_py_dest_string, rpsetup = _relative_path_and_setup(
-        full_name, [site_py_dest], relative_paths)
-    if is_win32:
-        script_name += '-script.py'
-    contents = interpreter_template % dict(
-        python = _safe_arg(executable),
-        site_dest = site_py_dest_string,
-        relative_paths_setup = rpsetup,
-        )
-    changed = not (os.path.exists(script_name) and
-                   open(script_name).read() == contents)
-    if is_win32:
-        # Generate exe file and give the script a magic name.
-        exe = full_name + '.exe'
-        open(exe, 'wb').write(
-            pkg_resources.resource_string('setuptools', 'cli.exe')
-            )
-        generated.append(exe)
-    if changed:
-        open(script_name, 'w').write(contents)
-        try:
-            os.chmod(script_name,0755)
-        except (AttributeError, os.error):
-            pass
-        logger.info("Generated interpreter %r.", full_name)
-    generated.append(script_name)
-    return generated
+def _relative_path_and_setup(sname, path,
+                             relative_paths=False, indent_level=1):
+    """Return a string of code of paths and of setup if appropriate.
 
-def _get_module_file(executable, name):
-    cmd = [executable, "-c",
-           "import imp; fp, path, desc = imp.find_module(%r); fp.close; print path" % (name,)]
-    _proc = subprocess.Popen(
-        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    stdout, stderr = _proc.communicate();
-    if _proc.returncode:
-        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 _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
         for i in range(_relative_depth(relative_paths, sname)):
             rpsetup += "base = os.path.dirname(base)\n"
     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``.
+
+    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)
@@ -1083,6 +1070,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)
@@ -1096,6 +1088,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]))
@@ -1113,73 +1110,63 @@
 base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
 """
 
-def _script(module_name, attrs, path, dest, executable, arguments,
-            initialization, rsetup, import_site):
+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'
-    if import_site:
-        import_site = import_site_snippet
-    else:
-        import_site = ''
-
-    contents = script_template % dict(
-        python = _safe_arg(executable),
-        path = path,
-        module_name = module_name,
-        attrs = attrs,
-        arguments = arguments,
-        initialization = initialization,
-        relative_paths_setup = rsetup,
-        import_site=import_site,
-        )
-    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):
+    contents = script_template % dict(
+        python = _safe_arg(executable),
+        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 -S'
 else:
     script_header = '#!%(python)s -S'
 
-_import_site_start = '''\
-# We have to import pkg_resources before namespace
-# package .pth files are processed or else the distribution's namespace
-# packages will mask all of the egg-based packages in the same namespace
-# package.
-try:
-  import pkg_resources
-except ImportError:
-  pass
-'''
-
-import_site_snippet = _import_site_start + 'import site\n'
-import_site_template = _import_site_start + 'execfile(%r)\n'
 sys_path_template = '''\
 import sys
 sys.path[0:0] = [
-  %s,
-  ]
+    %s,
+    ]
 '''
 
 script_template = script_header + '''\
@@ -1187,9 +1174,9 @@
 %(relative_paths_setup)s
 import sys
 sys.path[0:0] = [
-  %(path)s,
-  ]
-%(import_site)s
+    %(path)s,
+    ]
+
 %(initialization)s
 import %(module_name)s
 
@@ -1197,67 +1184,25 @@
     %(module_name)s.%(attrs)s(%(arguments)s)
 '''
 
-interpreter_template = script_header + '''\
+# These are used only by the older ``scripts`` function.
 
-%(relative_paths_setup)s
-import os
-import sys
-
-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
-os.execve(sys.executable, argv, environ)
-'''
-
-def _pyscript(path, dest, executable, rsetup, import_site):
-    generated = []
-    script = dest
-    if is_win32:
-        dest += '-script.py'
-    if import_site:
-        import_site = import_site_snippet
-    else:
-        import_site = ''
-
+def _pyscript(path, dest, executable, rsetup):
     contents = py_script_template % dict(
         python = _safe_arg(executable),
         path = path,
         relative_paths_setup = rsetup,
-        import_site=import_site
         )
-    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
 import sys
 
 sys.path[0:0] = [
-  %(path)s,
-  ]
-%(import_site)s
+    %(path)s,
+    ]
+
 _interactive = True
 if len(sys.argv) > 1:
     _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
@@ -1284,6 +1229,267 @@
     __import__("code").interact(banner="", local=globals())
 '''
 
+# These are used only by the newer ``generate_scripts`` function.
+
+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])"])
+        _proc = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=kwargs)
+        stdout, stderr = _proc.communicate();
+        if _proc.returncode:
+            raise RuntimeError(
+                'error trying to get system packages:\n%s' % (stderr,))
+        res = eval(stdout)
+        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_module_file(executable, name):
+    """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, "-c",
+           "import imp; "
+           "fp, path, desc = imp.find_module(%r); "
+           "fp.close; "
+           "print path" % (name,)]
+    _proc = subprocess.Popen(
+        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    stdout, stderr = _proc.communicate();
+    if _proc.returncode:
+        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')
+        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=(),
+                   add_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 add_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')
+    path_string, rpsetup = _relative_path_and_setup(
+        site_path, path, relative_paths, indent_level=2)
+    if rpsetup:
+        rpsetup = '\n'.join(
+            [(line and '    %s' % (line,) or line)
+             for line in rpsetup.split('\n')])
+    real_site_path = _get_module_file(executable, 'site')
+    real_site = open(real_site_path, 'r')
+    site = open(site_path, 'w')
+    extra_path_snippet = add_site_packages_snippet[add_site_packages]
+    extra_path_snippet_followup = add_site_packages_snippet_followup[
+        add_site_packages]
+    if add_site_packages:
+        stdlib, site_paths = _get_system_paths(executable)
+        extra_path_snippet = extra_path_snippet % _format_paths(
+            (repr(p) for p in site_paths), 2)
+    addsitepackages_marker = 'def addsitepackages('
+    enableusersite_marker = 'ENABLE_USER_SITE = '
+    successful_rewrite = False
+    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 % (
+                extra_path_snippet, rpsetup, path_string,
+                extra_path_snippet_followup))
+            site.write(line[len(addsitepackages_marker):])
+            successful_rewrite = True
+        else:
+            site.write(line)
+    if not successful_rewrite:
+        raise RuntimeError('Buildout did not successfully rewrite site.py')
+    return site_path
+
+add_site_packages_snippet = ['''
+    paths = []''', '''
+    paths = [ # These are the underlying Python's site-packages.
+        %s]
+    sys.path[0:0] = paths
+    known_paths.update([os.path.normcase(os.path.abspath(p)) for p in paths])
+    try:
+        import pkg_resources
+    except ImportError:
+        # No namespace packages in sys.path; no fixup needed.
+        pkg_resources = None''']
+
+add_site_packages_snippet_followup = ['', '''
+    if pkg_resources is not None:
+        # There may be namespace packages in sys.path.  This is much faster
+        # than importing pkg_resources after the sys.path has a large number
+        # of eggs.
+        for p in sys.path:
+            pkg_resources.fixup_namespace_packages(p)''']
+
+addsitepackages_script = '''\
+def addsitepackages(known_paths):%s
+%s    paths[0:0] = [ # eggs
+        %s
+        ]
+    # Process all dirs.  Look for .pth files.  If they exist, defer
+    # processing "import" varieties.
+    dotpth = os.extsep + "pth"
+    deferred = []
+    for path in reversed(paths):
+        # Duplicating addsitedir.
+        sitedir, sitedircase = makepath(path)
+        if not sitedircase in known_paths and os.path.exists(sitedir):
+            sys.path.insert(0, sitedir)
+            known_paths.add(sitedircase)
+        try:
+            names = os.listdir(sitedir)
+        except os.error:
+            continue
+        names = [name for name in names if name.endswith(dotpth)]
+        names.sort()
+        for name in names:
+            # Duplicating addpackage.
+            fullname = os.path.join(sitedir, name)
+            try:
+                f = open(fullname, "rU")
+            except IOError:
+                continue
+            try:
+                for line in f:
+                    if line.startswith("#"):
+                        continue
+                    if (line.startswith("import ") or
+                        line.startswith("import\t")):
+                        # This line is supposed to be executed.  It
+                        # might be a setuptools namespace package
+                        # installed with a system package manager.
+                        # Defer this so we can process egg namespace
+                        # packages first, or else the eggs with the same
+                        # namespace will be ignored.
+                        deferred.append((sitedir, name, fullname, line))
+                        continue
+                    line = line.rstrip()
+                    dir, dircase = makepath(sitedir, line)
+                    if not dircase in known_paths and os.path.exists(dir):
+                        sys.path.append(dir)
+                        known_paths.add(dircase)
+            finally:
+                f.close()%s
+    # Process "import ..." .pth lines.
+    for sitedir, name, fullname, line in deferred:
+        # Note that some lines--such as the one setuptools writes for
+        # namespace packages--expect some or all of sitedir, name, and
+        # fullname to be present in the frame locals, as it is in
+        # ``addpackage``.
+        try:
+            exec line
+        except:
+            print "Error in %%s" %% (fullname,)
+            raise
+    global addsitepackages
+    addsitepackages = original_addsitepackages
+    return known_paths
+
+buildout_addsitepackages = addsitepackages
+
+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)
+    contents = interpreter_template % dict(
+        python = _safe_arg(executable),
+        site_dest = site_py_dest_string,
+        relative_paths_setup = rpsetup,
+        )
+    return _write_script(full_name, contents, 'interpreter')
+
+interpreter_template = script_header + '''\
+
+%(relative_paths_setup)s
+import os
+import sys
+
+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
+os.execve(sys.executable, argv, environ)
+'''
+
+# End of script generation code.
+############################################################################
+
 runsetup_template = """
 import sys
 sys.path.insert(0, %(setupdir)r)

Modified: zc.buildout/branches/gary-4/src/zc/buildout/easy_install.txt
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/easy_install.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/easy_install.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -521,25 +521,30 @@
 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.  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 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.)
 
-The scripts method can be used to generate scripts. Let's create a
-destination directory for it to place them in:
+The ``scripts`` function
+~~~~~~~~~~~~~~~~~~~~~~~~
 
-    >>> import tempfile
+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
@@ -601,6 +606,15 @@
 - The module for the script entry point is imported and the entry
   point, in this case, 'main', is run.
 
+- On the shebang (first) line of the script, Python is invoked with -S.
+  This means that site.py is not imported, which in turn means that
+  site-packages are not part of the path.  This is the safest approach,
+  and let's you easily use a system Python to do buildout-based
+  development.  Note that, because of the setuptools-provided .exe files
+  that buildout uses to run scripts, it also works on Windows.  (If you
+  want to use site-packages, see the discussion--and warnings--for the
+  ``generate_scripts`` function, below.)
+
 Rather than requirement strings, you can pass tuples containing 3
 strings:
 
@@ -635,42 +649,6 @@
 distributions) that don't declare their entry points, such as
 distributions that aren't based on setuptools.
 
-As you can see by the shebang (first) line of the script, Python is
-invoked with -S.  This means that site.py is not imported, which in turn
-means that site-packages are not part of the path.  This is the safest
-approach, and let's you easily use a system Python to do buildout-based
-development.  Note that, because of the setuptools-provided .exe files
-that buildout uses to run scripts, it also works on Windows.
-
-However, if desired, you can also specify that site.py should be
-imported, using the ``import_site`` argument.  This is done carefully,
-as you can see in the comment below.
-
-    >>> scripts = zc.buildout.easy_install.scripts(
-    ...     ['demo'], ws, sys.executable, bin, import_site=True)
-    >>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
-    #!/usr/local/bin/python2.4 -S
-    <BLANKLINE>
-    import sys
-    sys.path[0:0] = [
-      '/sample-install/demo-0.3-py2.4.egg',
-      '/sample-install/demoneeded-1.1-py2.4.egg',
-      ]
-    # We have to import pkg_resources before namespace
-    # package .pth files are processed or else the distribution's namespace
-    # packages will mask all of the egg-based packages in the same namespace
-    # package.
-    try:
-      import pkg_resources
-    except ImportError:
-      pass
-    import site
-    <BLANKLINE>
-    import eggrecipedemo
-    <BLANKLINE>
-    if __name__ == '__main__':
-        eggrecipedemo.main()
-
 The interpreter keyword argument can be used to generate a script that can
 be used to invoke the Python interactive interpreter with the path set
 based on the working set.  This generated script can also be used to
@@ -752,56 +730,6 @@
     >>> print system(join(bin, 'py')+' -m pdb what'),
     Error: what does not exist
 
-The interpreter script generation also honors the ``import_site`` argument
-described above.
-
-    >>> scripts = zc.buildout.easy_install.scripts(
-    ...     ['demo'], ws, sys.executable, bin, interpreter='py',
-    ...     import_site=True)
-    >>> cat(bin, 'py') # doctest: +NORMALIZE_WHITESPACE
-    #!/usr/local/bin/python2.4 -S
-    <BLANKLINE>
-    import sys
-    <BLANKLINE>
-    sys.path[0:0] = [
-      '/sample-install/demo-0.3-pyN.N.egg',
-      '/sample-install/demoneeded-1.1-pyN.N.egg',
-      ]
-    # We have to import pkg_resources before namespace
-    # package .pth files are processed or else the distribution's namespace
-    # packages will mask all of the egg-based packages in the same namespace
-    # package.
-    try:
-      import pkg_resources
-    except ImportError:
-      pass
-    import site
-    <BLANKLINE>
-    _interactive = True
-    if len(sys.argv) > 1:
-        _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
-        _interactive = False
-        for (_opt, _val) in _options:
-            if _opt == '-i':
-                _interactive = True
-            elif _opt == '-c':
-                exec _val
-            elif _opt == '-m':
-                sys.argv[1:] = _args
-                _args = []
-                __import__("runpy").run_module(
-                     _val, {}, "__main__", alter_sys=True)
-    <BLANKLINE>
-        if _args:
-            sys.argv[:] = _args
-            __file__ = _args[0]
-            del _options, _args
-            execfile(__file__)
-    <BLANKLINE>
-    if _interactive:
-        del _interactive
-        __import__("code").interact(banner="", local=globals())
-
 An additional argument can be passed to define which scripts to install
 and to provide script names. The argument is a dictionary mapping
 original script names to new script names.
@@ -822,8 +750,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:
@@ -848,8 +776,8 @@
     if __name__ == '__main__':
         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
@@ -872,8 +800,8 @@
     if __name__ == '__main__':
         eggrecipedemo.main(1, 2)
 
-Passing initialization code
----------------------------
+The ``scripts`` function: Passing initialization code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 You can also pass script initialization code:
 
@@ -898,8 +826,8 @@
     if __name__ == '__main__':
         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
@@ -933,11 +861,11 @@
     <BLANKLINE>
     import sys
     sys.path[0:0] = [
-      join(base, 'eggs/demo-0.3-pyN.N.egg'),
-      join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
-      '/ba',
-      join(base, 'bar'),
-      ]
+        join(base, 'eggs/demo-0.3-pyN.N.egg'),
+        join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
+        '/ba',
+        join(base, 'bar'),
+        ]
     <BLANKLINE>
     <BLANKLINE>
     import eggrecipedemo
@@ -967,11 +895,11 @@
     import sys
     <BLANKLINE>
     sys.path[0:0] = [
-      join(base, 'eggs/demo-0.3-pyN.N.egg'),
-      join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
-      '/ba',
-      join(base, 'bar'),
-      ]
+        join(base, 'eggs/demo-0.3-pyN.N.egg'),
+        join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
+        '/ba',
+        join(base, 'bar'),
+        ]
     <BLANKLINE>
     _interactive = True
     if len(sys.argv) > 1:
@@ -998,46 +926,75 @@
         del _interactive
         __import__("code").interact(banner="", local=globals())
 
+The ``generate_scripts`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Interpreter generation
-----------------------
+The newer function for creating scripts is ``generate_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.
 
-The interpreter created above is a script that emulates the Python
-interpreter.  Sometimes it is useful to have a full-fledged Python interpreter
-available.  The ``interpreter`` function supports this.
+- 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 ``generate_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.
+
 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.
+Here's the simplest example, building an interpreter script.
 
     >>> interpreter_dir = tmpdir('interpreter')
-    >>> mkdir(interpreter_dir, 'bin')
+    >>> 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_dir, 'parts', 'interpreter')
+    >>> 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.interpreter(
-    ...     'py', ws, sys.executable, join(interpreter_dir, 'bin'),
-    ...     join(interpreter_dir, 'parts', 'interpreter'))
+    >>> generated = zc.buildout.easy_install.generate_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py')
 
-On non-Windows systems, this creates three files.
+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.
 
-    >>> len(generated)
-    3
-    >>> import pprint
-    >>> pprint.pprint(sorted(generated))
-    ['/interpreter/bin/py',
-     '/interpreter/parts/interpreter/site.py',
-     '/interpreter/parts/interpreter/sitecustomize.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))
 
-These are the three generated files.
+We didn't ask for any initialization, and we didn't ask to use the underlying
+sitecustomization, so sitecustomize.py is empty.
 
-    >>> cat(interpreter_dir, 'bin', 'py')
+    >>> 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/python2.4 -S
     <BLANKLINE>
     import os
@@ -1050,29 +1007,102 @@
         path = os.pathsep.join([path, environ['PYTHONPATH']])
     environ['PYTHONPATH'] = path
     os.execve(sys.executable, argv, environ)
-    >>> ls(interpreter_dir, 'parts', 'interpreter')
-    -  site.py
-    -  sitecustomize.py
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'site.py')
-    import sys
-    sys.path[0:0] = [
-      '/interpreter/eggs/demo-0.3-py2.4.egg',
-      '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
-      ]
-    import sitecustomize
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'sitecustomize.py')
 
+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 has all of the trickier bits, and sets up the
+Python path, similarly to the behavior of the function it replaces.  The
+following shows the part that buildout inserts.
+
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        paths = []
+        paths[0:0] = [ # eggs
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
+            ]
+        # Process all dirs.  Look for .pth files.  If they exist, defer
+        # processing "import" varieties.
+        dotpth = os.extsep + "pth"
+        deferred = []
+        for path in reversed(paths):
+            # Duplicating addsitedir.
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.insert(0, sitedir)
+                known_paths.add(sitedircase)
+            try:
+                names = os.listdir(sitedir)
+            except os.error:
+                continue
+            names = [name for name in names if name.endswith(dotpth)]
+            names.sort()
+            for name in names:
+                # Duplicating addpackage.
+                fullname = os.path.join(sitedir, name)
+                try:
+                    f = open(fullname, "rU")
+                except IOError:
+                    continue
+                try:
+                    for line in f:
+                        if line.startswith("#"):
+                            continue
+                        if (line.startswith("import ") or
+                            line.startswith("import ")):
+                            # This line is supposed to be executed.  It
+                            # might be a setuptools namespace package
+                            # installed with a system package manager.
+                            # Defer this so we can process egg namespace
+                            # packages first, or else the eggs with the same
+                            # namespace will be ignored.
+                            deferred.append((sitedir, name, fullname, line))
+                            continue
+                        line = line.rstrip()
+                        dir, dircase = makepath(sitedir, line)
+                        if not dircase in known_paths and os.path.exists(dir):
+                            sys.path.append(dir)
+                            known_paths.add(dircase)
+                finally:
+                    f.close()
+        # Process "import ..." .pth lines.
+        for sitedir, name, fullname, line in deferred:
+            # Note that some lines--such as the one setuptools writes for
+            # namespace packages--expect some or all of sitedir, name, and
+            # fullname to be present in the frame locals, as it is in
+            # ``addpackage``.
+            try:
+                exec line
+            except:
+                print "Error in %s" % (fullname,)
+                raise
+        global addsitepackages
+        addsitepackages = original_addsitepackages
+        return known_paths
+    <BLANKLINE>
+    buildout_addsitepackages = addsitepackages
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+As you can see, it manipulates the path to insert the eggs and then processes
+any .pth files.  The lines in the .pth files that use the "import" feature
+are deferred because it is a pattern we will need in a later example, when we
+show how we can add site packages, and handle competing namespace packages
+in both site packages and eggs.
+
 Here are some examples of the interpreter in use.
 
-    >>> print system(join(interpreter_dir, 'bin', 'py') + ' -c "print 16+26"')
+    >>> print system(interpreter_path + ' -c "print 16+26"')
     42
     <BLANKLINE>
-    >>> res = system(join(interpreter_dir, 'bin', 'py') +
+    >>> res = system(interpreter_path +
     ...              ' -c "import sys, pprint; pprint.pprint(sys.path)"')
     >>> print res # doctest: +ELLIPSIS
     ['',
-     '/interpreter/eggs/demo-0.3-py2.4.egg',
-     '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
      '/interpreter/parts/interpreter',
      ...]
     <BLANKLINE>
@@ -1083,24 +1113,23 @@
     >>> 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_dir, 'bin')
-    ...     mkdir(interpreter_dir, 'bin')
-    ...     rmdir(interpreter_dir, 'parts', 'interpreter')
-    ...     mkdir(interpreter_dir, 'parts', 'interpreter')
+    ...     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.interpreter(
-    ...     'py', ws, sys.executable, join(interpreter_dir, 'bin'),
-    ...     join(interpreter_dir, 'parts', 'interpreter'),
-    ...     initialization=initialization_string)
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'sitecustomize.py')
+    >>> generated = zc.buildout.easy_install.generate_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 system(join(interpreter_dir, 'bin', 'py') +
+    >>> print system(interpreter_path +
     ...              """ -c 'import os; print os.environ["FOO"]'""")
     bar baz bing shazam
     <BLANKLINE>
@@ -1108,11 +1137,10 @@
 If you use relative paths, this affects the interpreter and site.py.
 
     >>> reset_interpreter()
-    >>> generated = zc.buildout.easy_install.interpreter(
-    ...     'py', ws, sys.executable, join(interpreter_dir, 'bin'),
-    ...     join(interpreter_dir, 'parts', 'interpreter'),
-    ...     relative_paths=interpreter_dir)
-    >>> cat(interpreter_dir, 'bin', 'py')
+    >>> generated = zc.buildout.easy_install.generate_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', relative_paths=interpreter_dir)
+    >>> cat(py_path)
     #!/usr/bin/python2.4 -S
     <BLANKLINE>
     import os
@@ -1131,21 +1159,30 @@
         path = os.pathsep.join([path, environ['PYTHONPATH']])
     environ['PYTHONPATH'] = path
     os.execve(sys.executable, argv, environ)
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'site.py')
+
+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):
+        paths = []
     <BLANKLINE>
-    import os
+        import os
     <BLANKLINE>
-    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)
-    import sys
-    sys.path[0:0] = [
-      join(base, 'eggs/demo-0.3-pyN.N.egg'),
-      join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
-      ]
-    import sitecustomize
-    >>> print system(join(interpreter_dir, 'bin', 'py') +
+        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)
+        paths[0:0] = [ # eggs
+            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 system(interpreter_path +
     ...              ' -c "import sys, pprint; pprint.pprint(sys.path)"')
     ... # doctest: +ELLIPSIS
     ['',
@@ -1155,61 +1192,176 @@
      ...]
     <BLANKLINE>
 
-The ``extra_paths`` argument affects the path in site.py.
+The ``extra_paths`` argument affects the path in site.py.  Notice that
+/interpreter/other is added after the eggs.
 
     >>> reset_interpreter()
-    >>> generated = zc.buildout.easy_install.interpreter(
-    ...     'py', ws, sys.executable, join(interpreter_dir, 'bin'),
-    ...     join(interpreter_dir, 'parts', 'interpreter'),
-    ...     extra_paths=[join(interpreter_dir, 'other')])
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'site.py')
-    import sys
-    sys.path[0:0] = [
-      '/interpreter/eggs/demo-0.3-py2.4.egg',
-      '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
-      '/interpreter/other',
-      ]
-    import sitecustomize
-    >>> print system(join(interpreter_dir, 'bin', 'py') +
+    >>> mkdir(interpreter_dir, 'other')
+    >>> generated = zc.buildout.easy_install.generate_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):
+        paths = []
+        paths[0:0] = [ # eggs
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+            '/interpreter/other'
+            ]...
+
+    >>> print system(interpreter_path +
     ...              ' -c "import sys, pprint; pprint.pprint(sys.path)"')
     ... # doctest: +ELLIPSIS
     ['',
-     '/interpreter/eggs/demo-0.3-py2.4.egg',
-     '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
+     '/interpreter/eggs/demo-0.3-pyN.N.egg',
+     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
      '/interpreter/other',
      '/interpreter/parts/interpreter',
      ...]
     <BLANKLINE>
 
-The ``import_site`` argument also affects site.py.  It causes the executable's
-real site.py to be executed after our customizations have run.
+The ``generate_scripts`` function: using site-packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+The ``generate_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
+buildbot.  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.  Unfortunately, because of how
+setuptools namespace packages are implemented differently for operating
+system packages (debs or rpms) and normal installation, there's a tricky
+dance.
+
     >>> reset_interpreter()
-    >>> generated = zc.buildout.easy_install.interpreter(
-    ...     'py', ws, sys.executable, join(interpreter_dir, 'bin'),
-    ...     join(interpreter_dir, 'parts', 'interpreter'),
-    ...     import_site=True)
-    >>> cat(interpreter_dir, 'parts', 'interpreter', 'site.py')
-    import sys
-    sys.path[0:0] = [
-      '/interpreter/eggs/demo-0.3-py2.4.egg',
-      '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
-      ]
-    # We have to import pkg_resources before namespace
-    # package .pth files are processed or else the distribution's namespace
-    # packages will mask all of the egg-based packages in the same namespace
-    # package.
-    try:
-      import pkg_resources
-    except ImportError:
-      pass
-    execfile('/usr/lib/python/site.py')
+    >>> generated = zc.buildout.easy_install.generate_scripts(
+    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+    ...     interpreter='py', add_site_packages=True)
+    >>> sys.stdout.write('#\n'); cat(site_path)
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+    #...
+    def addsitepackages(known_paths):
+        paths = [ # These are the underlying Python's site-packages.
+            '...']
+        sys.path[0:0] = paths
+        known_paths.update([os.path.normcase(os.path.abspath(p)) for p in paths])
+        try:
+            import pkg_resources
+        except ImportError:
+            # No namespace packages in sys.path; no fixup needed.
+            pkg_resources = None
+        paths[0:0] = [ # eggs
+            '/interpreter/eggs/demo-0.3-pyN.N.egg',
+            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
+            ]
+        # Process all dirs.  Look for .pth files.  If they exist, defer
+        # processing "import" varieties.
+        dotpth = os.extsep + "pth"
+        deferred = []
+        for path in reversed(paths):
+            # Duplicating addsitedir.
+            sitedir, sitedircase = makepath(path)
+            if not sitedircase in known_paths and os.path.exists(sitedir):
+                sys.path.insert(0, sitedir)
+                known_paths.add(sitedircase)
+            try:
+                names = os.listdir(sitedir)
+            except os.error:
+                continue
+            names = [name for name in names if name.endswith(dotpth)]
+            names.sort()
+            for name in names:
+                # Duplicating addpackage.
+                fullname = os.path.join(sitedir, name)
+                try:
+                    f = open(fullname, "rU")
+                except IOError:
+                    continue
+                try:
+                    for line in f:
+                        if line.startswith("#"):
+                            continue
+                        if (line.startswith("import ") or
+                            line.startswith("import	")):
+                            # This line is supposed to be executed.  It
+                            # might be a setuptools namespace package
+                            # installed with a system package manager.
+                            # Defer this so we can process egg namespace
+                            # packages first, or else the eggs with the same
+                            # namespace will be ignored.
+                            deferred.append((sitedir, name, fullname, line))
+                            continue
+                        line = line.rstrip()
+                        dir, dircase = makepath(sitedir, line)
+                        if not dircase in known_paths and os.path.exists(dir):
+                            sys.path.append(dir)
+                            known_paths.add(dircase)
+                finally:
+                    f.close()
+        if pkg_resources is not None:
+            # There may be namespace packages in sys.path.  This is much faster
+            # than importing pkg_resources after the sys.path has a large number
+            # of eggs.
+            for p in sys.path:
+                pkg_resources.fixup_namespace_packages(p)
+        # Process "import ..." .pth lines.
+        for sitedir, name, fullname, line in deferred:
+            # Note that some lines--such as the one setuptools writes for
+            # namespace packages--expect some or all of sitedir, name, and
+            # fullname to be present in the frame locals, as it is in
+            # ``addpackage``.
+            try:
+                exec line
+            except:
+                print "Error in %s" % (fullname,)
+                raise
+        global addsitepackages
+        addsitepackages = original_addsitepackages
+        return known_paths
+    <BLANKLINE>
+    buildout_addsitepackages = addsitepackages
+    <BLANKLINE>
+    def original_addsitepackages(known_paths):...
+
+As you can see, the script now first tries to import pkg_resources.  If it
+exists, 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.
+
+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 = system(join(interpreter_dir, 'bin', 'py') +
     ...              ' -c "import sys, pprint; pprint.pprint(sys.path)"')
     >>> print res # doctest: +ELLIPSIS
     ['',
      '/interpreter/eggs/demo-0.3-py2.4.egg',
      '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
+     '...',
      '/interpreter/parts/interpreter',
      ...]
     <BLANKLINE>
@@ -1219,13 +1371,107 @@
     >>> full_paths = eval(res.strip())
     >>> len(clean_paths) < len(full_paths)
     True
-    >>> set(os.path.normpath(p) for p in clean_paths).difference(
+    >>> set(os.path.normpath(p) for p in clean_paths).issubset(
     ...     os.path.normpath(p) for p in full_paths)
-    set([])
+    True
 
-The ``import_sitecustomize`` argument does the same thing for the
-sitecustomize module.
+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 ``generate_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()
+    >>> generated = zc.buildout.easy_install.generate_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 site # imports custom buildbot-generated site.py
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main()
+
+    >>> print system(join(interpreter_bin_dir, 'demo'))
+    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.generate_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 site # imports custom buildbot-generated site.py
+    import os
+    os.chdir("foo")
+    <BLANKLINE>
+    import eggrecipedemo
+    <BLANKLINE>
+    if __name__ == '__main__':
+        eggrecipedemo.main(1, 2)
+
 Handling custom build options for extensions provided in source distributions
 -----------------------------------------------------------------------------
 

Modified: zc.buildout/branches/gary-4/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/testing.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/testing.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -117,8 +117,13 @@
     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.
+    args.append(env)
 
+
     here = os.getcwd()
     try:
         os.chdir(d)
@@ -136,6 +141,11 @@
 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)
     if e is not None:
@@ -295,12 +305,15 @@
             '  ' + 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
 
             [py]
-            recipe = zc.recipe.egg:interpreter
+            recipe = z3c.recipe.scripts
+            interpreter = py
             initialization =
             %(initialization)s
             extra-paths = %(site-packages)s

Modified: zc.buildout/branches/gary-4/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/tests.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/tests.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -53,6 +53,7 @@
 
     >>> ls('develop-eggs')
     -  foo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     """
@@ -84,6 +85,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
@@ -668,6 +670,7 @@
 
     >>> ls('develop-eggs')
     -  foox.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Create another:
@@ -692,6 +695,7 @@
     >>> ls('develop-eggs')
     -  foox.egg-link
     -  fooy.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Remove one:
@@ -709,6 +713,7 @@
 
     >>> ls('develop-eggs')
     -  fooy.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
 Remove the other:
@@ -723,6 +728,7 @@
 All gone
 
     >>> ls('develop-eggs')
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
     '''
 
@@ -797,6 +803,7 @@
     ...            + join(sample_buildout, 'eggs'))
 
     >>> ls('develop-eggs')
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     >>> ls('eggs') # doctest: +ELLIPSIS
@@ -1769,6 +1776,240 @@
     1 2
     """
 
+def versions_section_ignored_for_dependency_in_favor_of_site_packages():
+    r"""
+This is a test for a bugfix.
+
+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.
+
+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.
+
+    >>> py_path = make_py_with_system_install(make_py, sample_eggs)
+    >>> print system(
+    ...     py_path + " -c '" +
+    ...     "import tellmy.version\n" +
+    ...     "print tellmy.version.__version__\n" +
+    ...     "'"),
+    1.1
+
+Now here's a setup that would expose the bug, using the
+zc.buildout.easy_install API.
+
+    >>> 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
+
+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.generate_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 system(
+    ...     py_path + " -c '" +
+    ...     "import tellmy.version\n" +
+    ...     "print tellmy.version.__version__\n" +
+    ...     "'")
+    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
+    ... add-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 for the actual test.  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 system(
+    ...     join('bin', 'py') + " -c '" +
+    ...     "import tellmy.version\n" +
+    ...     "print tellmy.version.__version__\n" +
+    ...     "import tellmy.fortune\n" +
+    ...     "print tellmy.fortune.__version__\n" +
+    ...     "'")
+    1.0
+    1.0
+    <BLANKLINE>
+    >>> print system(join('bin', 'demo'))
+    1.0
+    1.0
+    4 2
+    <BLANKLINE>
+    """
+
+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 system(
+    ...     py_path + " -c '" +
+    ...     "import tellmy.version\n" +
+    ...     "print tellmy.version.__version__\n" +
+    ...     "'")
+    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>
+
+The bugfix was simply to add Python's "-S" option when calling
+easyinstall (see zc.buildout.easy_install.Installer._call_easy_install).
+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>
+
+    """
+
 if sys.version_info > (2, 4):
     def test_exit_codes():
         """
@@ -2367,6 +2608,7 @@
 
     >>> ls('develop-eggs')
     -  foo.egg-link
+    -  z3c.recipe.scripts.egg-link
     -  zc.recipe.egg.egg-link
 
     """
@@ -2654,6 +2896,44 @@
 
 ######################################################################
 
+def make_py_with_system_install(make_py, sample_eggs):
+    from zc.buildout.testing import write, mkdir
+    py_path, site_packages_path = make_py()
+    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(\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, sample_eggs)
+            if 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)
+    return py_path
+
 def create_sample_eggs(test, executable=sys.executable):
     write = test.globs['write']
     dest = test.globs['sample_eggs']
@@ -2776,6 +3056,7 @@
         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)
 
 egg_parse = re.compile('([0-9a-zA-Z_.]+)-([0-9a-zA-Z_.]+)-py(\d[.]\d).egg$'
                        ).match
@@ -2933,8 +3214,7 @@
                (re.compile('extdemo[.]pyd'), 'extdemo.so'),
                (re.compile('[-d]  setuptools-\S+[.]egg'), 'setuptools.egg'),
                (re.compile(r'\\[\\]?'), '/'),
-               (re.compile('''execfile\(['"].+site.py['"]\)'''),
-                "execfile('/usr/lib/python/site.py')"),
+               (re.compile(r'\#!\S+\bpython\S*'), '#!/usr/bin/python'),
                ]+(sys.version_info < (2, 5) and [
                   (re.compile('.*No module named runpy.*', re.S), ''),
                   (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),

Modified: zc.buildout/branches/gary-4/src/zc/buildout/testselectingpython.py
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/testselectingpython.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/testselectingpython.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-import os, re, sys, unittest
+import os, re, subprocess, sys, textwrap, unittest
 from zope.testing import doctest, renormalizing
 import zc.buildout.tests
 import zc.buildout.testing
@@ -43,6 +43,33 @@
 
 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)
+        ''')
+    if subprocess.call([other_executable, '-c', command],
+                       env=os.environ):
+        # 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.generate_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=os.environ), (
+            '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/gary-4/src/zc/buildout/update.txt
===================================================================
--- zc.buildout/branches/gary-4/src/zc/buildout/update.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/src/zc/buildout/update.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -85,9 +85,9 @@
     <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',
-      ]
+        '/sample-buildout/eggs/zc.buildout-99.99-py2.4.egg',
+        '/sample-buildout/eggs/setuptools-99.99-py2.4.egg',
+        ]
     <BLANKLINE>
     <BLANKLINE>
     import zc.buildout.buildout

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/CHANGES.txt
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/CHANGES.txt	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/CHANGES.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,7 @@
+Change History
+**************
+
+1.0.0
+=====
+
+Initial public version.

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/README.txt
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/README.txt	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/README.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,10 @@
+********************************
+Buildout Script Recipe
+********************************
+
+.. contents::
+
+The script recipe installs eggs into a buildout eggs directory, exactly
+like zc.recipe.egg, and then generates scripts in a buildout bin
+directory with egg paths baked into them.
+

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/setup.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/setup.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/setup.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,76 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# 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.
+#
+##############################################################################
+"""Setup for z3c.recipe.scripts package
+
+$Id: setup.py 106736 2009-12-18 02:33:08Z gary $
+"""
+
+version = '1.0'
+
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+name = "z3c.recipe.scripts"
+setup(
+    name = name,
+    version = version,
+    author = "Gary Poster",
+    author_email = "gary.poster at canonical.com",
+    description = "Recipe for installing Python scripts",
+    long_description = (
+        read('README.txt')
+        + '\n' +
+        read('CHANGES.txt')
+        + '\n' +
+        'Detailed Documentation\n'
+        '**********************\n'
+        + '\n' +
+        read('src', 'z3c', 'recipe', 'scripts', 'README.txt')
+        + '\n' +
+        'Download\n'
+        '*********\n'
+        ),
+    keywords = "development build",
+    classifiers = [
+       'Development Status :: 5 - Production/Stable',
+       'Framework :: Buildout',
+       'Intended Audience :: Developers',
+       'License :: OSI Approved :: Zope Public License',
+       'Topic :: Software Development :: Build Tools',
+       'Topic :: Software Development :: Libraries :: Python Modules',
+       ],
+    url='http://cheeseshop.python.org/pypi/z3c.recipe.scripts',
+    license = "ZPL 2.1",
+
+    packages = find_packages('src'),
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c', 'z3c.recipe'],
+    install_requires = [
+        'zc.buildout >=1.2.0',
+        'zc.recipe.egg',
+        'setuptools'],
+    tests_require = ['zope.testing'],
+    test_suite = name+'.tests.test_suite',
+    entry_points = {'zc.buildout': ['default = %s:Scripts' % name,
+                                    'script = %s:Scripts' % name,
+                                    'scripts = %s:Scripts' % name,
+                                    'interpreter = %s:Interpreter' % name,
+                                    ]
+                    },
+    include_package_data = True,
+    zip_safe=False,
+    )

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/__init__.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/__init__.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/__init__.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/__init__.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/__init__.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/__init__.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/README.txt
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/README.txt	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/README.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,377 @@
+Script and interpreter generation
+=================================
+
+This recipe is very similar to zc.recipe.egg, and if you are familiar with its
+options, you will be able to use this one easily.
+
+The script and interpreter generation in this recipe are improved from
+those provided by zc.recipe.egg in two basic ways.
+
+- The interpreter generated by the script supports all interpreter
+  options, as opposed to the subset provided by zc.recipe.egg.
+
+- Both scripts and interpreters from this recipe can optionally choose
+  to include site-packages, and even sitecustomize.
+
+The recipe takes several options.  First, here's the list of the options
+that overlap from the standard zc.recipe.eggs scripts recipe.  After
+this, we'll list the new options and describe them.
+
+* eggs
+* find-links
+* index
+* python
+* extra-paths
+* entry-points
+* scripts
+* dependent-scripts
+* interpreter
+* arguments
+* initialization
+* relative-paths
+
+In addition to these, the recipe offers these new options.  They are
+introduced here, and described more in depth below.
+
+add-site-packages
+    You can choose to have the site-packages of the underlying Python
+    available to your script or interpreter, in addition to the packages
+    from your eggs.  See the section on this option for motivations and
+    warnings.
+
+extends
+    You can extend another section using this value.  It is intended to be
+    used by extending a section that uses this package's scripts recipe.
+    In this manner, you can avoid repeating yourself.
+
+exec-sitecustomize
+    Normally the Python's real sitecustomize module is not processed.
+    If you want it to be processed, set this value to 'true'.  This will
+    be honored irrespective of the setting for include-site-paths.
+
+script-initialization
+    The standard initialization code affects both an interpreter and scripts.
+    The code in script-initialization is used only for the generated scripts.
+
+Finally, the "interpreter" entry point ignores ``script-initialization``,
+``scripts``, and ``arguments``, and provides yet another additional option.
+
+name
+    While, by default, the interpreter recipe takes the name of the
+    section to be the desired interpreter name, you can specify the
+    interpreter name here instead.
+
+Script generation
+-----------------
+
+Generating a basic script looks virtually identical to using zc.recipe.egg.
+
+(Note that the find-links and index values are typically not needed; they
+are included to help make this document run as a test successfully.)
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = demo
+    ...
+    ... [demo]
+    ... recipe = z3c.recipe.scripts
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... """ % dict(server=link_server))
+
+    >>> print system(buildout),
+    Installing demo.
+    Getting distribution for 'demo<0.3'.
+    Got demo 0.2.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated script '/sample-buildout/bin/demo'.
+
+    >>> print system(join(sample_buildout, 'bin', 'demo')),
+    2 2
+
+Interpreter generation
+----------------------
+
+As with zc.recipe.egg, you can generate an interpreter with the default
+script recipe shown above by supplying the "interpreter" option.
+This example will create both an entry point script and an interpreter.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = demo
+    ...
+    ... [demo]
+    ... recipe = z3c.recipe.scripts
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... interpreter = py
+    ... """ % dict(server=link_server))
+
+    >>> print system(buildout),
+    Uninstalling demo.
+    Installing demo.
+    Generated script '/sample-buildout/bin/demo'.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+You can also generate an interpreter alone with the ``interpreter`` recipe.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... """ % dict(server=link_server))
+
+    >>> print system(buildout),
+    Uninstalling demo.
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+In both cases, the bin/py script works by restarting Python after
+specifying a special path in PYTHONPATH.
+
+    >>> cat(sample_buildout, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
+    #!/usr/bin/python2.4 -S
+    <BLANKLINE>
+    import os
+    import sys
+    <BLANKLINE>
+    argv = [sys.executable] + sys.argv[1:]
+    environ = os.environ.copy()
+    path = '/sample-buildout/parts/py'
+    if environ.get('PYTHONPATH'):
+        path = os.pathsep.join([path, environ['PYTHONPATH']])
+    environ['PYTHONPATH'] = path
+    os.execve(sys.executable, argv, environ)
+
+The path is a directory that contains two files: our own site.py and
+sitecustomize.py.  The site.py is modified from the underlying Python's
+site.py, and is responsible for setting up our paths. The
+sitecustomize.py is responsible for running the initialization code
+provided.
+
+    >>> ls(sample_buildout, 'parts', 'py')
+    -  site.py
+    -  sitecustomize.py
+
+Here's an example of using the generated interpreter.
+
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
+    ['',
+     '/sample-buildout/eggs/demo-0.2-pyN.N.egg',
+     '/sample-buildout/eggs/demoneeded-1.2c1-pyN.N.egg']
+    <BLANKLINE>
+
+Including site-packages and sitecustomize
+-----------------------------------------
+
+As introduced above, this recipe 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
+buildbot.  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.
+
+To show off these features, we need to use buildout with a Python
+executable with some extra paths to show ``add-site-packages``; and one
+guaranteed to have a sitecustomize module to show
+``exec-sitecustomize``.  We'll make one using a test fixture called
+``make_py``. The os.environ change below will go into the sitecustomize,
+and the site_packages_path will be in the Python's path.
+
+    >>> py_path, site_packages_path = make_py(initialization='''\
+    ... import os
+    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
+    ... ''')
+    >>> print site_packages_path
+    /executable_buildout/site-packages
+
+Now let's take a look at add-site-packages.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = py
+    ... executable = %(py_path)s
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... add-site-packages = true
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... """ % dict(server=link_server, py_path=py_path))
+
+    >>> print system(buildout),
+    Uninstalling py.
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import sys, pprint; pprint.pprint(sys.path)"''')
+    ... # doctest: +ELLIPSIS
+    ['',
+     '/sample-buildout/eggs/demo-0.2-pyN.N.egg',
+     '/sample-buildout/eggs/demoneeded-1.2c1-pyN.N.egg',
+     '/executable_buildout/eggs/setuptools-0.6c11-pyN.N.egg',
+     '/executable_buildout/site-packages',
+     '/sample-buildout/parts/py',
+     '/executable_buildout/parts/py',
+     ...]
+    <BLANKLINE>
+
+Next we will use the exec-sitecustomize option.  It simply copies
+Python's underlying sitecustomize module, if it exists, to the local
+version.  The os.environ change shown above in the make_py call will go
+into the sitecustomize.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = py
+    ... executable = %(py_path)s
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... exec-sitecustomize = true
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... """ % dict(server=link_server, py_path=py_path))
+
+    >>> print system(buildout),
+    Uninstalling py.
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    <BLANKLINE>
+    # The following is from
+    # /executable_buildout/parts/py/sitecustomize.py
+    <BLANKLINE>
+    import os
+    os.environ['zc.buildout'] = 'foo bar baz shazam'
+
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import os; print os.environ['zc.buildout']"''')
+    foo bar baz shazam
+    <BLANKLINE>
+
+Options
+-------
+
+We'll focus now on the options that are different than zc.recipe.egg.
+
+Let's look at the ``extends`` option first.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = demo python
+    ...
+    ... [demo]
+    ... recipe = z3c.recipe.scripts
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ...
+    ... [python]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... extends = demo
+    ... initialization =
+    ...     import os
+    ...     os.environ['zc.buildout'] = 'foo bar baz shazam'
+    ... """ % dict(server=link_server))
+
+That makes it easier to specify some initialization for the interpreter
+that is different than a script, while duplicating other configuration.
+
+Now let's put it in action.
+
+    >>> print system(buildout),
+    Uninstalling py.
+    Installing demo.
+    Generated script '/sample-buildout/bin/demo'.
+    Installing python.
+    Generated interpreter '/sample-buildout/bin/python'.
+
+    >>> print system(join(sample_buildout, 'bin', 'python') +
+    ...              ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
+    ['',
+     '/sample-buildout/eggs/demo-0.2-pyN.N.egg',
+     '/sample-buildout/eggs/demoneeded-1.2c1-pyN.N.egg']
+    <BLANKLINE>
+    >>> print system(join(sample_buildout, 'bin', 'python') +
+    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
+    foo bar baz shazam
+
+Note that the parts/py directory has been cleaned up, and parts/python has
+been created.
+
+    >>> ls(sample_buildout, 'parts')
+    d  demo
+    d  python
+
+script-initialization
+
+XXX
+
+The last new option is ``name``.  This simply changes the name of the
+interpreter, so that you are not forced to use the name of the section.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... """
+    ... [buildout]
+    ... parts = interpreter
+    ...
+    ... [interpreter]
+    ... name = python2
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... """ % dict(server=link_server))
+
+    >>> print system(buildout),
+    Uninstalling python.
+    Uninstalling demo.
+    Installing interpreter.
+    Generated interpreter '/sample-buildout/bin/python2'.
+
+    >>> print system(join(sample_buildout, 'bin', 'python2') +
+    ...              ' -c "print 42"')
+    42
+    <BLANKLINE>
+
+The other options all identical to the zc.recipe.egg script.  Here are some
+quick demos and discussions.

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/__init__.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/__init__.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/__init__.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1 @@
+from z3c.recipe.scripts.scripts import Scripts, Interpreter

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/scripts.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/scripts.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/scripts.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,101 @@
+##############################################################################
+#
+# Copyright (c) 2009-2010 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# 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.
+#
+##############################################################################
+"""Install scripts from eggs.
+"""
+import os
+import zc.buildout
+import zc.buildout.easy_install
+from zc.recipe.egg.egg import ScriptBase
+
+
+class Base(ScriptBase):
+
+    def __init__(self, buildout, name, options):
+        if 'extends' in options:
+            options.update(buildout[options['extends']])
+        super(Base, self).__init__(buildout, name, options)
+        self.default_eggs = '' # Disables feature from zc.recipe.egg.
+        b_options = buildout['buildout']
+        options['parts-directory'] = os.path.join(
+            b_options['parts-directory'], self.name)
+
+        value = options.setdefault(
+            'add-site-packages',
+            b_options.get('add-site-packages', 'false'))
+        if value not in ('true', 'false'):
+            raise zc.buildout.UserError(
+                "Invalid value for add-site-packages option: %s" %
+                (value,))
+        self.add_site_packages = (value == 'true')
+
+        value = options.setdefault(
+            'exec-sitecustomize',
+            b_options.get('exec-sitecustomize', 'false'))
+        if value not in ('true', 'false'):
+            raise zc.buildout.UserError(
+                "Invalid value for exec-sitecustomize option: %s" %
+                (value,))
+        self.exec_sitecustomize = (value == 'true')
+
+
+class Interpreter(Base):
+
+    def __init__(self, buildout, name, options):
+        super(Interpreter, self).__init__(buildout, name, options)
+
+        options.setdefault('name', name)
+
+    def install(self):
+        reqs, ws = self.working_set()
+        options = self.options
+        generated = []
+        if not os.path.exists(options['parts-directory']):
+            os.mkdir(options['parts-directory'])
+            generated.append(options['parts-directory'])
+        generated.extend(zc.buildout.easy_install.generate_scripts(
+            options['bin-directory'], ws, options['executable'],
+            options['parts-directory'],
+            interpreter=options['name'],
+            extra_paths=self.extra_paths,
+            initialization=options.get('initialization', ''),
+            add_site_packages=self.add_site_packages,
+            exec_sitecustomize=self.exec_sitecustomize,
+            relative_paths=self._relative_paths,
+            ))
+        return generated
+
+    update = install
+
+
+class Scripts(Base):
+
+    def _install(self, reqs, ws, scripts):
+        options = self.options
+        generated = []
+        if not os.path.exists(options['parts-directory']):
+            os.mkdir(options['parts-directory'])
+            generated.append(options['parts-directory'])
+        generated.extend(zc.buildout.easy_install.generate_scripts(
+            options['bin-directory'], ws, options['executable'],
+            options['parts-directory'], reqs=reqs, scripts=scripts,
+            interpreter=options.get('interpreter'),
+            extra_paths=self.extra_paths,
+            initialization=options.get('initialization', ''),
+            add_site_packages=self.add_site_packages,
+            exec_sitecustomize=self.exec_sitecustomize,
+            relative_paths=self._relative_paths,
+            script_arguments=options.get('arguments', ''),
+            script_initialization=options.get('script-initialization', '')
+            ))
+        return generated

Added: zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/tests.py
===================================================================
--- zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/tests.py	                        (rev 0)
+++ zc.buildout/branches/gary-4/z3c.recipe.scripts_/src/z3c/recipe/scripts/tests.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -0,0 +1,291 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# 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, shutil, sys
+import zc.buildout.tests
+import zc.buildout.testselectingpython
+import zc.buildout.testing
+
+import unittest
+from zope.testing import doctest, renormalizing
+
+# We do not explicitly test the recipe support for the ``eggs``,
+# ``find-links``, and ``index`` options because they are used for most or
+# all of the examples.  The README tests ``extends``,
+# ``include-site-customization`` and ``name``.  That leaves ``python``,
+# ``extra-paths``, ``initialization``, ``relative-paths``, and
+# ``include-site-packages``.
+
+def supports_python_option():
+    """
+This simply shows that the ``python`` option can specify another section to
+find the ``executable``.  (The ``python`` option defaults to looking in the
+``buildout`` section.)  We do this by creating a custom Python that will have
+some initialization that we can look for.
+
+    >>> py_path, site_packages_path = make_py(initialization='''
+    ... import os
+    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
+    ... ''')
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [custom_python]
+    ... executable = %(py_path)s
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... exec-sitecustomize = true
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... python = custom_python
+    ... ''' % dict(server=link_server, py_path=py_path))
+
+    >>> print system(buildout),
+    Installing py.
+    Getting distribution for 'demo<0.3'.
+    Got demo 0.2.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
+    foo bar baz shazam
+"""
+
+def interpreter_recipe_supports_extra_paths_option():
+    """
+This shows that specifying extra-paths will affect sys.path.
+
+This recipe will not add paths that do not exist, so we create them.
+
+    >>> mkdir(sample_buildout, 'foo')
+    >>> mkdir(sample_buildout, 'foo', 'bar')
+    >>> mkdir(sample_buildout, 'spam')
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... extra-paths =
+    ...    ${buildout:directory}/foo/bar
+    ...    ${buildout:directory}/spam
+    ... ''' % dict(server=link_server))
+
+    >>> print system(buildout),
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import sys;print 'path' + ' '.join(sys.path)"''')
+    ... # doctest:+ELLIPSIS
+    path.../foo/bar /sample-buildout/spam...
+
+"""
+
+def interpreter_recipe_supports_initialization_option():
+    """
+This simply shows that the ``initialization`` option can specify code to
+run on initialization.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... initialization =
+    ...     import os
+    ...     os.environ['zc.buildout'] = 'foo bar baz shazam'
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... ''' % dict(server=link_server))
+
+    >>> print system(buildout),
+    Installing py.
+    Getting distribution for 'demo<0.3'.
+    Got demo 0.2.
+    Getting distribution for 'demoneeded'.
+    Got demoneeded 1.2c1.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    <BLANKLINE>
+    import os
+    os.environ['zc.buildout'] = 'foo bar baz shazam'
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
+    foo bar baz shazam
+
+This also works with the exec-sitecustomize option, processing local
+initialization, and then the Python's initialization.  We show this with a
+custom Python.
+
+    >>> py_path, site_packages_path = make_py(initialization='''
+    ... import os
+    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
+    ... ''')
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [custom_python]
+    ... executable = %(py_path)s
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... initialization =
+    ...     import os
+    ...     os.environ['zc.recipe.egg'] = 'baLOOba'
+    ... exec-sitecustomize = true
+    ... eggs = demo<0.3
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... python = custom_python
+    ... ''' % dict(server=link_server, py_path=py_path))
+
+    >>> print system(buildout),
+    Uninstalling py.
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
+    ... # doctest: +NORMALIZE_WHITESPACE
+    <BLANKLINE>
+    import os
+    os.environ['zc.recipe.egg'] = 'baLOOba'
+    <BLANKLINE>
+    # The following is from
+    # /executable_buildout/parts/py/sitecustomize.py
+    <BLANKLINE>
+    import os
+    os.environ['zc.buildout'] = 'foo bar baz shazam'
+
+    >>> print system(join(sample_buildout, 'bin', 'py') + ' -c ' +
+    ...              '''"import os; print os.environ['zc.recipe.egg']"'''),
+    baLOOba
+    >>> print system(join(sample_buildout, 'bin', 'py') +
+    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
+    foo bar baz shazam
+
+"""
+
+def interpreter_recipe_supports_relative_paths_option():
+    """
+This shows that the relative-paths option affects the code for inserting
+paths into sys.path.
+
+    >>> write(sample_buildout, 'buildout.cfg',
+    ... '''
+    ... [buildout]
+    ... parts = py
+    ...
+    ... [py]
+    ... recipe = z3c.recipe.scripts:interpreter
+    ... find-links = %(server)s
+    ... index = %(server)s/index
+    ... relative-paths = true
+    ... extra-paths =
+    ...    /foo/bar
+    ...    ${buildout:directory}/spam
+    ... ''' % dict(server=link_server))
+
+    >>> print system(buildout),
+    Installing py.
+    Generated interpreter '/sample-buildout/bin/py'.
+
+Let's look at the site.py that was generated:
+
+    >>> import sys
+    >>> sys.stdout.write('#'); cat(sample_buildout, 'parts', 'py', 'site.py')
+    ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+    #...
+    def addsitepackages(known_paths):
+        paths = []
+    <BLANKLINE>
+        import os
+    <BLANKLINE>
+        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)
+        paths[0:0] = [ # eggs
+            '/foo/bar',
+            join(base, 'spam')
+            ]...
+
+
+"""
+
+def setUp(test):
+    zc.buildout.tests.easy_install_SetUp(test)
+    zc.buildout.testing.install_develop('zc.recipe.egg', test)
+    zc.buildout.testing.install_develop('z3c.recipe.scripts', test)
+
+def setUpSelecting(test):
+    zc.buildout.testselectingpython.setup(test)
+    zc.buildout.testing.install_develop('zc.recipe.egg', test)
+    zc.buildout.testing.install_develop('z3c.recipe.scripts', 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(r'zc.buildout(-\S+)?[.]egg(-link)?'),
+                'zc.buildout.egg'),
+               (re.compile('[-d]  setuptools-[^-]+-'), 'setuptools-X-'),
+               (re.compile(r'setuptools-[\w.]+-py'), 'setuptools-X-py'),
+               (re.compile(r'eggs\\\\demo'), 'eggs/demo'),
+               (re.compile(r'[a-zA-Z]:\\\\foo\\\\bar'), '/foo/bar'),
+               (re.compile(r'\#!\S+\bpython\S*'), '#!/usr/bin/python'),
+               ])
+            ),
+        doctest.DocTestSuite(
+            setUp=setUp,
+            tearDown=zc.buildout.testing.buildoutTearDown,
+            checker=renormalizing.RENormalizing([
+                zc.buildout.testing.normalize_path,
+                zc.buildout.testing.normalize_endings,
+                zc.buildout.testing.normalize_egg_py,
+                ]),
+            ),
+
+        ))
+
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+

Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/README.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -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',
@@ -174,10 +174,6 @@
    egg paths.  This option can be set in either the script section or
    in the buildout section.
 
-include-site-packages
-    If set to true, then generated scripts will ``import site`` to include
-    the site packages defined by the executable's site module.
-
 Let's add an interpreter option:
 
     >>> write(sample_buildout, 'buildout.cfg',
@@ -193,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.
 
@@ -550,110 +546,6 @@
 has been stripped.  Similarly, the argument code we specified was
 added in the entry point call (to main).
 
-Including site packages
------------------------
-
-A specific kind of script initialization is available from an option:
-``include-site-packages``.  This option will include code that imports the
-current executable's site module, thus setting whatever site-packages are
-available.  This affects both custom generated scripts and interpreter
-scripts.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... """
-    ... [buildout]
-    ... parts = demo
-    ...
-    ... [demo]
-    ... recipe = zc.recipe.egg
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... scripts = demo=foo
-    ... interpreter = py
-    ... extra-paths =
-    ...    /foo/bar
-    ...    ${buildout:directory}/spam
-    ... include-site-packages = true
-    ... """ % dict(server=link_server))
-
-    >>> print system(buildout),
-    Uninstalling demo.
-    Installing demo.
-    Generated script '/sample-buildout/bin/foo'.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> cat(sample_buildout, 'bin', 'foo') # doctest: +NORMALIZE_WHITESPACE
-    #!/usr/local/bin/python2.4 -S
-    <BLANKLINE>
-    import sys
-    sys.path[0:0] = [
-      '/sample-buildout/eggs/demo-0.4c1-pyN.N.egg',
-      '/sample-buildout/eggs/demoneeded-1.2c1-pyN.N.egg',
-      '/foo/bar',
-      '/sample-buildout/spam',
-      ]
-    # We have to import pkg_resources before namespace
-    # package .pth files are processed or else the distribution's namespace
-    # packages will mask all of the egg-based packages in the same namespace
-    # package.
-    try:
-      import pkg_resources
-    except ImportError:
-      pass
-    import site
-    <BLANKLINE>
-    <BLANKLINE>
-    import eggrecipedemo
-    <BLANKLINE>
-    if __name__ == '__main__':
-        eggrecipedemo.main()
-
-    >>> cat(sample_buildout, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
-    #!/usr/local/bin/python2.4 -S
-    <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',
-      '/foo/bar',
-      '/sample-buildout/spam',
-      ]
-    # We have to import pkg_resources before namespace
-    # package .pth files are processed or else the distribution's namespace
-    # packages will mask all of the egg-based packages in the same namespace
-    # package.
-    try:
-      import pkg_resources
-    except ImportError:
-      pass
-    import site
-    <BLANKLINE>
-    _interactive = True
-    if len(sys.argv) > 1:
-        _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
-        _interactive = False
-        for (_opt, _val) in _options:
-            if _opt == '-i':
-                _interactive = True
-            elif _opt == '-c':
-                exec _val
-            elif _opt == '-m':
-                sys.argv[1:] = _args
-                _args = []
-                __import__("runpy").run_module(
-                     _val, {}, "__main__", alter_sys=True)
-    <BLANKLINE>
-        if _args:
-            sys.argv[:] = _args
-            __file__ = _args[0]
-            del _options, _args
-            execfile(__file__)
-    <BLANKLINE>
-    if _interactive:
-        del _interactive
-        __import__("code").interact(banner="", local=globals())
-
 Specifying entry points
 -----------------------
 
@@ -693,11 +585,11 @@
     <BLANKLINE>
     import sys
     sys.path[0:0] = [
-      '/sample-buildout/eggs/demo-0.4c1-py2.4.egg',
-      '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
-      '/foo/bar',
-      '/sample-buildout/spam',
-      ]
+        '/sample-buildout/eggs/demo-0.4c1-py2.4.egg',
+        '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
+        '/foo/bar',
+        '/sample-buildout/spam',
+        ]
     <BLANKLINE>
     <BLANKLINE>
     import foo.bar
@@ -753,217 +645,3 @@
     Installing demo.
     Generated script '/sample-buildout/bin/foo'.
 
-Interpreter generation
-----------------------
-
-The interpreter described above is a script that mimics an
-interpreter--it has support for only a limited number of command-line
-options. What if you want a more full-featured interpreter?
-
-The interpreter recipe generates a full-fledged version.  Here's an example.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... """
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... """ % dict(server=link_server))
-
-    >>> print system(buildout),
-    Uninstalling demo.
-    Installing py.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-Notice that the recipe took the name of the interpreter from the name of the
-section.
-
-The bin/py script now just restarts Python after specifying a special
-path in PYTHONPATH.
-
-    >>> cat(sample_buildout, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
-    #!/usr/bin/python2.4 -S
-    <BLANKLINE>
-    import os
-    import sys
-    <BLANKLINE>
-    argv = [sys.executable] + sys.argv[1:]
-    environ = os.environ.copy()
-    path = '/sample-buildout/parts/py'
-    if environ.get('PYTHONPATH'):
-        path = os.pathsep.join([path, environ['PYTHONPATH']])
-    environ['PYTHONPATH'] = path
-    os.execve(sys.executable, argv, environ)
-
-The path is a directory that contains two files: our own site.py and
-sitecustomize.py.
-
-    >>> ls(sample_buildout, 'parts', 'py')
-    -  site.py
-    -  sitecustomize.py
-
-    >>> cat(sample_buildout, 'parts', 'py', 'site.py')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    import sys
-    sys.path[0:0] = [
-      '/sample-buildout/eggs/demo-0.2-py2.4.egg',
-      '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg',
-      ]
-    import sitecustomize
-
-    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
-
-Here's an example of using the generated interpreter.
-
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
-    ['',
-     '/sample-buildout/eggs/demo-0.2-py2.4.egg',
-     '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg']
-    <BLANKLINE>
-
-The interpreter recipe takes several options.  First, here's the list of the
-options that overlap from the scripts recipe.  After this, we'll list the new
-options and describe them.
-
-* eggs
-* find-links
-* index
-* python
-* extra-paths
-* initialization
-* relative-paths
-* include-site-packages
-
-In addition to these, the interpreter script offers these three new options.
-
-extends
-    You can extend another section using this value.  It is intended to be
-    used by extending a section that uses this package's scripts recipe.
-    In this manner, you can avoid repeating yourself.
-
-include-site-customization
-    Normally the Python's real sitecustomize module is not processed.
-    If you want it to be processed, set this value to 'true'.  This will
-    be honored irrespective of the setting for include-site-paths.
-
-name
-    If you do not want to have the interpreter have the same name as the
-    section, you can set it explicitly with this option.
-
-Let's look at the ``extends`` option first.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... """
-    ... [buildout]
-    ... parts = demo python
-    ...
-    ... [demo]
-    ... recipe = zc.recipe.egg
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ...
-    ... [python]
-    ... recipe = zc.recipe.egg:interpreter
-    ... extends = demo
-    ... """ % dict(server=link_server))
-
-That's not quite as short as adding an "interpreter = py" option to the
-[demo] section, but an improvement over what it could be.
-
-Now let's put it in action.
-
-    >>> print system(buildout),
-    Uninstalling py.
-    Installing demo.
-    Generated script '/sample-buildout/bin/demo'.
-    Installing python.
-    Generated interpreter '/sample-buildout/bin/python'.
-
-    >>> print system(join(sample_buildout, 'bin', 'python') +
-    ...              ' -c "import sys, pprint; pprint.pprint(sys.path[:3])"')
-    ['',
-     '/sample-buildout/eggs/demo-0.2-py2.4.egg',
-     '/sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg']
-    <BLANKLINE>
-
-Note that the parts/py directory has been cleaned up, and parts/python has
-been created.
-
-    >>> ls(sample_buildout, 'parts')
-    d  python
-
-Now let's use the include-site-customization option.  It simply lets Python's
-underlying sitecustomize module, if it exists, be executed.
-
-To show this, we need a Python executable guaranteed to have a sitecustomize
-module.  We'll make one.  The os.environ change below will go into the
-sitecustomize.  We'll be able to use that as a flag.
-
-    >>> py_path, site_packages_path = make_py(initialization='''\
-    ... import os
-    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
-    ... ''')
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... """
-    ... [buildout]
-    ... parts = py
-    ... executable = %(py_path)s
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... include-site-customization = true
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... """ % dict(server=link_server, py_path=py_path))
-
-    >>> print system(buildout),
-    Uninstalling python.
-    Uninstalling demo.
-    Installing py.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    execfile('/executable_buildout/parts/py/sitecustomize.py')
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import os; print os.environ['zc.buildout']"''')
-    foo bar baz shazam
-    <BLANKLINE>
-
-The last new option is ``name``.  This simply changes the name of the
-interpreter, so that you are not forced to use the name of the section.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... """
-    ... [buildout]
-    ... parts = interpreter
-    ...
-    ... [interpreter]
-    ... name = python2
-    ... recipe = zc.recipe.egg:interpreter
-    ... include-site-customization = true
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... """ % dict(server=link_server))
-
-    >>> print system(buildout),
-    Uninstalling py.
-    Installing interpreter.
-    Generated interpreter '/sample-buildout/bin/python2'.
-
-    >>> print system(join(sample_buildout, 'bin', 'python2') +
-    ...              ' -c "print 42"')
-    42
-    <BLANKLINE>
-
-The other options have been described before for the scripts recipe, and so
-they will not be repeated here.

Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/__init__.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -1,2 +1,2 @@
-from zc.recipe.egg.egg import Egg, Scripts, Eggs, Interpreter
+from zc.recipe.egg.egg import Egg, Scripts, Eggs
 from zc.recipe.egg.custom import Custom, Develop

Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/api.txt
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/api.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -116,7 +116,6 @@
     executable = /usr/local/bin/python2.3
     extras = other
     find-links = http://localhost:27071/
-    include-site-packages = false
     index = http://localhost:27071/index
     python = buildout
     recipe = sample

Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/custom.txt
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/custom.txt	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/custom.txt	2010-02-03 15:41:04 UTC (rev 108736)
@@ -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/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/egg.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -128,27 +128,16 @@
             self._relative_paths = ''
             assert relative_paths == 'false'
 
-        value = options.setdefault(
-            'include-site-packages',
-            b_options.get('include-site-packages', 'false'))
-        if value not in ('true', 'false'):
-            raise zc.buildout.UserError(
-                "Invalid value for include-site-packages option: %s" %
-                (value,))
-        self.include_site_packages = (value == 'true')
-
-
-class Scripts(ScriptBase):
-
     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([
@@ -172,66 +161,30 @@
                     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,
-                import_site=self.include_site_packages,
-                )
-
+            return self._install(reqs, ws, scripts)
         return ()
 
     update = install
 
+    def _install(self, reqs, ws, scripts):
+        # Subclasses implement this.
+        raise NotImplementedError()
 
-class Interpreter(ScriptBase):
 
-    def __init__(self, buildout, name, options):
-        if 'extends' in options:
-            options.update(buildout[options['extends']])
-        super(Interpreter, self).__init__(buildout, name, options)
-        self.default_eggs = ''
-        b_options = buildout['buildout']
-        options['parts-directory'] = os.path.join(
-            b_options['parts-directory'], self.name)
+class Scripts(ScriptBase):
 
-        value = options.setdefault(
-            'include-site-customization',
-            b_options.get('include-site-customization', 'false'))
-        if value not in ('true', 'false'):
-            raise zc.buildout.UserError(
-                "Invalid value for include-site-customization option: %s" %
-                (value,))
-        self.include_site_customization = (value == 'true')
-
-        options.setdefault('name', name)
-
-    def install(self):
-        reqs, ws = self.working_set()
+    def _install(self, reqs, ws, scripts):
         options = self.options
-        if not os.path.exists(options['parts-directory']):
-            os.mkdir(options['parts-directory'])
-            dir_made = True
-        else:
-            dir_made = False
-        generated = zc.buildout.easy_install.interpreter(
-            options['name'], ws, options['executable'],
-            options['bin-directory'], options['parts-directory'],
+        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', ''),
-            relative_paths=self._relative_paths,
-            import_site=self.include_site_packages,
-            import_sitecustomize=self.include_site_customization,
+            arguments=options.get('arguments', ''),
+            relative_paths=self._relative_paths
             )
-        if dir_made:
-            generated.append(options['parts-directory'])
-        return generated
 
 
 def get_bool(options, name, default=False):

Modified: zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/tests.py
===================================================================
--- zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2010-02-03 15:23:10 UTC (rev 108735)
+++ zc.buildout/branches/gary-4/zc.recipe.egg_/src/zc/recipe/egg/tests.py	2010-02-03 15:41:04 UTC (rev 108736)
@@ -29,249 +29,6 @@
         return d
     return dirname(os.path.dirname(d), level-1)
 
-# We do not explicitly test the interpreter recipe support for the ``eggs``,
-# ``find-links``, and ``index`` options because they are used for most or
-# all of the examples.  The README tests ``extends``,
-# ``include-site-customization`` and ``name``.  That leaves ``python``,
-# ``extra-paths``, ``initialization``, ``relative-paths``, and
-# ``include-site-packages``.
-
-def interpreter_recipe_supports_python_option():
-    """
-This simply shows that the ``python`` option can specify another section to
-find the ``executable``.  (The ``python`` option defaults to looking in the
-``buildout`` section.)  We do this by creating a custom Python that will have
-some initialization that we can look for.
-
-    >>> py_path, site_packages_path = make_py(initialization='''
-    ... import os
-    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
-    ... ''')
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [custom_python]
-    ... executable = %(py_path)s
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... include-site-customization = true
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... python = custom_python
-    ... ''' % dict(server=link_server, py_path=py_path))
-
-    >>> print system(buildout),
-    Installing py.
-    Getting distribution for 'demo<0.3'.
-    Got demo 0.2.
-    Getting distribution for 'demoneeded'.
-    Got demoneeded 1.2c1.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
-    foo bar baz shazam
-"""
-
-def interpreter_recipe_supports_extra_paths_option():
-    """
-This shows that specifying extra-paths will affect sys.path.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... extra-paths =
-    ...    /foo/bar
-    ...    ${buildout:directory}/spam
-    ... ''' % dict(server=link_server))
-
-    >>> print system(buildout),
-    Installing py.
-    Generated interpreter '/sample-buildout/bin/py'.
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import sys;print 'path' + ' '.join(sys.path)"''')
-    ... # doctest:+ELLIPSIS
-    path.../foo/bar /sample-buildout/spam...
-
-"""
-
-def interpreter_recipe_supports_initialization_option():
-    """
-This simply shows that the ``initialization`` option can specify code to
-run on initialization.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... initialization =
-    ...     import os
-    ...     os.environ['zc.buildout'] = 'foo bar baz shazam'
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... ''' % dict(server=link_server))
-
-    >>> print system(buildout),
-    Installing py.
-    Getting distribution for 'demo<0.3'.
-    Got demo 0.2.
-    Getting distribution for 'demoneeded'.
-    Got demoneeded 1.2c1.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    <BLANKLINE>
-    import os
-    os.environ['zc.buildout'] = 'foo bar baz shazam'
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
-    foo bar baz shazam
-
-This also works with the include-site-customization option, processing local
-initialization, and then the Python's initialization.  We show this with a
-custom Python.
-
-    >>> py_path, site_packages_path = make_py(initialization='''
-    ... import os
-    ... os.environ['zc.buildout'] = 'foo bar baz shazam'
-    ... ''')
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [custom_python]
-    ... executable = %(py_path)s
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... initialization =
-    ...     import os
-    ...     os.environ['zc.recipe.egg'] = 'baLOOba'
-    ... include-site-customization = true
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... python = custom_python
-    ... ''' % dict(server=link_server, py_path=py_path))
-
-    >>> print system(buildout),
-    Uninstalling py.
-    Installing py.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> cat(sample_buildout, 'parts', 'py', 'sitecustomize.py')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    <BLANKLINE>
-    import os
-    os.environ['zc.recipe.egg'] = 'baLOOba'
-    execfile('/executable_buildout/parts/py/sitecustomize.py')
-
-    >>> print system(join(sample_buildout, 'bin', 'py') + ' -c ' +
-    ...              '''"import os; print os.environ['zc.recipe.egg']"'''),
-    baLOOba
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import os; print os.environ['zc.buildout']"'''),
-    foo bar baz shazam
-
-"""
-
-def interpreter_recipe_supports_relative_paths_option():
-    """
-This shows that the relative-paths option affects the code for inserting
-paths into sys.path.
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... relative-paths = true
-    ... extra-paths =
-    ...    /foo/bar
-    ...    ${buildout:directory}/spam
-    ... ''' % dict(server=link_server))
-
-    >>> print system(buildout),
-    Installing py.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-Let's look at the site.py that was generated:
-
-    >>> cat(sample_buildout, 'parts', 'py', 'site.py')
-    ... # doctest: +NORMALIZE_WHITESPACE
-    <BLANKLINE>
-    import os
-    <BLANKLINE>
-    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)
-    import sys
-    sys.path[0:0] = [
-      '/foo/bar',
-      join(base, 'spam'),
-      ]
-    import sitecustomize
-
-"""
-
-def interpreter_recipe_supports_include_site_packages_option():
-    """
-This option simply causes the executable's usual site.py to be processed.
-We'll demonstrate this by using a Python that has its own extra path.
-
-    >>> py_path, site_packages_path = make_py()
-
-    >>> write(sample_buildout, 'buildout.cfg',
-    ... '''
-    ... [buildout]
-    ... parts = py
-    ... executable = %(py_path)s
-    ...
-    ... [py]
-    ... recipe = zc.recipe.egg:interpreter
-    ... include-site-packages = true
-    ... eggs = demo<0.3
-    ... find-links = %(server)s
-    ... index = %(server)s/index
-    ... ''' % dict(server=link_server, py_path=py_path))
-
-    >>> print system(buildout),
-    Installing py.
-    Getting distribution for 'demo<0.3'.
-    Got demo 0.2.
-    Getting distribution for 'demoneeded'.
-    Got demoneeded 1.2c1.
-    Generated interpreter '/sample-buildout/bin/py'.
-
-    >>> print system(join(sample_buildout, 'bin', 'py') +
-    ...              ''' -c "import sys; print (%r in sys.path) or (%r, sys.path)"''' %
-    ...              (site_packages_path, site_packages_path)),
-    True
-"""
-
 def setUp(test):
     zc.buildout.tests.easy_install_SetUp(test)
     zc.buildout.testing.install_develop('zc.recipe.egg', test)
@@ -332,15 +89,6 @@
                (re.compile('extdemo[.]pyd'), 'extdemo.so')
                ]),
             ),
-        doctest.DocTestSuite(
-            setUp=setUp,
-            tearDown=zc.buildout.testing.buildoutTearDown,
-            checker=renormalizing.RENormalizing([
-                zc.buildout.testing.normalize_path,
-                zc.buildout.testing.normalize_endings,
-                zc.buildout.testing.normalize_egg_py,
-                ]),
-            ),
 
         ))
 



More information about the checkins mailing list