Python API for egg and script installation
==========================================

The easy_install module provides some functions to provide support for
egg and script installation.  It provides functionality at the python
level that is similar to easy_install, with a few exceptions:

- By default, we look for new packages *and* the packages that
  they depend on.  This is somewhat like (and uses) the --upgrade
  option of easy_install, except that we also upgrade required
  packages.

- If the highest-revision package satisfying a specification is
  already present, then we don't try to get another one.  This saves a
  lot of search time in the common case that packages are pegged to
  specific versions.

- If there is a develop egg that satisfies a requirement, we don't
  look for additional distributions.  We always give preference to
  develop eggs.

- Distutils options for building extensions can be passed.

Distribution installation
-------------------------

The easy_install module provides a function, install, for installing one
or more packages and their dependencies.  The install function takes 2
positional arguments:

- An iterable of setuptools requirement strings for the distributions
  to be installed, and

- A destination directory to install to and to satisfy requirements
  from.  The destination directory can be None, in which case, no new
  distributions are downloaded and there will be an error if the
  needed distributions can't be found among those already installed.

It supports a number of optional keyword arguments:

links
   A sequence of URLs, file names, or directories to look for
   links to distributions.

index
   The URL of an index server, or almost any other valid URL. :)

   If not specified, the Python Package Index,
   http://pypi.python.org/simple/, is used.  You can specify an
   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
   will make our examples run a little bit faster.

executable
   A path to a Python executable.  Distributions will be installed
   using this executable and will be for the matching Python version.

path
   A list of additional directories to search for locally-installed
   distributions.

always_unzip
   A flag indicating that newly-downloaded distributions should be
   directories even if they could be installed as zip files.

working_set
   An existing working set to be augmented with additional
   distributions, if necessary to satisfy requirements.  This allows
   you to call install multiple times, if necessary, to gather
   multiple sets of requirements.

newest
   A boolean value indicating whether to search for new distributions
   when already-installed distributions meet the requirement.  When
   this is true, the default, and when the destination directory is
   not None, then the install function will search for the newest
   distributions that satisfy the requirements.

versions
   A dictionary mapping project names to version numbers to be used
   when selecting distributions.  This can be used to specify a set of
   distribution versions independent of other requirements.

use_dependency_links
   A flag indicating whether to search for dependencies using the
   setup dependency_links metadata or not. If true, links are searched
   for using dependency_links in preference to other
   locations. Defaults to true.

include_site_packages
    A flag indicating whether Python's non-standard-library packages should
    be available for finding dependencies.  Defaults to true.

    Paths outside of Python's standard library--or more precisely, those that
    are not included when Python is started with the -S argument--are loosely
    referred to as "site-packages" here.

relative_paths
   Adjust egg paths so they are relative to the script path.  This
   allows scripts to work when scripts and eggs are moved, as long as
   they are both moved in the same way.

The install method returns a working set containing the distributions
needed to meet the given requirements.

We have a link server that has a number of eggs:

    >>> print get(link_server),
    <html><body>
    <a href="bigdemo-0.1-py2.4.egg">bigdemo-0.1-py2.4.egg</a><br>
    <a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
    <a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
    <a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
    <a href="demo-0.4c1-py2.4.egg">demo-0.4c1-py2.4.egg</a><br>
    <a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
    <a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
    <a href="demoneeded-1.2c1.zip">demoneeded-1.2c1.zip</a><br>
    <a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
    <a href="index/">index/</a><br>
    <a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
    </body></html>

Let's make a directory and install the demo egg to it, using the demo:

    >>> dest = tmpdir('sample-install')
    >>> import zc.buildout.easy_install
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo==0.2'], dest,
    ...     links=[link_server], index=link_server+'index/')

We requested version 0.2 of the demo distribution to be installed into
the destination server.  We specified that we should search for links
on the link server and that we should use the (empty) link server
index directory as a package index.

The working set contains the distributions we retrieved.

    >>> for dist in ws:
    ...     print dist
    demo 0.2
    demoneeded 1.1

We got demoneeded because it was a dependency of demo.

And the actual eggs were added to the eggs directory.

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    -  demoneeded-1.1-py2.4.egg

If we remove the version restriction on demo, but specify a false
value for newest, no new distributions will be installed:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     newest=False)
    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    -  demoneeded-1.1-py2.4.egg

If we leave off the newest option, we'll get an update for demo:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/')
    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    -  demo-0.3-py2.4.egg
    -  demoneeded-1.1-py2.4.egg

Note that we didn't get the newest versions available.  There were
release candidates for newer versions of both packages. By default,
final releases are preferred.  We can change this behavior using the
prefer_final function:

    >>> zc.buildout.easy_install.prefer_final(False)
    True

The old setting is returned.

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/')
    >>> for dist in ws:
    ...     print dist
    demo 0.4c1
    demoneeded 1.2c1

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    -  demo-0.3-py2.4.egg
    -  demo-0.4c1-py2.4.egg
    -  demoneeded-1.1-py2.4.egg
    -  demoneeded-1.2c1-py2.4.egg

Let's put the setting back to the default.

    >>> zc.buildout.easy_install.prefer_final(True)
    False

We can supply additional distributions.  We can also supply
specifications for distributions that would normally be found via
dependencies.  We might do this to specify a specific version.

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo', 'other', 'demoneeded==1.0'], dest,
    ...     links=[link_server], index=link_server+'index/')

    >>> for dist in ws:
    ...     print dist
    demo 0.3
    other 1.0
    demoneeded 1.0

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    -  demo-0.3-py2.4.egg
    -  demo-0.4c1-py2.4.egg
    -  demoneeded-1.0-py2.4.egg
    -  demoneeded-1.1-py2.4.egg
    -  demoneeded-1.2c1-py2.4.egg
    d  other-1.0-py2.4.egg

We can request that eggs be unzipped even if they are zip safe.  This
can be useful when debugging.  (Note that Distribute will unzip eggs by
default, so if you are using Distribute, most or all eggs will already be
unzipped without this flag.)

    >>> rmdir(dest)
    >>> dest = tmpdir('sample-install')
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)

    >>> ls(dest)
    d  demo-0.3-py2.4.egg
    d  demoneeded-1.1-py2.4.egg

    >>> rmdir(dest)
    >>> dest = tmpdir('sample-install')
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     always_unzip=False)

    >>> ls(dest)
    -  demo-0.3-py2.4.egg
    -  demoneeded-1.1-py2.4.egg

We can also set a default by calling the always_unzip function:

    >>> zc.buildout.easy_install.always_unzip(True)
    False

The old default is returned:

    >>> rmdir(dest)
    >>> dest = tmpdir('sample-install')
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/')

    >>> ls(dest)
    d  demo-0.3-py2.4.egg
    d  demoneeded-1.1-py2.4.egg


    >>> zc.buildout.easy_install.always_unzip(False)
    True

    >>> rmdir(dest)
    >>> dest = tmpdir('sample-install')
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/')

    >>> ls(dest)
    -  demo-0.3-py2.4.egg
    -  demoneeded-1.1-py2.4.egg

    >>> rmdir(dest)
    >>> dest = tmpdir('sample-install')
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)

    >>> ls(dest)
    d  demo-0.3-py2.4.egg
    d  demoneeded-1.1-py2.4.egg

Specifying version information independent of requirements
----------------------------------------------------------

Sometimes it's useful to specify version information independent of
normal requirements specifications.  For example, a buildout may need
to lock down a set of versions, without having to put put version
numbers in setup files or part definitions.  If a dictionary is passed
to the install function, mapping project names to version numbers,
then the versions numbers will be used.

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     versions = dict(demo='0.2', demoneeded='1.0'))
    >>> [d.version for d in ws]
    ['0.2', '1.0']

In this example, we specified a version for demoneeded, even though we
didn't define a requirement for it.  The versions specified apply to
dependencies as well as the specified requirements.

If we specify a version that's incompatible with a requirement, then
we'll get an error:

    >>> from zope.testing.loggingsupport import InstalledHandler
    >>> handler = InstalledHandler('zc.buildout.easy_install')
    >>> import logging
    >>> logging.getLogger('zc.buildout.easy_install').propagate = False

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo >0.2'], dest, links=[link_server],
    ...     index=link_server+'index/',
    ...     versions = dict(demo='0.2', demoneeded='1.0'))
    Traceback (most recent call last):
    ...
    IncompatibleVersionError: Bad version 0.2

    >>> print handler
    zc.buildout.easy_install DEBUG
      Installing 'demo >0.2'.
    zc.buildout.easy_install ERROR
      The version, 0.2, is not consistent with the requirement, 'demo>0.2'.

    >>> handler.clear()

If no versions are specified, a debugging message will be output
reporting that a version was picked automatically:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     )

    >>> print handler
    zc.buildout.easy_install DEBUG
      Installing 'demo'.
    zc.buildout.easy_install DEBUG
      We have the best distribution that satisfies 'demo'.
    zc.buildout.easy_install DEBUG
      Picked: demo = 0.3
    zc.buildout.easy_install DEBUG
      Getting required 'demoneeded'
    zc.buildout.easy_install DEBUG
        required by demo 0.3.
    zc.buildout.easy_install DEBUG
      We have the best distribution that satisfies 'demoneeded'.
    zc.buildout.easy_install DEBUG
      Picked: demoneeded = 1.1

    >>> handler.uninstall()
    >>> logging.getLogger('zc.buildout.easy_install').propagate = True

We can request that we get an error if versions are picked:

    >>> zc.buildout.easy_install.allow_picked_versions(False)
    True

(The old setting is returned.)

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     )
    Traceback (most recent call last):
    ...
    UserError: Picked: demo = 0.3

    >>> zc.buildout.easy_install.allow_picked_versions(True)
    False

The function default_versions can be used to get and set default
version information to be used when no version information is passes.
If called with an argument, it sets the default versions:

    >>> zc.buildout.easy_install.default_versions(dict(demoneeded='1'))
    {}

It always returns the previous default versions.  If called without an
argument, it simply returns the default versions without changing
them:

    >>> zc.buildout.easy_install.default_versions()
    {'demoneeded': '1'}

So with the default versions set, we'll get the requested version even
if the versions option isn't used:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     )

    >>> [d.version for d in ws]
    ['0.3', '1.0']

Of course, we can unset the default versions by passing an empty
dictionary:

    >>> zc.buildout.easy_install.default_versions({})
    {'demoneeded': '1'}

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest, links=[link_server], index=link_server+'index/',
    ...     )

    >>> [d.version for d in ws]
    ['0.3', '1.1']

Dependencies in Site Packages
-----------------------------

Paths outside of Python's standard library--or more precisely, those that are
not included when Python is started with the -S argument--are loosely referred
to as "site-packages" here.  These site-packages are searched by default for
distributions.  This can be disabled, so that, for instance, a system Python
can be used with buildout, cleaned of any packages installed by a user or
system package manager.

The default behavior can be controlled and introspected using
zc.buildout.easy_install.include_site_packages.

    >>> zc.buildout.easy_install.include_site_packages()
    True

Here's an example of using a Python executable that includes our dependencies.

Our "py_path" will have the "demoneeded," and "demo" packages available.
 We'll simply be asking for "demoneeded" here, but without any external
 index or links.

    >>> from zc.buildout.tests import create_sample_sys_install
    >>> py_path, site_packages_path = make_py()
    >>> create_sample_sys_install(site_packages_path)

    >>> example_dest = tmpdir('site-packages-example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
    ...     index=None)
    >>> [dist.project_name for dist in workingset]
    ['demoneeded']

That worked fine.  Let's try again with site packages not allowed.  We'll
change the policy by changing the default.  Notice that the function for
changing the default value returns the previous value.

    >>> zc.buildout.easy_install.include_site_packages(False)
    True

    >>> zc.buildout.easy_install.include_site_packages()
    False

    >>> zc.buildout.easy_install.clear_index_cache()
    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('site-packages-example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['demoneeded'], example_dest, links=[], executable=py_path,
    ...     index=None)
    Traceback (most recent call last):
        ...
    MissingDistribution: Couldn't find a distribution for 'demoneeded'.
    >>> zc.buildout.easy_install.clear_index_cache()

Now we'll reset the default.

    >>> zc.buildout.easy_install.include_site_packages(True)
    False

    >>> zc.buildout.easy_install.include_site_packages()
    True

Dependency links
----------------

Setuptools allows metadata that describes where to search for package
dependencies. This option is called dependency_links. Buildout has its
own notion of where to look for dependencies, but it also uses the
setup tools dependency_links information if it's available.

Let's demo this by creating an egg that specifies dependency_links.

To begin, let's create a new egg repository. This repository hold a
newer version of the 'demoneeded' egg than the sample repository does.

    >>> repoloc = tmpdir('repo')
    >>> from zc.buildout.tests import create_egg
    >>> create_egg('demoneeded', '1.2', repoloc)
    >>> link_server2 = start_server(repoloc)

Turn on logging on this server so that we can see when eggs are pulled
from it.

    >>> get(link_server2 + 'enable_server_logging')
    GET 200 /enable_server_logging
    ''

Now we can create an egg that specifies that its dependencies are
found on this server.

    >>> repoloc = tmpdir('repo2')
    >>> create_egg('hasdeps', '1.0', repoloc,
    ...            install_requires = "'demoneeded'",
    ...            dependency_links = [link_server2])

Let's add the egg to another repository.

    >>> link_server3 = start_server(repoloc)

Now let's install the egg.

    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest,
    ...     links=[link_server3], index=link_server3+'index/')
    GET 200 /
    GET 200 /demoneeded-1.2-pyN.N.egg

The server logs show that the dependency was retrieved from the server
specified in the dependency_links.

Now let's see what happens if we provide two different ways to retrieve
the dependencies.

    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest, index=link_server+'index/',
    ...     links=[link_server, link_server3])
    GET 200 /
    GET 200 /demoneeded-1.2-pyN.N.egg

Once again the dependency is fetched from the logging server even
though it is also available from the non-logging server. This is
because the version on the logging server is newer and buildout
normally chooses the newest egg available.

If you wish to control where dependencies come from regardless of
dependency_links setup metadata use the 'use_dependency_links' option
to zc.buildout.easy_install.install().

    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest, index=link_server+'index/',
    ...     links=[link_server, link_server3],
    ...     use_dependency_links=False)

Notice that this time the dependency egg is not fetched from the
logging server. When you specify not to use dependency_links, eggs
will only be searched for using the links you explicitly provide.

Another way to control this option is with the
zc.buildout.easy_install.use_dependency_links() function. This
function sets the default behavior for the zc.buildout.easy_install()
function.

    >>> zc.buildout.easy_install.use_dependency_links(False)
    True

The function returns its previous setting.

    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest, index=link_server+'index/',
    ...     links=[link_server, link_server3])

It can be overridden by passing a keyword argument to the install
function.

    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest, index=link_server+'index/',
    ...     links=[link_server, link_server3],
    ...	    use_dependency_links=True)
    GET 200 /demoneeded-1.2-pyN.N.egg

To return the dependency_links behavior to normal call the function again.

    >>> zc.buildout.easy_install.use_dependency_links(True)
    False
    >>> rmdir(example_dest)
    >>> example_dest = tmpdir('example-install')
    >>> workingset = zc.buildout.easy_install.install(
    ...     ['hasdeps'], example_dest, index=link_server+'index/',
    ...     links=[link_server, link_server3])
    GET 200 /demoneeded-1.2-pyN.N.egg


Script generation
-----------------

The easy_install module provides support for creating scripts from eggs.
It provides two competing functions.  One, ``scripts``, is a
well-established approach to generating reliable scripts with a "clean"
Python--e.g., one that does not have any packages in its site-packages.
The other, ``sitepackage_safe_scripts``, is newer, a bit trickier, and is
designed to work with a Python that has code in its site-packages, such
as a system Python.

Both are similar to setuptools except that they provides facilities for
baking a script's path into the script.  This has two advantages:

- The eggs to be used by a script are not chosen at run time, making
  startup faster and, more importantly, deterministic.

- The script doesn't have to import pkg_resources because the logic that
  pkg_resources would execute at run time is executed at script-creation
  time.  (There is an exception in ``sitepackage_safe_scripts`` if you
  want to have your Python's site packages available, as discussed
  below, but even in that case pkg_resources is only partially
  activated, which can be a significant time savings.)


The ``scripts`` function
~~~~~~~~~~~~~~~~~~~~~~~~

The ``scripts`` function is the first way to generate scripts that we'll
examine. It is the earlier approach that the package offered.  Let's
create a destination directory for it to place them in:

    >>> bin = tmpdir('bin')

Now, we'll use the scripts function to generate scripts in this directory
from the demo egg:

    >>> import sys
    >>> scripts = zc.buildout.easy_install.scripts(
    ...     ['demo'], ws, sys.executable, bin)

the four arguments we passed were:

1. A sequence of distribution requirements.  These are of the same
   form as setuptools requirements.  Here we passed a single
   requirement, for the version 0.1 demo distribution.

2. A working set,

3. The Python executable to use, and

3. The destination directory.

The bin directory now contains a generated script:

    >>> ls(bin)
    -  demo

The return value is a list of the scripts generated:

    >>> import os, sys
    >>> if sys.platform == 'win32':
    ...     scripts == [os.path.join(bin, 'demo.exe'),
    ...                 os.path.join(bin, 'demo-script.py')]
    ... else:
    ...     scripts == [os.path.join(bin, 'demo')]
    True

Note that 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.

The demo script run the entry point defined in the demo egg:

    >>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
      '/sample-install/demo-0.3-py2.4.egg',
      '/sample-install/demoneeded-1.1-py2.4.egg',
      ]
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main()

Some things to note:

- The demo and demoneeded eggs are added to the beginning of sys.path.

- The module for the script entry point is imported and the entry
  point, in this case, 'main', is run.

Rather than requirement strings, you can pass tuples containing 3
strings:

  - A script name,

  - A module,

  - An attribute expression for an entry point within the module.

For example, we could have passed entry point information directly
rather than passing a requirement:

    >>> scripts = zc.buildout.easy_install.scripts(
    ...     [('demo', 'eggrecipedemo', 'main')],
    ...     ws, sys.executable, bin)

    >>> cat(bin, 'demo') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
      '/sample-install/demo-0.3-py2.4.egg',
      '/sample-install/demoneeded-1.1-py2.4.egg',
      ]
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main()

Passing entry-point information directly is handy when using eggs (or
distributions) that don't declare their entry points, such as
distributions that aren't based on setuptools.

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
run other scripts with the path set on the working set:

    >>> scripts = zc.buildout.easy_install.scripts(
    ...     ['demo'], ws, sys.executable, bin, interpreter='py')


    >>> ls(bin)
    -  demo
    -  py

    >>> if sys.platform == 'win32':
    ...     scripts == [os.path.join(bin, 'demo.exe'),
    ...                 os.path.join(bin, 'demo-script.py'),
    ...                 os.path.join(bin, 'py.exe'),
    ...                 os.path.join(bin, 'py-script.py')]
    ... else:
    ...     scripts == [os.path.join(bin, 'demo'),
    ...                 os.path.join(bin, 'py')]
    True

The py script simply runs the Python interactive interpreter with
the path set:

    >>> cat(bin, 'py') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import sys
    <BLANKLINE>
    sys.path[0:0] = [
      '/sample-install/demo-0.3-pyN.N.egg',
      '/sample-install/demoneeded-1.1-pyN.N.egg',
      ]
    <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())

If invoked with a script name and arguments, it will run that script, instead.

    >>> write('ascript', '''
    ... "demo doc"
    ... print sys.argv
    ... print (__name__, __file__, __doc__)
    ... ''')
    >>> print system(join(bin, 'py')+' ascript a b c'),
    ['ascript', 'a', 'b', 'c']
    ('__main__', 'ascript', 'demo doc')

For Python 2.5 and higher, you can also use the -m option to run a
module:

    >>> print system(join(bin, 'py')+' -m pdb'),
    usage: pdb.py scriptfile [arg] ...

    >>> print system(join(bin, 'py')+' -m pdb what'),
    Error: what does not exist

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.

    >>> bin = tmpdir('bin2')
    >>> scripts = zc.buildout.easy_install.scripts(
    ...    ['demo'], ws, sys.executable, bin, dict(demo='run'))

    >>> if sys.platform == 'win32':
    ...     scripts == [os.path.join(bin, 'run.exe'),
    ...                 os.path.join(bin, 'run-script.py')]
    ... else:
    ...     scripts == [os.path.join(bin, 'run')]
    True
    >>> ls(bin)
    -  run

    >>> print system(os.path.join(bin, 'run')),
    3 1

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:

    >>> foo = tmpdir('foo')
    >>> scripts = zc.buildout.easy_install.scripts(
    ...    ['demo'], ws, sys.executable, bin, dict(demo='run'),
    ...    extra_paths=[foo])

    >>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
      '/sample-install/demo-0.3-py2.4.egg',
      '/sample-install/demoneeded-1.1-py2.4.egg',
      '/foo',
      ]
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main()

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
parentheses in the call:

    >>> scripts = zc.buildout.easy_install.scripts(
    ...    ['demo'], ws, sys.executable, bin, dict(demo='run'),
    ...    arguments='1, 2')

    >>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    import sys
    sys.path[0:0] = [
      '/sample-install/demo-0.3-py2.4.egg',
      '/sample-install/demoneeded-1.1-py2.4.egg',
      ]
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main(1, 2)

The ``scripts`` function: Passing initialization code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can also pass script initialization code:

    >>> scripts = zc.buildout.easy_install.scripts(
    ...    ['demo'], ws, sys.executable, bin, dict(demo='run'),
    ...    arguments='1, 2',
    ...    initialization='import os\nos.chdir("foo")')

    >>> cat(bin, 'run') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    import sys
    sys.path[0:0] = [
      '/sample-install/demo-0.3-py2.4.egg',
      '/sample-install/demoneeded-1.1-py2.4.egg',
      ]
    <BLANKLINE>
    import os
    os.chdir("foo")
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main(1, 2)

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
control this using the relative_paths option to install.  You need
to pass a common base directory of the scripts and eggs:

    >>> bo = tmpdir('bo')
    >>> ba = tmpdir('ba')
    >>> mkdir(bo, 'eggs')
    >>> mkdir(bo, 'bin')
    >>> mkdir(bo, 'other')

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], join(bo, 'eggs'), links=[link_server],
    ...     index=link_server+'index/')

    >>> scripts = zc.buildout.easy_install.scripts(
    ...    ['demo'], ws, sys.executable, join(bo, 'bin'), dict(demo='run'),
    ...    extra_paths=[ba, join(bo, 'bar')],
    ...    interpreter='py',
    ...    relative_paths=bo)

    >>> cat(bo, 'bin', 'run') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import os
    <BLANKLINE>
    join = os.path.join
    base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
    base = os.path.dirname(base)
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
      join(base, 'eggs/demo-0.3-pyN.N.egg'),
      join(base, 'eggs/demoneeded-1.1-pyN.N.egg'),
      '/ba',
      join(base, 'bar'),
      ]
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main()

Note that the extra path we specified that was outside the directory
passed as relative_paths wasn't converted to a relative path.

Of course, running the script works:

    >>> print system(join(bo, 'bin', 'run')),
    3 1

We specified an interpreter and its paths are adjusted too:

    >>> cat(bo, 'bin', 'py') # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import os
    <BLANKLINE>
    join = os.path.join
    base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
    base = os.path.dirname(base)
    <BLANKLINE>
    import sys
    <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'),
      ]
    <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())

The ``sitepackage_safe_scripts`` function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The newer function for creating scripts is ``sitepackage_safe_scripts``.
It has the same basic functionality as the ``scripts`` function: it can
create scripts to run arbitrary entry points, and to run a Python
interpreter.  The following are the differences from a user's
perspective.

- It can be used safely with a Python that has packages installed itself,
  such as a system-installed Python.

- In contrast to the interpreter generated by the ``scripts`` method, which
  supports only a small subset of the usual Python executable's options,
  the interpreter generated by ``sitepackage_safe_scripts`` supports all
  of them. This makes it possible to use as full Python replacement for
  scripts that need the distributions specified in your buildout.

- Both the interpreter and the entry point scripts allow you to include the
  site packages, and/or the sitecustomize, of the Python executable, if
  desired.

It works by creating site.py and sitecustomize.py files that set up the
desired paths and initialization.  These must be placed within an otherwise
empty directory.  Typically this is in a recipe's parts directory.

Here's the simplest example, building an interpreter script.

    >>> interpreter_dir = tmpdir('interpreter')
    >>> interpreter_parts_dir = os.path.join(
    ...     interpreter_dir, 'parts', 'interpreter')
    >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
    >>> mkdir(interpreter_bin_dir)
    >>> mkdir(interpreter_dir, 'eggs')
    >>> mkdir(interpreter_dir, 'parts')
    >>> mkdir(interpreter_parts_dir)

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
    ...     index=link_server+'index/')
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py')

Depending on whether the machine being used is running Windows or not, this
produces either three or four files.  In both cases, we have site.py and
sitecustomize.py generated in the parts/interpreter directory.  For Windows,
we have py.exe and py-script.py; for other operating systems, we have py.

    >>> sitecustomize_path = os.path.join(
    ...     interpreter_parts_dir, 'sitecustomize.py')
    >>> site_path = os.path.join(interpreter_parts_dir, 'site.py')
    >>> interpreter_path = os.path.join(interpreter_bin_dir, 'py')
    >>> if sys.platform == 'win32':
    ...     py_path = os.path.join(interpreter_bin_dir, 'py-script.py')
    ...     expected = [sitecustomize_path,
    ...                 site_path,
    ...                 os.path.join(interpreter_bin_dir, 'py.exe'),
    ...                 py_path]
    ... else:
    ...     py_path = interpreter_path
    ...     expected = [sitecustomize_path, site_path, py_path]
    ...
    >>> assert generated == expected, repr((generated, expected))

We didn't ask for any initialization, and we didn't ask to use the underlying
sitecustomization, so sitecustomize.py is empty.

    >>> cat(sitecustomize_path)

The interpreter script is simple.  It puts the directory with the
site.py and sitecustomize.py on the PYTHONPATH and (re)starts Python.

    >>> cat(py_path)
    #!/usr/bin/python -S
    import os
    import sys
    <BLANKLINE>
    argv = [sys.executable] + sys.argv[1:]
    environ = os.environ.copy()
    path = '/interpreter/parts/interpreter'
    if environ.get('PYTHONPATH'):
        path = os.pathsep.join([path, environ['PYTHONPATH']])
    environ['PYTHONPATH'] = path
    os.execve(sys.executable, argv, environ)

The site.py file is a modified version of the underlying Python's site.py.
The most important modification is that it has a different version of the
addsitepackages function.  It sets up the Python path, similarly to the
behavior of the function it replaces.  The following shows the part that
buildout inserts, in the simplest case.

    >>> sys.stdout.write('#\n'); cat(site_path)
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        buildout_paths = [
            '/interpreter/eggs/demo-0.3-pyN.N.egg',
            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
            ]
        for path in buildout_paths:
            sitedir, sitedircase = makepath(path)
            if not sitedircase in known_paths and os.path.exists(sitedir):
                sys.path.append(sitedir)
                known_paths.add(sitedircase)
        return known_paths
    <BLANKLINE>
    def original_addsitepackages(known_paths):...

Here are some examples of the interpreter in use.

    >>> print call_py(interpreter_path, "print 16+26")
    42
    <BLANKLINE>
    >>> res = call_py(interpreter_path, "import sys; print sys.path")
    >>> print res # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '/interpreter/eggs/demo-0.3-pyN.N.egg',
     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
    <BLANKLINE>
    >>> clean_paths = eval(res.strip()) # This is used later for comparison.

If you provide initialization, it goes in sitecustomize.py.

    >>> def reset_interpreter():
    ...     # This is necessary because, in our tests, the timestamps of the
    ...     # .pyc files are not outdated when we want them to be.
    ...     rmdir(interpreter_bin_dir)
    ...     mkdir(interpreter_bin_dir)
    ...     rmdir(interpreter_parts_dir)
    ...     mkdir(interpreter_parts_dir)
    ...
    >>> reset_interpreter()

    >>> initialization_string = """\
    ... import os
    ... os.environ['FOO'] = 'bar baz bing shazam'"""
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', initialization=initialization_string)
    >>> cat(sitecustomize_path)
    import os
    os.environ['FOO'] = 'bar baz bing shazam'
    >>> print call_py(interpreter_path, "import os; print os.environ['FOO']")
    bar baz bing shazam
    <BLANKLINE>

If you use relative paths, this affects the interpreter and site.py.  (This is
again the UNIX version; the Windows version uses subprocess instead of
os.execve.)

    >>> reset_interpreter()
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', relative_paths=interpreter_dir)
    >>> cat(py_path)
    #!/usr/bin/python -S
    import os
    import sys
    <BLANKLINE>
    join = os.path.join
    base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
    base = os.path.dirname(base)
    <BLANKLINE>
    argv = [sys.executable] + sys.argv[1:]
    environ = os.environ.copy()
    path = join(base, 'parts/interpreter')
    if environ.get('PYTHONPATH'):
        path = os.pathsep.join([path, environ['PYTHONPATH']])
    environ['PYTHONPATH'] = path
    os.execve(sys.executable, argv, environ)

For site.py, we again show only the pertinent parts.  Notice that the egg
paths join a base to a path, as with the use of this argument in the
``scripts`` function.

    >>> sys.stdout.write('#\n'); cat(site_path) # doctest: +ELLIPSIS
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        join = os.path.join
        base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
        base = os.path.dirname(base)
        base = os.path.dirname(base)
        buildout_paths = [
            join(base, 'eggs/demo-0.3-pyN.N.egg'),
            join(base, 'eggs/demoneeded-1.1-pyN.N.egg')
            ]...

The paths resolve in practice as you would expect.

    >>> print call_py(interpreter_path,
    ...               "import sys, pprint; pprint.pprint(sys.path)")
    ... # doctest: +ELLIPSIS
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '/interpreter/eggs/demo-0.3-pyN.N.egg',
     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
    <BLANKLINE>

The ``extra_paths`` argument affects the path in site.py.  Notice that
/interpreter/other is added after the eggs.

    >>> reset_interpreter()
    >>> mkdir(interpreter_dir, 'other')
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', extra_paths=[join(interpreter_dir, 'other')])
    >>> sys.stdout.write('#\n'); cat(site_path) # doctest: +ELLIPSIS
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        buildout_paths = [
            '/interpreter/eggs/demo-0.3-pyN.N.egg',
            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
            '/interpreter/other'
            ]...

    >>> print call_py(interpreter_path,
    ...               "import sys, pprint; pprint.pprint(sys.path)")
    ... # doctest: +ELLIPSIS
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '/interpreter/eggs/demo-0.3-pyN.N.egg',
     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
     '/interpreter/other']
    <BLANKLINE>

The ``sitepackage_safe_scripts`` function: using site-packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``sitepackage_safe_scripts`` function supports including site
packages.  This has some advantages and some serious dangers.

A typical reason to include site-packages is that it is easier to
install one or more dependencies in your Python than it is with
buildout.  Some packages, such as lxml or Python PostgreSQL integration,
have dependencies that can be much easier to build and/or install using
other mechanisms, such as your operating system's package manager.  By
installing some core packages into your Python's site-packages, this can
significantly simplify some application installations.

However, doing this has a significant danger.  One of the primary goals
of buildout is to provide repeatability.  Some packages (one of the
better known Python openid packages, for instance) change their behavior
depending on what packages are available.  If Python curl bindings are
available, these may be preferred by the library.  If a certain XML
package is installed, it may be preferred by the library.  These hidden
choices may cause small or large behavior differences.  The fact that
they can be rarely encountered can actually make it worse: you forget
that this might be a problem, and debugging the differences can be
difficult.  If you allow site-packages to be included in your buildout,
and the Python you use is not managed precisely by your application (for
instance, it is a system Python), you open yourself up to these
possibilities.  Don't be unaware of the dangers.

That explained, let's see how it works.  If you don't use namespace packages,
this is very straightforward.

    >>> reset_interpreter()
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', include_site_packages=True)
    >>> sys.stdout.write('#\n'); cat(site_path)
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        setuptools_path = None
        buildout_paths = [
            '/interpreter/eggs/demo-0.3-pyN.N.egg',
            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
            ]
        for path in buildout_paths:
            sitedir, sitedircase = makepath(path)
            if not sitedircase in known_paths and os.path.exists(sitedir):
                sys.path.append(sitedir)
                known_paths.add(sitedircase)
        sys.__egginsert = len(buildout_paths) # Support distribute.
        original_paths = [
            ...
            ]
        for path in original_paths:
            if path == setuptools_path or path not in known_paths:
                addsitedir(path, known_paths)
        return known_paths
    <BLANKLINE>
    def original_addsitepackages(known_paths):...

It simply adds the original paths using addsitedir after the code to add the
buildout paths.

Here's an example of the new script in use.  Other documents and tests in
this package give the feature a more thorough workout, but this should
give you an idea of the feature.

    >>> res = call_py(interpreter_path, "import sys; print sys.path")
    >>> print res # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '/interpreter/eggs/demo-0.3-py2.4.egg',
     '/interpreter/eggs/demoneeded-1.1-py2.4.egg',
     ...]
    <BLANKLINE>

The clean_paths gathered earlier is a subset of this full list of paths.

    >>> full_paths = eval(res.strip())
    >>> len(clean_paths) < len(full_paths)
    True
    >>> set(os.path.normpath(p) for p in clean_paths).issubset(
    ...     os.path.normpath(p) for p in full_paths)
    True

Unfortunately, because of how setuptools namespace packages are implemented
differently for operating system packages (debs or rpms) as opposed to
standard setuptools installation, there's a slightly trickier dance if you
use them.  To show this we'll needs some extra eggs that use namespaces.
We'll use the ``tellmy.fortune`` package, which we'll need to make an initial
call to another text fixture to create.

    >>> from zc.buildout.tests import create_sample_namespace_eggs
    >>> namespace_eggs = tmpdir('namespace_eggs')
    >>> create_sample_namespace_eggs(namespace_eggs)

    >>> reset_interpreter()
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo', 'tellmy.fortune'], join(interpreter_dir, 'eggs'),
    ...     links=[link_server, namespace_eggs], index=link_server+'index/')
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', include_site_packages=True)
    >>> sys.stdout.write('#\n'); cat(site_path)
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        setuptools_path = '...setuptools...'
        sys.path.append(setuptools_path)
        known_paths.add(os.path.normcase(setuptools_path))
        import pkg_resources
        buildout_paths = [
            '/interpreter/eggs/demo-0.3-pyN.N.egg',
            '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
            '...setuptools...',
            '/interpreter/eggs/demoneeded-1.1-pyN.N.egg'
            ]
        for path in buildout_paths:
            sitedir, sitedircase = makepath(path)
            if not sitedircase in known_paths and os.path.exists(sitedir):
                sys.path.append(sitedir)
                known_paths.add(sitedircase)
                pkg_resources.working_set.add_entry(sitedir)
        sys.__egginsert = len(buildout_paths) # Support distribute.
        original_paths = [
            ...
            ]
        for path in original_paths:
            if path == setuptools_path or path not in known_paths:
                addsitedir(path, known_paths)
        return known_paths
    <BLANKLINE>
    def original_addsitepackages(known_paths):...

    >>> print call_py(interpreter_path, "import sys; print sys.path")
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '...setuptools...',
     '/interpreter/eggs/demo-0.3-pyN.N.egg',
     '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
     ...]

As you can see, the script now first imports pkg_resources.  Then we
need to process egg files specially to look for namespace packages there
*before* we process process lines in .pth files that use the "import"
feature--lines that might be part of the setuptools namespace package
implementation for system packages, as mentioned above, and that must
come after processing egg namespaces.

The most complex that this function gets is if you use namespace packages,
include site-packages, and use relative paths.  For completeness, we'll look
at that result.

    >>> reset_interpreter()
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     interpreter='py', include_site_packages=True,
    ...     relative_paths=interpreter_dir)
    >>> sys.stdout.write('#\n'); cat(site_path)
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    #...
    def addsitepackages(known_paths):
        """Add site packages, as determined by zc.buildout.
    <BLANKLINE>
        See original_addsitepackages, below, for the original version."""
        join = os.path.join
        base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
        base = os.path.dirname(base)
        base = os.path.dirname(base)
        setuptools_path = '...setuptools...'
        sys.path.append(setuptools_path)
        known_paths.add(os.path.normcase(setuptools_path))
        import pkg_resources
        buildout_paths = [
            join(base, 'eggs/demo-0.3-pyN.N.egg'),
            join(base, 'eggs/tellmy.fortune-1.0-pyN.N.egg'),
            '...setuptools...',
            join(base, 'eggs/demoneeded-1.1-pyN.N.egg')
            ]
        for path in buildout_paths:
            sitedir, sitedircase = makepath(path)
            if not sitedircase in known_paths and os.path.exists(sitedir):
                sys.path.append(sitedir)
                known_paths.add(sitedircase)
                pkg_resources.working_set.add_entry(sitedir)
        sys.__egginsert = len(buildout_paths) # Support distribute.
        original_paths = [
            ...
            ]
        for path in original_paths:
            if path == setuptools_path or path not in known_paths:
                addsitedir(path, known_paths)
        return known_paths
    <BLANKLINE>
    def original_addsitepackages(known_paths):...

    >>> print call_py(interpreter_path, "import sys; print sys.path")
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    ['',
     '/interpreter/parts/interpreter',
     ...,
     '...setuptools...',
     '/interpreter/eggs/demo-0.3-pyN.N.egg',
     '/interpreter/eggs/tellmy.fortune-1.0-pyN.N.egg',
     '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
     ...]

The ``exec_sitecustomize`` argument does the same thing for the
sitecustomize module--it allows you to include the code from the
sitecustomize module in the underlying Python if you set the argument to
True.  The z3c.recipe.scripts package sets up the full environment necessary
to demonstrate this piece.

The ``sitepackage_safe_scripts`` function: writing scripts for entry points
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All of the examples so far for this function have been creating
interpreters.  The function can also write scripts for entry
points.  They are almost identical to the scripts that we saw for the
``scripts`` function except that they ``import site`` after setting the
sys.path to include our custom site.py and sitecustomize.py files.  These
files then initialize the Python environment as we have already seen.  Let's
see a simple example.

    >>> reset_interpreter()
    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
    ...     index=link_server+'index/')
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     reqs=['demo'])

As before, in Windows, 2 files are generated for each script.  A script
file, ending in '-script.py', and an exe file that allows the script
to be invoked directly without having to specify the Python
interpreter and without having to provide a '.py' suffix.  This is in addition
to the site.py and sitecustomize.py files that are generated as with our
interpreter examples above.

    >>> if sys.platform == 'win32':
    ...     demo_path = os.path.join(interpreter_bin_dir, 'demo-script.py')
    ...     expected = [sitecustomize_path,
    ...                 site_path,
    ...                 os.path.join(interpreter_bin_dir, 'demo.exe'),
    ...                 demo_path]
    ... else:
    ...     demo_path = os.path.join(interpreter_bin_dir, 'demo')
    ...     expected = [sitecustomize_path, site_path, demo_path]
    ...
    >>> assert generated == expected, repr((generated, expected))

The demo script runs the entry point defined in the demo egg:

    >>> cat(demo_path) # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4 -S
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
        '/interpreter/parts/interpreter',
        ]
    <BLANKLINE>
    <BLANKLINE>
    import os
    path = sys.path[0]
    if os.environ.get('PYTHONPATH'):
        path = os.pathsep.join([path, os.environ['PYTHONPATH']])
    os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ.get('PYTHONPATH', '')
    os.environ['PYTHONPATH'] = path
    import site # imports custom buildout-generated site.py
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main()

    >>> demo_call = join(interpreter_bin_dir, 'demo')
    >>> if sys.platform == 'win32':
    ...     demo_call = '"%s"' % demo_call
    >>> print system(demo_call)
    3 1
    <BLANKLINE>

There are a few differences from the ``scripts`` function.  First, the
``reqs`` argument (an iterable of string requirements or entry point
tuples) is a keyword argument here.  We see that in the example above.
Second, the ``arguments`` argument is now named ``script_arguments`` to
try and clarify that it does not affect interpreters. While the
``initialization`` argument continues to affect both the interpreters
and the entry point scripts, if you have initialization that is only
pertinent to the entry point scripts, you can use the
``script_initialization`` argument.

Let's see ``script_arguments`` and ``script_initialization`` in action.

    >>> reset_interpreter()
    >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
    ...     interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
    ...     reqs=['demo'], script_arguments='1, 2',
    ...     script_initialization='import os\nos.chdir("foo")')

    >>> cat(demo_path) # doctest: +NORMALIZE_WHITESPACE
    #!/usr/local/bin/python2.4 -S
    import sys
    sys.path[0:0] = [
      '/interpreter/parts/interpreter',
      ]
    <BLANKLINE>
    import os
    path = sys.path[0]
    if os.environ.get('PYTHONPATH'):
        path = os.pathsep.join([path, os.environ['PYTHONPATH']])
    os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ.get('PYTHONPATH', '')
    os.environ['PYTHONPATH'] = path
    import site # imports custom buildout-generated site.py
    import os
    os.chdir("foo")
    <BLANKLINE>
    import eggrecipedemo
    <BLANKLINE>
    if __name__ == '__main__':
        eggrecipedemo.main(1, 2)

Handling custom build options for extensions provided in source distributions
-----------------------------------------------------------------------------

Sometimes, we need to control how extension modules are built.  The
build function provides this level of control.  It takes a single
package specification, downloads a source distribution, and builds it
with specified custom build options.

The build function takes 3 positional arguments:

spec
   A package specification for a source distribution

dest
   A destination directory

build_ext
   A dictionary of options to be passed to the distutils build_ext
   command when building extensions.

It supports a number of optional keyword arguments:

links
   a sequence of URLs, file names, or directories to look for
   links to distributions,

index
   The URL of an index server, or almost any other valid URL. :)

   If not specified, the Python Package Index,
   http://pypi.python.org/simple/, is used.  You can specify an
   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
   will make our examples run a little bit faster.

executable
   A path to a Python executable.  Distributions will be installed
   using this executable and will be for the matching Python version.

path
   A list of additional directories to search for locally-installed
   distributions.

newest
   A boolean value indicating whether to search for new distributions
   when already-installed distributions meet the requirement.  When
   this is true, the default, and when the destination directory is
   not None, then the install function will search for the newest
   distributions that satisfy the requirements.

versions
   A dictionary mapping project names to version numbers to be used
   when selecting distributions.  This can be used to specify a set of
   distribution versions independent of other requirements.


Our link server included a source distribution that includes a simple
extension, extdemo.c::

  #include <Python.h>
  #include <extdemo.h>

  static PyMethodDef methods[] = {};

  PyMODINIT_FUNC
  initextdemo(void)
  {
      PyObject *m;
      m = Py_InitModule3("extdemo", methods, "");
  #ifdef TWO
      PyModule_AddObject(m, "val", PyInt_FromLong(2));
  #else
      PyModule_AddObject(m, "val", PyInt_FromLong(EXTDEMO));
  #endif
  }

The extension depends on a system-dependent include file, extdemo.h,
that defines a constant, EXTDEMO, that is exposed by the extension.

We'll add an include directory to our sample buildout and add the
needed include file to it:

    >>> mkdir('include')
    >>> write('include', 'extdemo.h',
    ... """
    ... #define EXTDEMO 42
    ... """)

Now, we can use the build function to create an egg from the source
distribution:

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/')
    ['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']

The function returns the list of eggs

Now if we look in our destination directory, we see we have an extdemo egg:

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    d  demo-0.3-py2.4.egg
    -  demoneeded-1.0-py2.4.egg
    d  demoneeded-1.1-py2.4.egg
    d  extdemo-1.4-py2.4-unix-i686.egg

Let's update our link server with a new version of extdemo:

    >>> update_extdemo()
    >>> print get(link_server),
    <html><body>
    <a href="bigdemo-0.1-py2.4.egg">bigdemo-0.1-py2.4.egg</a><br>
    <a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
    <a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
    <a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
    <a href="demo-0.4c1-py2.4.egg">demo-0.4c1-py2.4.egg</a><br>
    <a href="demoneeded-1.0.zip">demoneeded-1.0.zip</a><br>
    <a href="demoneeded-1.1.zip">demoneeded-1.1.zip</a><br>
    <a href="demoneeded-1.2c1.zip">demoneeded-1.2c1.zip</a><br>
    <a href="extdemo-1.4.zip">extdemo-1.4.zip</a><br>
    <a href="extdemo-1.5.zip">extdemo-1.5.zip</a><br>
    <a href="index/">index/</a><br>
    <a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
    </body></html>

The easy_install caches information about servers to reduce network
access. To see the update, we have to call the clear_index_cache
function to clear the index cache:

    >>> zc.buildout.easy_install.clear_index_cache()

If we run build with newest set to False, we won't get an update:

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/',
    ...   newest=False)
    ['/sample-install/extdemo-1.4-py2.4-linux-i686.egg']

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    d  demo-0.3-py2.4.egg
    -  demoneeded-1.0-py2.4.egg
    d  demoneeded-1.1-py2.4.egg
    d  extdemo-1.4-py2.4-unix-i686.egg

But if we run it with the default True setting for newest, then we'll
get an updated egg:

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/')
    ['/sample-install/extdemo-1.5-py2.4-unix-i686.egg']

    >>> ls(dest)
    -  demo-0.2-py2.4.egg
    d  demo-0.3-py2.4.egg
    -  demoneeded-1.0-py2.4.egg
    d  demoneeded-1.1-py2.4.egg
    d  extdemo-1.4-py2.4-unix-i686.egg
    d  extdemo-1.5-py2.4-unix-i686.egg

The versions option also influences the versions used.  For example,
if we specify a version for extdemo, then that will be used, even
though it isn't the newest.  Let's clean out the destination directory
first:

    >>> import os
    >>> for name in os.listdir(dest):
    ...     remove(dest, name)

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/',
    ...   versions=dict(extdemo='1.4'))
    ['/sample-install/extdemo-1.4-py2.4-unix-i686.egg']

    >>> ls(dest)
    d  extdemo-1.4-py2.4-unix-i686.egg

Handling custom build options for extensions in develop eggs
------------------------------------------------------------

The develop function is similar to the build function, except that,
rather than building an egg from a source directory containing a
setup.py script.

The develop function takes 2 positional arguments:

setup
   The path to a setup script, typically named "setup.py", or a
   directory containing a setup.py script.

dest
   The directory to install the egg link to

It supports some optional keyword argument:

build_ext
   A dictionary of options to be passed to the distutils build_ext
   command when building extensions.

executable
   A path to a Python executable.  Distributions will be installed
   using this executable and will be for the matching Python version.

We have a local directory containing the extdemo source:

    >>> ls(extdemo)
    -  MANIFEST
    -  MANIFEST.in
    -  README
    -  extdemo.c
    -  setup.py

Now, we can use the develop function to create a develop egg from the source
distribution:

    >>> zc.buildout.easy_install.develop(
    ...   extdemo, dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')})
    '/sample-install/extdemo.egg-link'

The name of the egg link created is returned.

Now if we look in our destination directory, we see we have an extdemo
egg link:

    >>> ls(dest)
    d  extdemo-1.4-py2.4-unix-i686.egg
    -  extdemo.egg-link

And that the source directory contains the compiled extension:

    >>> ls(extdemo)
    -  MANIFEST
    -  MANIFEST.in
    -  README
    d  build
    -  extdemo.c
    d  extdemo.egg-info
    -  extdemo.so
    -  setup.py

Download cache
--------------

Normally, when distributions are installed, if any processing is
needed, they are downloaded from the internet to a temporary directory
and then installed from there.  A download cache can be used to avoid
the download step.  This can be useful to reduce network access and to
create source distributions of an entire buildout.

A download cache is specified by calling the download_cache
function.  The function always returns the previous setting. If no
argument is passed, then the setting is unchanged.  If an argument is
passed, the download cache is set to the given path, which must point
to an existing directory.  Passing None clears the cache setting.

To see this work, we'll create a directory and set it as the cache
directory:

    >>> cache = tmpdir('cache')
    >>> zc.buildout.easy_install.download_cache(cache)

We'll recreate our destination directory:

    >>> remove(dest)
    >>> dest = tmpdir('sample-install')

We'd like to see what is being fetched from the server, so we'll
enable server logging:

    >>> get(link_server+'enable_server_logging')
    GET 200 /enable_server_logging
    ''

Now, if we install demo, and extdemo:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo==0.2'], dest,
    ...     links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)
    GET 200 /
    GET 404 /index/demo/
    GET 200 /index/
    GET 200 /demo-0.2-py2.4.egg
    GET 404 /index/demoneeded/
    GET 200 /demoneeded-1.1.zip

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/')
    GET 404 /index/extdemo/
    GET 200 /extdemo-1.5.zip
    ['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']

Not only will we get eggs in our destination directory:

    >>> ls(dest)
    d  demo-0.2-py2.4.egg
    d  demoneeded-1.1-py2.4.egg
    d  extdemo-1.5-py2.4-linux-i686.egg

But we'll get distributions in the cache directory:

    >>> ls(cache)
    -  demo-0.2-py2.4.egg
    -  demoneeded-1.1.zip
    -  extdemo-1.5.zip

The cache directory contains uninstalled distributions, such as zipped
eggs or source distributions.

Let's recreate our destination directory and clear the index cache:

    >>> remove(dest)
    >>> dest = tmpdir('sample-install')
    >>> zc.buildout.easy_install.clear_index_cache()

Now when we install the distributions:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo==0.2'], dest,
    ...     links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)
    GET 200 /
    GET 404 /index/demo/
    GET 200 /index/
    GET 404 /index/demoneeded/

    >>> zc.buildout.easy_install.build(
    ...   'extdemo', dest,
    ...   {'include-dirs': os.path.join(sample_buildout, 'include')},
    ...   links=[link_server], index=link_server+'index/')
    GET 404 /index/extdemo/
    ['/sample-install/extdemo-1.5-py2.4-linux-i686.egg']

    >>> ls(dest)
    d  demo-0.2-py2.4.egg
    d  demoneeded-1.1-py2.4.egg
    d  extdemo-1.5-py2.4-linux-i686.egg

Note that we didn't download the distributions from the link server.

If we remove the restriction on demo, we'll download a newer version
from the link server:

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest,
    ...     links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)
    GET 200 /demo-0.3-py2.4.egg

Normally, the download cache is the preferred source of downloads, but
not the only one.

Installing solely from a download cache
---------------------------------------

A download cache can be used as the basis of application source
releases.  In an application source release, we want to distribute an
application that can be built without making any network accesses.  In
this case, we distribute a download cache and tell the easy_install
module to install from the download cache only, without making network
accesses.  The install_from_cache function can be used to signal that
packages should be installed only from the download cache.  The
function always returns the previous setting.  Calling it with no
arguments returns the current setting without changing it:

    >>> zc.buildout.easy_install.install_from_cache()
    False

Calling it with a boolean value changes the setting and returns the
previous setting:

    >>> zc.buildout.easy_install.install_from_cache(True)
    False

Let's remove demo-0.3-py2.4.egg from the cache, clear the index cache,
recreate the destination directory, and reinstall demo:

    >>> for  f in os.listdir(cache):
    ...     if f.startswith('demo-0.3-'):
    ...         remove(cache, f)

    >>> zc.buildout.easy_install.clear_index_cache()
    >>> remove(dest)
    >>> dest = tmpdir('sample-install')

    >>> ws = zc.buildout.easy_install.install(
    ...     ['demo'], dest,
    ...     links=[link_server], index=link_server+'index/',
    ...     always_unzip=True)

    >>> ls(dest)
    d  demo-0.2-py2.4.egg
    d  demoneeded-1.1-py2.4.egg

This time, we didn't download from or even query the link server.

.. Disable the download cache:

    >>> zc.buildout.easy_install.download_cache(None)
    '/cache'

    >>> zc.buildout.easy_install.install_from_cache(False)
    True
