--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/eggs/zc.buildout-1.5.2-py2.6.egg/zc/buildout/tests.py Sat Jan 08 11:20:57 2011 +0530
@@ -0,0 +1,4190 @@
+##############################################################################
+#
+# Copyright (c) 2004-2009 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# 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 doctest
+from zope.testing import renormalizing
+import os
+import pkg_resources
+import re
+import shutil
+import sys
+import tempfile
+import unittest
+import zc.buildout.easy_install
+import zc.buildout.testing
+import zc.buildout.testselectingpython
+import zipfile
+
+os_path_sep = os.path.sep
+if os_path_sep == '\\':
+ os_path_sep *= 2
+
+
+def develop_w_non_setuptools_setup_scripts():
+ """
+We should be able to deal with setup scripts that aren't setuptools based.
+
+ >>> mkdir('foo')
+ >>> write('foo', 'setup.py',
+ ... '''
+ ... from distutils.core import setup
+ ... setup(name="foo")
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = foo
+ ... parts =
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+
+ >>> ls('develop-eggs')
+ - foo.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+ """
+
+def develop_verbose():
+ """
+We should be able to deal with setup scripts that aren't setuptools based.
+
+ >>> mkdir('foo')
+ >>> write('foo', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name="foo")
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = foo
+ ... parts =
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')+' -vv'), # doctest: +ELLIPSIS
+ Installing...
+ Develop: '/sample-buildout/foo'
+ ...
+ Installed /sample-buildout/foo
+ ...
+
+ >>> ls('develop-eggs')
+ - foo.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+ >>> print system(join('bin', 'buildout')+' -vvv'), # doctest: +ELLIPSIS
+ Installing...
+ Develop: '/sample-buildout/foo'
+ in: '/sample-buildout/foo'
+ ... -q develop -mxN -d /sample-buildout/develop-eggs/...
+
+
+ """
+
+def buildout_error_handling():
+ r"""Buildout error handling
+
+Asking for a section that doesn't exist, yields a missing section error:
+
+ >>> import os
+ >>> os.chdir(sample_buildout)
+ >>> import zc.buildout.buildout
+ >>> buildout = zc.buildout.buildout.Buildout('buildout.cfg', [])
+ >>> buildout['eek']
+ Traceback (most recent call last):
+ ...
+ MissingSection: The referenced section, 'eek', was not defined.
+
+Asking for an option that doesn't exist, a MissingOption error is raised:
+
+ >>> buildout['buildout']['eek']
+ Traceback (most recent call last):
+ ...
+ MissingOption: Missing option: buildout:eek
+
+It is an error to create a variable-reference cycle:
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts =
+ ... x = ${buildout:y}
+ ... y = ${buildout:z}
+ ... z = ${buildout:x}
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+ While:
+ Initializing.
+ Getting section buildout.
+ Initializing section buildout.
+ Getting option buildout:y.
+ Getting option buildout:z.
+ Getting option buildout:x.
+ Getting option buildout:y.
+ Error: Circular reference in substitutions.
+
+It is an error to use funny characters in variable refereces:
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data_dir debug
+ ... x = ${bui$ldout:y}
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Initializing.
+ Getting section buildout.
+ Initializing section buildout.
+ Getting option buildout:x.
+ Error: The section name in substitution, ${bui$ldout:y},
+ has invalid characters.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data_dir debug
+ ... x = ${buildout:y{z}
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Initializing.
+ Getting section buildout.
+ Initializing section buildout.
+ Getting option buildout:x.
+ Error: The option name in substitution, ${buildout:y{z},
+ has invalid characters.
+
+and too have too many or too few colons:
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data_dir debug
+ ... x = ${parts}
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Initializing.
+ Getting section buildout.
+ Initializing section buildout.
+ Getting option buildout:x.
+ Error: The substitution, ${parts},
+ doesn't contain a colon.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data_dir debug
+ ... x = ${buildout:y:z}
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Initializing.
+ Getting section buildout.
+ Initializing section buildout.
+ Getting option buildout:x.
+ Error: The substitution, ${buildout:y:z},
+ has too many colons.
+
+Al parts have to have a section:
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Installing.
+ Getting section x.
+ Error: The referenced section, 'x', was not defined.
+
+and all parts have to have a specified recipe:
+
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ...
+ ... [x]
+ ... foo = 1
+ ... ''')
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
+ While:
+ Installing.
+ Error: Missing option: x:recipe
+
+"""
+
+make_dist_that_requires_setup_py_template = """
+from setuptools import setup
+setup(name=%r, version=%r,
+ install_requires=%r,
+ )
+"""
+
+def make_dist_that_requires(dest, name, requires=[], version=1, egg=''):
+ os.mkdir(os.path.join(dest, name))
+ open(os.path.join(dest, name, 'setup.py'), 'w').write(
+ make_dist_that_requires_setup_py_template
+ % (name, version, requires)
+ )
+
+def show_who_requires_when_there_is_a_conflict():
+ """
+It's a pain when we require eggs that have requirements that are
+incompatible. We want the error we get to tell us what is missing.
+
+Let's make a few develop distros, some of which have incompatible
+requirements.
+
+ >>> make_dist_that_requires(sample_buildout, 'sampley',
+ ... ['demoneeded ==1.0'])
+ >>> make_dist_that_requires(sample_buildout, 'samplez',
+ ... ['demoneeded ==1.1'])
+
+Now, let's create a buildout that requires y and z:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... develop = sampley samplez
+ ... find-links = %(link_server)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = sampley
+ ... samplez
+ ... ''' % globals())
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/sampley'
+ Develop: '/sample-buildout/samplez'
+ Installing eggs.
+ Getting distribution for 'demoneeded==1.1'.
+ Got demoneeded 1.1.
+ While:
+ Installing eggs.
+ Error: There is a version conflict.
+ We already have: demoneeded 1.1
+ but sampley 1 requires 'demoneeded==1.0'.
+
+Here, we see that sampley required an older version of demoneeded. What
+if we hadn't required sampley ourselves:
+
+ >>> make_dist_that_requires(sample_buildout, 'samplea', ['sampleb'])
+ >>> make_dist_that_requires(sample_buildout, 'sampleb',
+ ... ['sampley', 'samplea'])
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... develop = sampley samplez samplea sampleb
+ ... find-links = %(link_server)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = samplea
+ ... samplez
+ ... ''' % globals())
+
+If we use the verbose switch, we can see where requirements are coming from:
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ We have a develop egg: zc.buildout 1.0.0
+ We have the best distribution that satisfies 'setuptools'.
+ Picked: setuptools = 0.6
+ Develop: '/sample-buildout/sampley'
+ Develop: '/sample-buildout/samplez'
+ Develop: '/sample-buildout/samplea'
+ Develop: '/sample-buildout/sampleb'
+ ...Installing eggs.
+ Installing 'samplea', 'samplez'.
+ We have a develop egg: samplea 1
+ We have a develop egg: samplez 1
+ Getting required 'demoneeded==1.1'
+ required by samplez 1.
+ We have the distribution that satisfies 'demoneeded==1.1'.
+ Getting required 'sampleb'
+ required by samplea 1.
+ We have a develop egg: sampleb 1
+ Getting required 'sampley'
+ required by sampleb 1.
+ We have a develop egg: sampley 1
+ While:
+ Installing eggs.
+ Error: There is a version conflict.
+ We already have: demoneeded 1.1
+ but sampley 1 requires 'demoneeded==1.0'.
+ """
+
+def show_who_requires_missing_distributions():
+ """
+
+When working with a lot of eggs, which require eggs recursively, it can
+be hard to tell why we're requiring things we can't find. Fortunately,
+buildout will tell us who's asking for something that we can't find.
+
+ >>> make_dist_that_requires(sample_buildout, 'sampley', ['demoneeded'])
+ >>> make_dist_that_requires(sample_buildout, 'samplea', ['sampleb'])
+ >>> make_dist_that_requires(sample_buildout, 'sampleb',
+ ... ['sampley', 'samplea'])
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... develop = sampley samplea sampleb
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = samplea
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/sampley'
+ Develop: '/sample-buildout/samplea'
+ Develop: '/sample-buildout/sampleb'
+ Installing eggs.
+ Couldn't find index page for 'demoneeded' (maybe misspelled?)
+ Getting distribution for 'demoneeded'.
+ While:
+ Installing eggs.
+ Getting distribution for 'demoneeded'.
+ Error: Couldn't find a distribution for 'demoneeded'.
+ """
+
+def show_eggs_from_site_packages():
+ """
+Sometimes you want to know what eggs are coming from site-packages. This
+might be for a diagnostic, or so that you can get a starting value for the
+allowed-eggs-from-site-packages option. The -v flag will also include this
+information.
+
+Our "py_path" has the "demoneeded," "demo"
+packages available. We'll ask for "bigdemo," which will get both of them.
+
+Here's our set up.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... prefer-final = true
+ ... find-links = %(link_server)s
+ ...
+ ... [primed_python]
+ ... executable = %(py_path)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... python = primed_python
+ ... eggs = bigdemo
+ ... ''' % globals())
+
+Now here is the output. The lines that begin with "Egg from site-packages:"
+indicate the eggs from site-packages that have been selected. You'll see
+we have two: demo 0.3 and demoneeded 1.1.
+
+ >>> print system(buildout+" -v"),
+ Installing 'zc.buildout', 'setuptools'.
+ We have a develop egg: zc.buildout V
+ We have the best distribution that satisfies 'setuptools'.
+ Picked: setuptools = V
+ Installing 'zc.recipe.egg'.
+ We have a develop egg: zc.recipe.egg V
+ Installing eggs.
+ Installing 'bigdemo'.
+ We have no distributions for bigdemo that satisfies 'bigdemo'.
+ Getting distribution for 'bigdemo'.
+ Got bigdemo 0.1.
+ Picked: bigdemo = 0.1
+ Getting required 'demo'
+ required by bigdemo 0.1.
+ We have the best distribution that satisfies 'demo'.
+ Egg from site-packages: demo 0.3
+ Getting required 'demoneeded'
+ required by demo 0.3.
+ We have the best distribution that satisfies 'demoneeded'.
+ Egg from site-packages: demoneeded 1.1
+ """
+
+def test_comparing_saved_options_with_funny_characters():
+ """
+If an option has newlines, extra/odd spaces or a %, we need to make sure
+the comparison with the saved value works correctly.
+
+ >>> mkdir(sample_buildout, 'recipes')
+ >>> write(sample_buildout, 'recipes', 'debug.py',
+ ... '''
+ ... class Debug:
+ ... def __init__(self, buildout, name, options):
+ ... options['debug'] = \"\"\" <zodb>
+ ...
+ ... <filestorage>
+ ... path foo
+ ... </filestorage>
+ ...
+ ... </zodb>
+ ... \"\"\"
+ ... options['debug1'] = \"\"\"
+ ... <zodb>
+ ...
+ ... <filestorage>
+ ... path foo
+ ... </filestorage>
+ ...
+ ... </zodb>
+ ... \"\"\"
+ ... options['debug2'] = ' x '
+ ... options['debug3'] = '42'
+ ... options['format'] = '%3d'
+ ...
+ ... def install(self):
+ ... open('t', 'w').write('t')
+ ... return 't'
+ ...
+ ... update = install
+ ... ''')
+
+
+ >>> write(sample_buildout, 'recipes', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(
+ ... name = "recipes",
+ ... entry_points = {'zc.buildout': ['default = debug:Debug']},
+ ... )
+ ... ''')
+
+ >>> write(sample_buildout, 'recipes', 'README.txt', " ")
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = debug
+ ...
+ ... [debug]
+ ... recipe = recipes
+ ... ''')
+
+ >>> os.chdir(sample_buildout)
+ >>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Installing debug.
+
+If we run the buildout again, we shoudn't get a message about
+uninstalling anything because the configuration hasn't changed.
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Updating debug.
+"""
+
+def finding_eggs_as_local_directories():
+ r"""
+It is possible to set up find-links so that we could install from
+a local directory that may contained unzipped eggs.
+
+ >>> src = tmpdir('src')
+ >>> write(src, 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='demo', py_modules=[''],
+ ... zip_safe=False, version='1.0', author='bob', url='bob',
+ ... author_email='bob')
+ ... ''')
+
+ >>> write(src, 't.py', '#\n')
+ >>> write(src, 'README.txt', '')
+ >>> _ = system(join('bin', 'buildout')+' setup ' + src + ' bdist_egg')
+
+Install it so it gets unzipped:
+
+ >>> d1 = tmpdir('d1')
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo'], d1, links=[join(src, 'dist')],
+ ... )
+
+ >>> ls(d1)
+ d demo-1.0-py2.4.egg
+
+Then try to install it again:
+
+ >>> d2 = tmpdir('d2')
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo'], d2, links=[d1],
+ ... )
+
+ >>> ls(d2)
+ d demo-1.0-py2.4.egg
+
+ """
+
+def make_sure__get_version_works_with_2_digit_python_versions():
+ """
+
+This is a test of an internal function used by higher-level machinery.
+
+We'll start by creating a faux 'python' that executable that prints a
+2-digit version. This is a bit of a pain to do portably. :(
+
+ >>> mkdir('demo')
+ >>> write('demo', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='demo',
+ ... entry_points = {'console_scripts': ['demo = demo:main']},
+ ... )
+ ... ''')
+ >>> write('demo', 'demo.py',
+ ... '''
+ ... def main():
+ ... print 'Python 2.5'
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = demo
+ ... parts =
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/demo'
+
+ >>> import zc.buildout.easy_install
+ >>> ws = zc.buildout.easy_install.working_set(
+ ... ['demo'], sys.executable, ['develop-eggs'])
+ >>> bool(zc.buildout.easy_install.scripts(
+ ... ['demo'], ws, sys.executable, 'bin'))
+ True
+
+ >>> print system(join('bin', 'demo')),
+ Python 2.5
+
+Now, finally, let's test _get_version:
+
+ >>> zc.buildout.easy_install._get_version(join('bin', 'demo'))
+ '2.5'
+
+ """
+
+def create_sections_on_command_line():
+ """
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts =
+ ... x = ${foo:bar}
+ ... ''')
+
+ >>> print system(buildout + ' foo:bar=1 -vv'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ ...
+ [foo]
+ bar = 1
+ ...
+
+ """
+
+def test_help():
+ """
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')+' -h'),
+ ... # doctest: +ELLIPSIS
+ Usage: buildout [options] [assignments] [command [command arguments]]
+ <BLANKLINE>
+ Options:
+ <BLANKLINE>
+ -h, --help
+ ...
+
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')
+ ... +' --help'),
+ ... # doctest: +ELLIPSIS
+ Usage: buildout [options] [assignments] [command [command arguments]]
+ <BLANKLINE>
+ Options:
+ <BLANKLINE>
+ -h, --help
+ ...
+ """
+
+def test_bootstrap_with_extension():
+ """
+We had a problem running a bootstrap with an extension. Let's make
+sure it is fixed. Basically, we don't load extensions when
+bootstrapping.
+
+ >>> d = tmpdir('sample-bootstrap')
+
+ >>> write(d, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... extensions = some_awsome_extension
+ ... parts =
+ ... ''')
+
+ >>> os.chdir(d)
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')
+ ... + ' bootstrap'),
+ Creating directory '/sample-bootstrap/bin'.
+ Creating directory '/sample-bootstrap/parts'.
+ Creating directory '/sample-bootstrap/eggs'.
+ Creating directory '/sample-bootstrap/develop-eggs'.
+ Generated script '/sample-bootstrap/bin/buildout'.
+ """
+
+
+def bug_92891_bootstrap_crashes_with_egg_recipe_in_buildout_section():
+ """
+ >>> d = tmpdir('sample-bootstrap')
+
+ >>> write(d, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = buildout
+ ... eggs-directory = eggs
+ ...
+ ... [buildout]
+ ... recipe = zc.recipe.egg
+ ... eggs = zc.buildout
+ ... scripts = buildout=buildout
+ ... ''')
+
+ >>> os.chdir(d)
+ >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')
+ ... + ' bootstrap'),
+ Creating directory '/sample-bootstrap/bin'.
+ Creating directory '/sample-bootstrap/parts'.
+ Creating directory '/sample-bootstrap/eggs'.
+ Creating directory '/sample-bootstrap/develop-eggs'.
+ Generated script '/sample-bootstrap/bin/buildout'.
+
+ >>> print system(os.path.join('bin', 'buildout')),
+ Unused options for buildout: 'scripts' 'eggs'.
+
+ """
+
+def removing_eggs_from_develop_section_causes_egg_link_to_be_removed():
+ '''
+ >>> cd(sample_buildout)
+
+Create a develop egg:
+
+ >>> mkdir('foo')
+ >>> write('foo', 'setup.py',
+ ... """
+ ... from setuptools import setup
+ ... setup(name='foox')
+ ... """)
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = foo
+ ... parts =
+ ... """)
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+
+ >>> ls('develop-eggs')
+ - foox.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+Create another:
+
+ >>> mkdir('bar')
+ >>> write('bar', 'setup.py',
+ ... """
+ ... from setuptools import setup
+ ... setup(name='fooy')
+ ... """)
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = foo bar
+ ... parts =
+ ... """)
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+ Develop: '/sample-buildout/bar'
+
+ >>> ls('develop-eggs')
+ - foox.egg-link
+ - fooy.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+Remove one:
+
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = bar
+ ... parts =
+ ... """)
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/bar'
+
+It is gone
+
+ >>> ls('develop-eggs')
+ - fooy.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+Remove the other:
+
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... parts =
+ ... """)
+ >>> print system(join('bin', 'buildout')),
+
+All gone
+
+ >>> ls('develop-eggs')
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+ '''
+
+
+def add_setuptools_to_dependencies_when_namespace_packages():
+ '''
+Often, a package depends on setuptools soley by virtue of using
+namespace packages. In this situation, package authors often forget to
+declare setuptools as a dependency. This is a mistake, but,
+unfortunately, a common one that we need to work around. If an egg
+uses namespace packages and does not include setuptools as a depenency,
+we will still include setuptools in the working set. If we see this for
+a devlop egg, we will also generate a warning.
+
+ >>> mkdir('foo')
+ >>> mkdir('foo', 'src')
+ >>> mkdir('foo', 'src', 'stuff')
+ >>> write('foo', 'src', 'stuff', '__init__.py',
+ ... """__import__('pkg_resources').declare_namespace(__name__)
+ ... """)
+ >>> mkdir('foo', 'src', 'stuff', 'foox')
+ >>> write('foo', 'src', 'stuff', 'foox', '__init__.py', '')
+ >>> write('foo', 'setup.py',
+ ... """
+ ... from setuptools import setup
+ ... setup(name='foox',
+ ... namespace_packages = ['stuff'],
+ ... package_dir = {'': 'src'},
+ ... packages = ['stuff', 'stuff.foox'],
+ ... )
+ ... """)
+ >>> write('foo', 'README.txt', '')
+
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = foo
+ ... parts =
+ ... """)
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+
+Now, if we generate a working set using the egg link, we will get a warning
+and we will get setuptools included in the working set.
+
+ >>> import logging, zope.testing.loggingsupport
+ >>> handler = zope.testing.loggingsupport.InstalledHandler(
+ ... 'zc.buildout.easy_install', level=logging.WARNING)
+ >>> logging.getLogger('zc.buildout.easy_install').propagate = False
+
+ >>> [dist.project_name
+ ... for dist in zc.buildout.easy_install.working_set(
+ ... ['foox'], sys.executable,
+ ... [join(sample_buildout, 'eggs'),
+ ... join(sample_buildout, 'develop-eggs'),
+ ... ])]
+ ['foox', 'setuptools']
+
+ >>> print handler
+ zc.buildout.easy_install WARNING
+ Develop distribution: foox 0.0.0
+ uses namespace packages but the distribution does not require setuptools.
+
+ >>> handler.clear()
+
+On the other hand, if we have a regular egg, rather than a develop egg:
+
+ >>> os.remove(join('develop-eggs', 'foox.egg-link'))
+
+ >>> _ = system(join('bin', 'buildout') + ' setup foo bdist_egg -d'
+ ... + join(sample_buildout, 'eggs'))
+
+ >>> ls('develop-eggs')
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+ >>> print 'START ->'; ls('eggs') # doctest: +ELLIPSIS
+ START...
+ - foox-0.0.0-py2.4.egg
+ ...
+
+We do not get a warning, but we do get setuptools included in the working set:
+
+ >>> [dist.project_name
+ ... for dist in zc.buildout.easy_install.working_set(
+ ... ['foox'], sys.executable,
+ ... [join(sample_buildout, 'eggs'),
+ ... join(sample_buildout, 'develop-eggs'),
+ ... ])]
+ ['foox', 'setuptools']
+
+ >>> print handler,
+
+We get the same behavior if the it is a depedency that uses a
+namespace package.
+
+
+ >>> mkdir('bar')
+ >>> write('bar', 'setup.py',
+ ... """
+ ... from setuptools import setup
+ ... setup(name='bar', install_requires = ['foox'])
+ ... """)
+ >>> write('bar', 'README.txt', '')
+
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = foo bar
+ ... parts =
+ ... """)
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+ Develop: '/sample-buildout/bar'
+
+ >>> [dist.project_name
+ ... for dist in zc.buildout.easy_install.working_set(
+ ... ['bar'], sys.executable,
+ ... [join(sample_buildout, 'eggs'),
+ ... join(sample_buildout, 'develop-eggs'),
+ ... ])]
+ ['bar', 'foox', 'setuptools']
+
+ >>> print handler,
+ zc.buildout.easy_install WARNING
+ Develop distribution: foox 0.0.0
+ uses namespace packages but the distribution does not require setuptools.
+
+
+ >>> logging.getLogger('zc.buildout.easy_install').propagate = True
+ >>> handler.uninstall()
+
+ '''
+
+def develop_preserves_existing_setup_cfg():
+ """
+
+See "Handling custom build options for extensions in develop eggs" in
+easy_install.txt. This will be very similar except that we'll have an
+existing setup.cfg:
+
+ >>> write(extdemo, "setup.cfg",
+ ... '''
+ ... # sampe cfg file
+ ...
+ ... [foo]
+ ... bar = 1
+ ...
+ ... [build_ext]
+ ... define = X,Y
+ ... ''')
+
+ >>> mkdir('include')
+ >>> write('include', 'extdemo.h',
+ ... '''
+ ... #define EXTDEMO 42
+ ... ''')
+
+ >>> dest = tmpdir('dest')
+ >>> zc.buildout.easy_install.develop(
+ ... extdemo, dest,
+ ... {'include-dirs': os.path.join(sample_buildout, 'include')})
+ '/dest/extdemo.egg-link'
+
+ >>> ls(dest)
+ - extdemo.egg-link
+
+ >>> cat(extdemo, "setup.cfg")
+ <BLANKLINE>
+ # sampe cfg file
+ <BLANKLINE>
+ [foo]
+ bar = 1
+ <BLANKLINE>
+ [build_ext]
+ define = X,Y
+
+"""
+
+def uninstall_recipes_used_for_removal():
+ """
+Uninstall recipes need to be called when a part is removed too:
+
+ >>> mkdir("recipes")
+ >>> write("recipes", "setup.py",
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipes',
+ ... entry_points={
+ ... 'zc.buildout': ["demo=demo:Install"],
+ ... 'zc.buildout.uninstall': ["demo=demo:uninstall"],
+ ... })
+ ... ''')
+
+ >>> write("recipes", "demo.py",
+ ... '''
+ ... class Install:
+ ... def __init__(*args): pass
+ ... def install(self):
+ ... print 'installing'
+ ... return ()
+ ... def uninstall(name, options): print 'uninstalling'
+ ... ''')
+
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = demo
+ ... [demo]
+ ... recipe = recipes:demo
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/recipes'
+ Installing demo.
+ installing
+
+
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = demo
+ ... [demo]
+ ... recipe = recipes:demo
+ ... x = 1
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling demo.
+ Running uninstall recipe.
+ uninstalling
+ Installing demo.
+ installing
+
+
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts =
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling demo.
+ Running uninstall recipe.
+ uninstalling
+
+"""
+
+def extensions_installed_as_eggs_work_in_offline_mode():
+ '''
+ >>> mkdir('demo')
+
+ >>> write('demo', 'demo.py',
+ ... """
+ ... def ext(buildout):
+ ... print 'ext', list(buildout)
+ ... """)
+
+ >>> write('demo', 'setup.py',
+ ... """
+ ... from setuptools import setup
+ ...
+ ... setup(
+ ... name = "demo",
+ ... py_modules=['demo'],
+ ... entry_points = {'zc.buildout.extension': ['ext = demo:ext']},
+ ... )
+ ... """)
+
+ >>> bdist_egg(join(sample_buildout, "demo"), sys.executable,
+ ... join(sample_buildout, "eggs"))
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... extensions = demo
+ ... parts =
+ ... offline = true
+ ... """)
+
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ ext ['buildout']
+
+
+ '''
+
+def changes_in_svn_or_CVS_dont_affect_sig():
+ """
+
+If we have a develop recipe, it's signature shouldn't be affected to
+changes in .svn or CVS directories.
+
+ >>> mkdir('recipe')
+ >>> write('recipe', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipe',
+ ... entry_points={'zc.buildout': ['default=foo:Foo']})
+ ... ''')
+ >>> write('recipe', 'foo.py',
+ ... '''
+ ... class Foo:
+ ... def __init__(*args): pass
+ ... def install(*args): return ()
+ ... update = install
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipe
+ ... parts = foo
+ ...
+ ... [foo]
+ ... recipe = recipe
+ ... ''')
+
+
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ Develop: '/sample-buildout/recipe'
+ Installing foo.
+
+ >>> mkdir('recipe', '.svn')
+ >>> mkdir('recipe', 'CVS')
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ Develop: '/sample-buildout/recipe'
+ Updating foo.
+
+ >>> write('recipe', '.svn', 'x', '1')
+ >>> write('recipe', 'CVS', 'x', '1')
+
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ Develop: '/sample-buildout/recipe'
+ Updating foo.
+
+ """
+
+if hasattr(os, 'symlink'):
+ def bug_250537_broken_symlink_doesnt_affect_sig():
+ """
+If we have a develop recipe, it's signature shouldn't be affected by
+broken symlinks, and better yet, computing the hash should not break
+because of the missing target file.
+
+ >>> mkdir('recipe')
+ >>> write('recipe', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipe',
+ ... entry_points={'zc.buildout': ['default=foo:Foo']})
+ ... ''')
+ >>> write('recipe', 'foo.py',
+ ... '''
+ ... class Foo:
+ ... def __init__(*args): pass
+ ... def install(*args): return ()
+ ... update = install
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipe
+ ... parts = foo
+ ...
+ ... [foo]
+ ... recipe = recipe
+ ... ''')
+
+
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ Develop: '/sample-buildout/recipe'
+ Installing foo.
+
+ >>> write('recipe', 'some-file', '1')
+ >>> os.symlink(join('recipe', 'some-file'),
+ ... join('recipe', 'another-file'))
+ >>> ls('recipe')
+ l another-file
+ - foo.py
+ - foo.pyc
+ d recipe.egg-info
+ - setup.py
+ - some-file
+
+ >>> remove('recipe', 'some-file')
+
+ >>> print system(join(sample_buildout, 'bin', 'buildout')),
+ Develop: '/sample-buildout/recipe'
+ Updating foo.
+
+ """
+
+def o_option_sets_offline():
+ """
+ >>> print system(join(sample_buildout, 'bin', 'buildout')+' -vvo'),
+ ... # doctest: +ELLIPSIS
+ <BLANKLINE>
+ ...
+ offline = true
+ ...
+ """
+
+def recipe_upgrade():
+ """
+
+The buildout will upgrade recipes in newest (and non-offline) mode.
+
+Let's create a recipe egg
+
+ >>> mkdir('recipe')
+ >>> write('recipe', 'recipe.py',
+ ... '''
+ ... class Recipe:
+ ... def __init__(*a): pass
+ ... def install(self):
+ ... print 'recipe v1'
+ ... return ()
+ ... update = install
+ ... ''')
+
+ >>> write('recipe', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipe', version='1', py_modules=['recipe'],
+ ... entry_points={'zc.buildout': ['default = recipe:Recipe']},
+ ... )
+ ... ''')
+
+ >>> write('recipe', 'README', '')
+
+ >>> print system(buildout+' setup recipe bdist_egg'), # doctest: +ELLIPSIS
+ Running setup script 'recipe/setup.py'.
+ ...
+
+ >>> rmdir('recipe', 'build')
+
+And update our buildout to use it.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = foo
+ ... find-links = %s
+ ...
+ ... [foo]
+ ... recipe = recipe
+ ... ''' % join('recipe', 'dist'))
+
+ >>> print system(buildout),
+ Getting distribution for 'recipe'.
+ Got recipe 1.
+ Installing foo.
+ recipe v1
+
+Now, if we update the recipe egg:
+
+ >>> write('recipe', 'recipe.py',
+ ... '''
+ ... class Recipe:
+ ... def __init__(*a): pass
+ ... def install(self):
+ ... print 'recipe v2'
+ ... return ()
+ ... update = install
+ ... ''')
+
+ >>> write('recipe', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipe', version='2', py_modules=['recipe'],
+ ... entry_points={'zc.buildout': ['default = recipe:Recipe']},
+ ... )
+ ... ''')
+
+
+ >>> print system(buildout+' setup recipe bdist_egg'), # doctest: +ELLIPSIS
+ Running setup script 'recipe/setup.py'.
+ ...
+
+We won't get the update if we specify -N:
+
+ >>> print system(buildout+' -N'),
+ Updating foo.
+ recipe v1
+
+or if we use -o:
+
+ >>> print system(buildout+' -o'),
+ Updating foo.
+ recipe v1
+
+But we will if we use neither of these:
+
+ >>> print system(buildout),
+ Getting distribution for 'recipe'.
+ Got recipe 2.
+ Uninstalling foo.
+ Installing foo.
+ recipe v2
+
+We can also select a particular recipe version:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = foo
+ ... find-links = %s
+ ...
+ ... [foo]
+ ... recipe = recipe ==1
+ ... ''' % join('recipe', 'dist'))
+
+ >>> print system(buildout),
+ Uninstalling foo.
+ Installing foo.
+ recipe v1
+
+ """
+
+def update_adds_to_uninstall_list():
+ """
+
+Paths returned by the update method are added to the list of paths to
+uninstall
+
+ >>> mkdir('recipe')
+ >>> write('recipe', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='recipe',
+ ... entry_points={'zc.buildout': ['default = recipe:Recipe']},
+ ... )
+ ... ''')
+
+ >>> write('recipe', 'recipe.py',
+ ... '''
+ ... import os
+ ... class Recipe:
+ ... def __init__(*_): pass
+ ... def install(self):
+ ... r = ('a', 'b', 'c')
+ ... for p in r: os.mkdir(p)
+ ... return r
+ ... def update(self):
+ ... r = ('c', 'd', 'e')
+ ... for p in r:
+ ... if not os.path.exists(p):
+ ... os.mkdir(p)
+ ... return r
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipe
+ ... parts = foo
+ ...
+ ... [foo]
+ ... recipe = recipe
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipe'
+ Installing foo.
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipe'
+ Updating foo.
+
+ >>> cat('.installed.cfg') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+ [buildout]
+ ...
+ [foo]
+ __buildout_installed__ = a
+ b
+ c
+ d
+ e
+ __buildout_signature__ = ...
+
+"""
+
+def log_when_there_are_not_local_distros():
+ """
+ >>> from zope.testing.loggingsupport import InstalledHandler
+ >>> handler = InstalledHandler('zc.buildout.easy_install')
+ >>> import logging
+ >>> logger = logging.getLogger('zc.buildout.easy_install')
+ >>> old_propogate = logger.propagate
+ >>> logger.propagate = False
+
+ >>> 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/')
+
+ >>> print handler # doctest: +ELLIPSIS
+ zc.buildout.easy_install DEBUG
+ Installing 'demo==0.2'.
+ zc.buildout.easy_install DEBUG
+ We have no distributions for demo that satisfies 'demo==0.2'.
+ ...
+
+ >>> handler.uninstall()
+ >>> logger.propagate = old_propogate
+
+ """
+
+def internal_errors():
+ """Internal errors are clearly marked and don't generate tracebacks:
+
+ >>> mkdir(sample_buildout, 'recipes')
+
+ >>> write(sample_buildout, 'recipes', 'mkdir.py',
+ ... '''
+ ... class Mkdir:
+ ... def __init__(self, buildout, name, options):
+ ... self.name, self.options = name, options
+ ... options['path'] = os.path.join(
+ ... buildout['buildout']['directory'],
+ ... options['path'],
+ ... )
+ ... ''')
+
+ >>> write(sample_buildout, 'recipes', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name = "recipes",
+ ... entry_points = {'zc.buildout': ['mkdir = mkdir:Mkdir']},
+ ... )
+ ... ''')
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data-dir
+ ...
+ ... [data-dir]
+ ... recipe = recipes:mkdir
+ ... ''')
+
+ >>> print system(buildout), # doctest: +ELLIPSIS
+ Develop: '/sample-buildout/recipes'
+ While:
+ Installing.
+ Getting section data-dir.
+ Initializing part data-dir.
+ <BLANKLINE>
+ An internal error occurred due to a bug in either zc.buildout or in a
+ recipe being used:
+ Traceback (most recent call last):
+ ...
+ NameError: global name 'os' is not defined
+ """
+
+def whine_about_unused_options():
+ '''
+
+ >>> write('foo.py',
+ ... """
+ ... class Foo:
+ ...
+ ... def __init__(self, buildout, name, options):
+ ... self.name, self.options = name, options
+ ... options['x']
+ ...
+ ... def install(self):
+ ... self.options['y']
+ ... return ()
+ ... """)
+
+ >>> write('setup.py',
+ ... """
+ ... from setuptools import setup
+ ... setup(name = "foo",
+ ... entry_points = {'zc.buildout': ['default = foo:Foo']},
+ ... )
+ ... """)
+
+ >>> write('buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = .
+ ... parts = foo
+ ... a = 1
+ ...
+ ... [foo]
+ ... recipe = foo
+ ... x = 1
+ ... y = 1
+ ... z = 1
+ ... """)
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/.'
+ Unused options for buildout: 'a'.
+ Installing foo.
+ Unused options for foo: 'z'.
+ '''
+
+def abnormal_exit():
+ """
+People sometimes hit control-c while running a builout. We need to make
+sure that the installed database Isn't corrupted. To test this, we'll create
+some evil recipes that exit uncleanly:
+
+ >>> mkdir('recipes')
+ >>> write('recipes', 'recipes.py',
+ ... '''
+ ... import os
+ ...
+ ... class Clean:
+ ... def __init__(*_): pass
+ ... def install(_): return ()
+ ... def update(_): pass
+ ...
+ ... class EvilInstall(Clean):
+ ... def install(_): os._exit(1)
+ ...
+ ... class EvilUpdate(Clean):
+ ... def update(_): os._exit(1)
+ ... ''')
+
+ >>> write('recipes', 'setup.py',
+ ... '''
+ ... import setuptools
+ ... setuptools.setup(name='recipes',
+ ... entry_points = {
+ ... 'zc.buildout': [
+ ... 'clean = recipes:Clean',
+ ... 'evil_install = recipes:EvilInstall',
+ ... 'evil_update = recipes:EvilUpdate',
+ ... 'evil_uninstall = recipes:Clean',
+ ... ],
+ ... },
+ ... )
+ ... ''')
+
+Now let's look at 3 cases:
+
+1. We exit during installation after installing some other parts:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = p1 p2 p3 p4
+ ...
+ ... [p1]
+ ... recipe = recipes:clean
+ ...
+ ... [p2]
+ ... recipe = recipes:clean
+ ...
+ ... [p3]
+ ... recipe = recipes:evil_install
+ ...
+ ... [p4]
+ ... recipe = recipes:clean
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Installing p1.
+ Installing p2.
+ Installing p3.
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Updating p1.
+ Updating p2.
+ Installing p3.
+
+ >>> print system(buildout+' buildout:parts='),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling p2.
+ Uninstalling p1.
+
+2. We exit while updating:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = p1 p2 p3 p4
+ ...
+ ... [p1]
+ ... recipe = recipes:clean
+ ...
+ ... [p2]
+ ... recipe = recipes:clean
+ ...
+ ... [p3]
+ ... recipe = recipes:evil_update
+ ...
+ ... [p4]
+ ... recipe = recipes:clean
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Installing p1.
+ Installing p2.
+ Installing p3.
+ Installing p4.
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Updating p1.
+ Updating p2.
+ Updating p3.
+
+ >>> print system(buildout+' buildout:parts='),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling p2.
+ Uninstalling p1.
+ Uninstalling p4.
+ Uninstalling p3.
+
+3. We exit while installing or updating after uninstalling:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = p1 p2 p3 p4
+ ...
+ ... [p1]
+ ... recipe = recipes:evil_update
+ ...
+ ... [p2]
+ ... recipe = recipes:clean
+ ...
+ ... [p3]
+ ... recipe = recipes:clean
+ ...
+ ... [p4]
+ ... recipe = recipes:clean
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Installing p1.
+ Installing p2.
+ Installing p3.
+ Installing p4.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = p1 p2 p3 p4
+ ...
+ ... [p1]
+ ... recipe = recipes:evil_update
+ ...
+ ... [p2]
+ ... recipe = recipes:clean
+ ...
+ ... [p3]
+ ... recipe = recipes:clean
+ ...
+ ... [p4]
+ ... recipe = recipes:clean
+ ... x = 1
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling p4.
+ Updating p1.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = p1 p2 p3 p4
+ ...
+ ... [p1]
+ ... recipe = recipes:clean
+ ...
+ ... [p2]
+ ... recipe = recipes:clean
+ ...
+ ... [p3]
+ ... recipe = recipes:clean
+ ...
+ ... [p4]
+ ... recipe = recipes:clean
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling p1.
+ Installing p1.
+ Updating p2.
+ Updating p3.
+ Installing p4.
+
+ """
+
+def install_source_dist_with_bad_py():
+ """
+
+ >>> mkdir('badegg')
+ >>> mkdir('badegg', 'badegg')
+ >>> write('badegg', 'badegg', '__init__.py', '#\\n')
+ >>> mkdir('badegg', 'badegg', 'scripts')
+ >>> write('badegg', 'badegg', 'scripts', '__init__.py', '#\\n')
+ >>> write('badegg', 'badegg', 'scripts', 'one.py',
+ ... '''
+ ... return 1
+ ... ''')
+
+ >>> write('badegg', 'setup.py',
+ ... '''
+ ... from setuptools import setup, find_packages
+ ... setup(
+ ... name='badegg',
+ ... version='1',
+ ... packages = find_packages('.'),
+ ... zip_safe=False)
+ ... ''')
+
+ >>> print system(buildout+' setup badegg sdist'), # doctest: +ELLIPSIS
+ Running setup script 'badegg/setup.py'.
+ ...
+
+ >>> dist = join('badegg', 'dist')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs bo
+ ... find-links = %(dist)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = badegg
+ ...
+ ... [bo]
+ ... recipe = zc.recipe.egg
+ ... eggs = zc.buildout
+ ... scripts = buildout=bo
+ ... ''' % globals())
+
+ >>> print system(buildout);print 'X' # doctest: +ELLIPSIS
+ Installing eggs.
+ Getting distribution for 'badegg'.
+ Got badegg 1.
+ Installing bo.
+ ...
+ SyntaxError: ...'return' outside function...
+ ...
+ SyntaxError: ...'return' outside function...
+ ...
+
+ >>> ls('eggs') # doctest: +ELLIPSIS
+ d badegg-1-py2.4.egg
+ ...
+
+ >>> ls('bin')
+ - bo
+ - buildout
+ """
+
+def version_requirements_in_build_honored():
+ '''
+
+ >>> update_extdemo()
+ >>> dest = tmpdir('sample-install')
+ >>> mkdir('include')
+ >>> write('include', 'extdemo.h',
+ ... """
+ ... #define EXTDEMO 42
+ ... """)
+
+ >>> zc.buildout.easy_install.build(
+ ... 'extdemo ==1.4', 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']
+
+ '''
+
+def bug_105081_Specific_egg_versions_are_ignored_when_newer_eggs_are_around():
+ """
+ Buildout might ignore a specific egg requirement for a recipe:
+
+ - Have a newer version of an egg in your eggs directory
+ - Use 'recipe==olderversion' in your buildout.cfg to request an
+ older version
+
+ Buildout will go and fetch the older version, but it will *use*
+ the newer version when installing a part with this recipe.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ... find-links = %(sample_eggs)s
+ ...
+ ... [x]
+ ... recipe = zc.recipe.egg
+ ... eggs = demo
+ ... ''' % globals())
+
+ >>> print system(buildout),
+ Installing x.
+ Getting distribution for 'demo'.
+ Got demo 0.4c1.
+ Getting distribution for 'demoneeded'.
+ Got demoneeded 1.2c1.
+ Generated script '/sample-buildout/bin/demo'.
+
+ >>> print system(join('bin', 'demo')),
+ 4 2
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ... find-links = %(sample_eggs)s
+ ...
+ ... [x]
+ ... recipe = zc.recipe.egg
+ ... eggs = demo ==0.1
+ ... ''' % globals())
+
+ >>> print system(buildout),
+ Uninstalling x.
+ Installing x.
+ Getting distribution for 'demo==0.1'.
+ Got demo 0.1.
+ Generated script '/sample-buildout/bin/demo'.
+
+ >>> print system(join('bin', 'demo')),
+ 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 call_py(
+ ... py_path,
+ ... "import tellmy.version; print tellmy.version.__version__"),
+ 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.sitepackage_safe_scripts correctly
+combine the two. We show this with the local recipe that uses the
+function, z3c.recipe.scripts.
+
+To demonstrate this, we will create three packages: tellmy.version 1.0,
+tellmy.version 1.1, and tellmy.fortune 1.0. tellmy.version 1.1 is installed.
+
+ >>> py_path = make_py_with_system_install(make_py, sample_eggs)
+ >>> print call_py(
+ ... py_path,
+ ... "import tellmy.version; print tellmy.version.__version__")
+ 1.1
+ <BLANKLINE>
+
+Now we will create a buildout that creates a script and a faux-Python script.
+We want to see that both can successfully import the specified versions of
+tellmy.version and tellmy.fortune.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ...
+ ... [primed_python]
+ ... executable = %(py_path)s
+ ...
+ ... [eggs]
+ ... recipe = z3c.recipe.scripts
+ ... python = primed_python
+ ... interpreter = py
+ ... include-site-packages = true
+ ... eggs = tellmy.version == 1.0
+ ... tellmy.fortune == 1.0
+ ... demo
+ ... script-initialization =
+ ... import tellmy.version
+ ... print tellmy.version.__version__
+ ... import tellmy.fortune
+ ... print tellmy.fortune.__version__
+ ... ''' % globals())
+
+ >>> print system(buildout)
+ Installing eggs.
+ Getting distribution for 'tellmy.version==1.0'.
+ Got tellmy.version 1.0.
+ Getting distribution for 'tellmy.fortune==1.0'.
+ Got tellmy.fortune 1.0.
+ Getting distribution for 'demo'.
+ Got demo 0.4c1.
+ Getting distribution for 'demoneeded'.
+ Got demoneeded 1.2c1.
+ Generated script '/sample-buildout/bin/demo'.
+ Generated interpreter '/sample-buildout/bin/py'.
+ <BLANKLINE>
+
+Finally, we are ready to see if it worked. Prior to the bug fix that
+this tests, the results of both calls below was the following::
+
+ 1.1
+ Traceback (most recent call last):
+ ...
+ ImportError: No module named fortune
+ <BLANKLINE>
+
+In other words, we got the site-packages version of tellmy.version, and
+we could not import tellmy.fortune at all. The following are the correct
+results for the interpreter and for the script.
+
+ >>> print call_py(
+ ... join('bin', 'py'),
+ ... "import tellmy.version; " +
+ ... "print tellmy.version.__version__; " +
+ ... "import tellmy.fortune; " +
+ ... "print tellmy.fortune.__version__") # doctest: +ELLIPSIS
+ 1.0
+ 1.0...
+
+ >>> print system(join('bin', 'demo'))
+ 1.0
+ 1.0
+ 4 2
+ <BLANKLINE>
+ """
+
+def handle_sys_path_version_hack():
+ r"""
+This is a test for a bugfix.
+
+If you use a Python that has a different version of one of your
+dependencies, and the new package tries to do sys.path tricks in the
+setup.py to get a __version__, and it uses namespace packages, the older
+package will be loaded first, making the setup version the wrong number.
+While very arguably packages simply shouldn't do this, some do, and we
+don't want buildout to fall over when they do.
+
+To demonstrate this, we will need to create a distribution that has one of
+these unpleasant tricks, and a Python that has an older version installed.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> for version in ('1.0', '1.1'):
+ ... tmp = tempfile.mkdtemp()
+ ... try:
+ ... write(tmp, 'README.txt', '')
+ ... mkdir(tmp, 'src')
+ ... mkdir(tmp, 'src', 'tellmy')
+ ... write(tmp, 'src', 'tellmy', '__init__.py',
+ ... "__import__("
+ ... "'pkg_resources').declare_namespace(__name__)\n")
+ ... mkdir(tmp, 'src', 'tellmy', 'version')
+ ... write(tmp, 'src', 'tellmy', 'version',
+ ... '__init__.py', '__version__=%r\n' % version)
+ ... write(
+ ... tmp, 'setup.py',
+ ... "from setuptools import setup\n"
+ ... "import sys\n"
+ ... "sys.path.insert(0, 'src')\n"
+ ... "from tellmy.version import __version__\n"
+ ... "setup(\n"
+ ... " name='tellmy.version',\n"
+ ... " package_dir = {'': 'src'},\n"
+ ... " packages = ['tellmy', 'tellmy.version'],\n"
+ ... " install_requires = ['setuptools'],\n"
+ ... " namespace_packages=['tellmy'],\n"
+ ... " zip_safe=True, version=__version__,\n"
+ ... " author='bob', url='bob', author_email='bob')\n"
+ ... )
+ ... zc.buildout.testing.sdist(tmp, sample_eggs)
+ ... if version == '1.0':
+ ... # We install the 1.0 version in site packages the way a
+ ... # system packaging system (debs, rpms) would do it.
+ ... zc.buildout.testing.sys_install(tmp, site_packages_path)
+ ... finally:
+ ... shutil.rmtree(tmp)
+ >>> print call_py(
+ ... py_path,
+ ... "import tellmy.version; print tellmy.version.__version__")
+ 1.0
+ <BLANKLINE>
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(sample_eggs)s
+ ...
+ ... [primed_python]
+ ... executable = %(py_path)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... python = primed_python
+ ... eggs = tellmy.version == 1.1
+ ... ''' % globals())
+
+Before the bugfix, running this buildout would generate this error:
+
+ Installing eggs.
+ Getting distribution for 'tellmy.version==1.1'.
+ Installing tellmy.version 1.1
+ Caused installation of a distribution:
+ tellmy.version 1.0
+ with a different version.
+ Got None.
+ While:
+ Installing eggs.
+ Error: There is a version conflict.
+ We already have: tellmy.version 1.0
+ <BLANKLINE>
+
+You can see the copiously commented fix for this in easy_install.py (see
+zc.buildout.easy_install.Installer._call_easy_install and particularly
+the comment leading up to zc.buildout.easy_install._easy_install_cmd).
+Now the install works correctly, as seen here.
+
+ >>> print system(buildout)
+ Installing eggs.
+ Getting distribution for 'tellmy.version==1.1'.
+ Got tellmy.version 1.1.
+ <BLANKLINE>
+
+ """
+
+def isolated_include_site_packages():
+ """
+
+This is an isolated test of the include_site_packages functionality, passing
+the argument directly to install, overriding a default.
+
+Our "py_path" has the "demoneeded" and "demo" packages available. We'll
+simply be asking for "demoneeded" here.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+ >>> zc.buildout.easy_install.include_site_packages(False)
+ True
+
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None, include_site_packages=True)
+ >>> [dist.project_name for dist in workingset]
+ ['demoneeded']
+
+That worked fine. Let's try again with site packages not allowed (and
+reversing the default).
+
+ >>> zc.buildout.easy_install.include_site_packages(True)
+ False
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None, include_site_packages=False)
+ Traceback (most recent call last):
+ ...
+ MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+That's a failure, as expected.
+
+Now we explore an important edge case.
+
+Some system Pythons include setuptools (and other Python packages) in their
+site-packages (or equivalent) using a .egg-info directory. The pkg_resources
+module (from setuptools) considers a package installed using .egg-info to be a
+develop egg.
+
+zc.buildout.buildout.Buildout.bootstrap will make setuptools and zc.buildout
+available to the buildout via the eggs directory, for normal eggs; or the
+develop-eggs directory, for develop-eggs.
+
+If setuptools or zc.buildout is found in site-packages and considered by
+pkg_resources to be a develop egg, then the bootstrap code will use a .egg-link
+in the local develop-eggs, pointing to site-packages, in its entirety. Because
+develop-eggs must always be available for searching for distributions, this
+indirectly brings site-packages back into the search path for distributions.
+
+Because of this, we have to take special care that we still exclude
+site-packages even in this case. See the comments about site packages in the
+Installer._satisfied and Installer._obtain methods for the implementation
+(as of this writing).
+
+In this demonstration, we insert a link to the "demoneeded" distribution
+in our develop-eggs, which would bring the package back in, except for
+the special care we have taken to exclude it.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> mkdir(example_dest, 'develop-eggs')
+ >>> write(example_dest, 'develop-eggs', 'demoneeded.egg-link',
+ ... site_packages_path)
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[],
+ ... path=[join(example_dest, 'develop-eggs')],
+ ... executable=py_path,
+ ... index=None, include_site_packages=False)
+ Traceback (most recent call last):
+ ...
+ MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+The MissingDistribution error shows that buildout correctly excluded the
+"site-packages" source even though it was indirectly included in the path
+via a .egg-link file.
+
+ """
+
+def include_site_packages_bug_623590():
+ """
+As mentioned in isolated_include_site_packages, some system Pythons
+include various Python packages in their site-packages (or equivalent)
+using a .egg-info directory. The pkg_resources module (from setuptools)
+considers a package installed using .egg-info to be a develop egg
+
+We generally prefer develop eggs when we are selecting dependencies, because
+we expect them to be eggs that buildout has been told to develop. However,
+we should not consider these site-packages eggs as develop eggs--they should
+not have automatic precedence over eggs available elsewhere.
+
+We have specific code to handle this case, as identified in bug 623590.
+See zc.buildout.easy_install.Installer._satisfied, as of this writing,
+for the pertinent code. Here's the test for the bugfix.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+ >>> zc.buildout.easy_install.include_site_packages(False)
+ True
+
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demo'], example_dest, links=[sample_eggs], executable=py_path,
+ ... index=None, include_site_packages=True, prefer_final=False)
+ >>> [(dist.project_name, dist.version) for dist in workingset]
+ [('demo', '0.4c1'), ('demoneeded', '1.2c1')]
+"""
+
+def allowed_eggs_from_site_packages():
+ """
+Sometimes you need or want to control what eggs from site-packages are used.
+The allowed-eggs-from-site-packages option allows you to specify a whitelist of
+project names that may be included from site-packages. You can use globs to
+specify the value. It defaults to a single value of '*', indicating that any
+package may come from site-packages.
+
+This option interacts with include-site-packages in the following ways.
+
+If include-site-packages is true, then allowed-eggs-from-site-packages filters
+what eggs from site-packages may be chosen. If allowed-eggs-from-site-packages
+is an empty list, then no eggs from site-packages are chosen, but site-packages
+will still be included at the end of path lists.
+
+If include-site-packages is false, allowed-eggs-from-site-packages is
+irrelevant.
+
+This test shows the interaction with the zc.buildout.easy_install API. Another
+test below (allow_site_package_eggs_option) shows using it with a buildout.cfg.
+
+Our "py_path" has the "demoneeded" and "demo" packages available. We'll
+simply be asking for "demoneeded" here.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None,
+ ... allowed_eggs_from_site_packages=['demoneeded', 'other'])
+ >>> [dist.project_name for dist in workingset]
+ ['demoneeded']
+
+That worked fine. It would work fine for a glob too.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None,
+ ... allowed_eggs_from_site_packages=['?emon*', 'other'])
+ >>> [dist.project_name for dist in workingset]
+ ['demoneeded']
+
+But now let's try again with 'demoneeded' not allowed.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None,
+ ... allowed_eggs_from_site_packages=['demo'])
+ Traceback (most recent call last):
+ ...
+ MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+Here's the same, but with an empty list.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None,
+ ... allowed_eggs_from_site_packages=[])
+ Traceback (most recent call last):
+ ...
+ MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+Of course, this doesn't stop us from getting a package from elsewhere. Here,
+we add a link server.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, executable=py_path,
+ ... links=[link_server], index=link_server+'index/',
+ ... allowed_eggs_from_site_packages=['other'])
+ >>> [dist.project_name for dist in workingset]
+ ['demoneeded']
+ >>> [dist.location for dist in workingset]
+ ['/site-packages-example-install/demoneeded-1.1-py2.6.egg']
+
+Finally, here's an example of an interaction: we say that it is OK to
+allow the "demoneeded" egg to come from site-packages, but we don't
+include-site-packages.
+
+ >>> zc.buildout.easy_install.clear_index_cache()
+ >>> rmdir(example_dest)
+ >>> example_dest = tmpdir('site-packages-example-install')
+ >>> workingset = zc.buildout.easy_install.install(
+ ... ['demoneeded'], example_dest, links=[], executable=py_path,
+ ... index=None, include_site_packages=False,
+ ... allowed_eggs_from_site_packages=['demoneeded'])
+ Traceback (most recent call last):
+ ...
+ MissingDistribution: Couldn't find a distribution for 'demoneeded'.
+
+ """
+
+def allowed_eggs_from_site_packages_dependencies_bugfix():
+ """
+If you specify that a package with a dependency may come from site-packages,
+that doesn't mean that the dependency may come from site-packages. This
+is a test for a bug fix to verify that this is true.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+ >>> interpreter_dir = tmpdir('interpreter')
+ >>> interpreter_parts_dir = os.path.join(
+ ... interpreter_dir, 'parts', 'interpreter')
+ >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+ >>> mkdir(interpreter_bin_dir)
+ >>> mkdir(interpreter_dir, 'eggs')
+ >>> mkdir(interpreter_dir, 'parts')
+ >>> mkdir(interpreter_parts_dir)
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo'], join(interpreter_dir, 'eggs'), executable=py_path,
+ ... links=[link_server], index=link_server+'index/',
+ ... allowed_eggs_from_site_packages=['demo'])
+ >>> [dist.project_name for dist in ws]
+ ['demo', 'demoneeded']
+ >>> from pprint import pprint
+ >>> pprint([dist.location for dist in ws])
+ ['/executable_buildout/site-packages',
+ '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+ """
+
+def allowed_eggs_from_site_packages_bug_592524():
+ """
+When we use allowed_eggs_from_site_packages, we need to make sure that the
+site-packages paths are not inserted with the normal egg paths. They already
+included at the end, and including them along with the normal egg paths will
+possibly mask subsequent egg paths. This affects interpreters and scripts
+generated by sitepackage_safe_scripts.
+
+Our "py_path" has the "demoneeded" and "demo" packages available.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+ >>> interpreter_dir = tmpdir('interpreter')
+ >>> interpreter_parts_dir = os.path.join(
+ ... interpreter_dir, 'parts', 'interpreter')
+ >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+ >>> mkdir(interpreter_bin_dir)
+ >>> mkdir(interpreter_dir, 'eggs')
+ >>> mkdir(interpreter_dir, 'parts')
+ >>> mkdir(interpreter_parts_dir)
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo', 'other'], join(interpreter_dir, 'eggs'), executable=py_path,
+ ... links=[link_server], index=link_server+'index/',
+ ... allowed_eggs_from_site_packages=['demo'])
+ >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+ ... interpreter_bin_dir, ws, py_path, interpreter_parts_dir,
+ ... interpreter='py', include_site_packages=True)
+
+Now we will look at the paths in the site.py we generated. Notice that the
+site-packages are at the end. They were not before this bugfix.
+
+ >>> test = 'import pprint, sys; pprint.pprint(sys.path[-4:])'
+ >>> print call_py(join(interpreter_bin_dir, 'py'), test)
+ ['/interpreter/eggs/other-1.0-pyN.N.egg',
+ '/interpreter/eggs/demoneeded-1.1-pyN.N.egg',
+ '/executable_buildout/eggs/setuptools-0.0-pyN.N.egg',
+ '/executable_buildout/site-packages']
+ <BLANKLINE>
+ """
+
+def subprocesses_have_same_environment_by_default():
+ """
+The scripts generated by sitepackage_safe_scripts set the PYTHONPATH so that,
+if the environment is maintained (the default behavior), subprocesses get
+the same Python packages.
+
+First, we set up a script and an interpreter.
+
+ >>> interpreter_dir = tmpdir('interpreter')
+ >>> interpreter_parts_dir = os.path.join(
+ ... interpreter_dir, 'parts', 'interpreter')
+ >>> interpreter_bin_dir = os.path.join(interpreter_dir, 'bin')
+ >>> mkdir(interpreter_bin_dir)
+ >>> mkdir(interpreter_dir, 'eggs')
+ >>> mkdir(interpreter_dir, 'parts')
+ >>> mkdir(interpreter_parts_dir)
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo'], join(interpreter_dir, 'eggs'), links=[link_server],
+ ... index=link_server+'index/')
+ >>> test = (
+ ... "import subprocess, sys; subprocess.call("
+ ... "[sys.executable, '-c', "
+ ... "'import eggrecipedemo; print eggrecipedemo.x'])")
+ >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+ ... interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+ ... reqs=['demo'], interpreter='py',
+ ... script_initialization=test + '; sys.exit(0)')
+
+This works for the script.
+
+ >>> print system(join(interpreter_bin_dir, 'demo'))
+ 3
+ <BLANKLINE>
+
+This also works for the generated interpreter.
+
+ >>> print call_py(join(interpreter_bin_dir, 'py'), test)
+ 3
+ <BLANKLINE>
+
+If you have a PYTHONPATH in your environment, it will be honored, after
+the buildout-generated path.
+
+ >>> original_pythonpath = os.environ.get('PYTHONPATH')
+ >>> os.environ['PYTHONPATH'] = 'foo'
+ >>> test = (
+ ... "import subprocess, sys; subprocess.call("
+ ... "[sys.executable, '-c', "
+ ... "'import sys, pprint; pprint.pprint(sys.path)'])")
+ >>> generated = zc.buildout.easy_install.sitepackage_safe_scripts(
+ ... interpreter_bin_dir, ws, sys.executable, interpreter_parts_dir,
+ ... reqs=['demo'], interpreter='py',
+ ... script_initialization=test + '; sys.exit(0)')
+
+This works for the script. As you can see, /sample_buildout/foo is included
+right after the "parts" directory that contains site.py and sitecustomize.py.
+You can also see, actually more easily than in the other example, that we
+have the desired eggs available.
+
+ >>> print system(join(interpreter_bin_dir, 'demo')), # doctest: +ELLIPSIS
+ ['',
+ '/interpreter/parts/interpreter',
+ '/sample-buildout/foo',
+ ...
+ '/interpreter/eggs/demo-0.3-pyN.N.egg',
+ '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+This also works for the generated interpreter, with identical results.
+
+ >>> print call_py(join(interpreter_bin_dir, 'py'), test),
+ ... # doctest: +ELLIPSIS
+ ['',
+ '/interpreter/parts/interpreter',
+ '/sample-buildout/foo',
+ ...
+ '/interpreter/eggs/demo-0.3-pyN.N.egg',
+ '/interpreter/eggs/demoneeded-1.1-pyN.N.egg']
+
+ >>> # Cleanup
+ >>> if original_pythonpath:
+ ... os.environ['PYTHONPATH'] = original_pythonpath
+ ... else:
+ ... del os.environ['PYTHONPATH']
+ ...
+
+ """
+
+def bootstrap_makes_buildout_that_works_with_system_python():
+ r"""
+In order to work smoothly with a system Python, bootstrapping creates
+the buildout script with
+zc.buildout.easy_install.sitepackage_safe_scripts. If it did not, a
+variety of problems might happen. For instance, if another version of
+buildout or setuptools is installed in the site-packages than is
+desired, it may cause a problem.
+
+A problem actually experienced in the field is when
+a recipe wants a different version of a dependency that is installed in
+site-packages. We will create a similar situation, and show that it is now
+handled.
+
+First let's write a dummy recipe.
+
+ >>> mkdir(sample_buildout, 'recipes')
+ >>> write(sample_buildout, 'recipes', 'dummy.py',
+ ... '''
+ ... import logging, os, zc.buildout
+ ... class Dummy:
+ ... def __init__(self, buildout, name, options):
+ ... pass
+ ... def install(self):
+ ... return ()
+ ... def update(self):
+ ... pass
+ ... ''')
+ >>> write(sample_buildout, 'recipes', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ...
+ ... setup(
+ ... name = "recipes",
+ ... entry_points = {'zc.buildout': ['dummy = dummy:Dummy']},
+ ... install_requires = 'demoneeded==1.2c1',
+ ... )
+ ... ''')
+ >>> write(sample_buildout, 'recipes', 'README.txt', " ")
+
+Now we'll try to use it with a Python that has a different version of
+demoneeded installed.
+
+ >>> py_path, site_packages_path = make_py()
+ >>> create_sample_sys_install(site_packages_path)
+ >>> rmdir('develop-eggs')
+ >>> from zc.buildout.testing import make_buildout
+ >>> make_buildout(executable=py_path)
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = recipes
+ ... parts = dummy
+ ... find-links = %(link_server)s
+ ... executable = %(py_path)s
+ ...
+ ... [dummy]
+ ... recipe = recipes:dummy
+ ... ''' % globals())
+
+Now we actually run the buildout. Before the change, we got the following
+error:
+
+ Develop: '/sample-buildout/recipes'
+ While:
+ Installing.
+ Getting section dummy.
+ Initializing section dummy.
+ Installing recipe recipes.
+ Error: There is a version conflict.
+ We already have: demoneeded 1.1
+ but recipes 0.0.0 requires 'demoneeded==1.2c1'.
+
+Now, it is handled smoothly.
+
+ >>> print system(buildout)
+ Develop: '/sample-buildout/recipes'
+ Getting distribution for 'demoneeded==1.2c1'.
+ Got demoneeded 1.2c1.
+ Installing dummy.
+ <BLANKLINE>
+
+Here's the same story with a namespace package, which has some additional
+complications behind the scenes. First, a recipe, in the "tellmy" namespace.
+
+ >>> mkdir(sample_buildout, 'ns')
+ >>> mkdir(sample_buildout, 'ns', 'tellmy')
+ >>> write(sample_buildout, 'ns', 'tellmy', '__init__.py',
+ ... "__import__('pkg_resources').declare_namespace(__name__)\n")
+ >>> mkdir(sample_buildout, 'ns', 'tellmy', 'recipes')
+ >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', '__init__.py', ' ')
+ >>> write(sample_buildout, 'ns', 'tellmy', 'recipes', 'dummy.py',
+ ... '''
+ ... import logging, os, zc.buildout
+ ... class Dummy:
+ ... def __init__(self, buildout, name, options):
+ ... pass
+ ... def install(self):
+ ... return ()
+ ... def update(self):
+ ... pass
+ ... ''')
+ >>> write(sample_buildout, 'ns', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(
+ ... name="tellmy.recipes",
+ ... packages=['tellmy', 'tellmy.recipes'],
+ ... install_requires=['setuptools'],
+ ... namespace_packages=['tellmy'],
+ ... entry_points = {'zc.buildout':
+ ... ['dummy = tellmy.recipes.dummy:Dummy']},
+ ... )
+ ... ''')
+
+Now, a buildout that uses it.
+
+ >>> create_sample_namespace_eggs(sample_eggs, site_packages_path)
+ >>> rmdir('develop-eggs')
+ >>> from zc.buildout.testing import make_buildout
+ >>> make_buildout(executable=py_path)
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = ns
+ ... recipes
+ ... parts = dummy
+ ... find-links = %(link_server)s
+ ... executable = %(py_path)s
+ ...
+ ... [dummy]
+ ... recipe = tellmy.recipes:dummy
+ ... ''' % globals())
+
+Now we actually run the buildout.
+
+ >>> print system(buildout)
+ Develop: '/sample-buildout/ns'
+ Develop: '/sample-buildout/recipes'
+ Uninstalling dummy.
+ Installing dummy.
+ <BLANKLINE>
+
+ """
+
+if sys.version_info > (2, 4):
+ def test_exit_codes():
+ """
+ >>> import subprocess
+ >>> def call(s):
+ ... p = subprocess.Popen(s, stdin=subprocess.PIPE,
+ ... stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ ... p.stdin.close()
+ ... print p.stdout.read()
+ ... print 'Exit:', bool(p.wait())
+
+ >>> call(buildout)
+ <BLANKLINE>
+ Exit: False
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ... ''')
+
+ >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE
+ While:
+ Installing.
+ Getting section x.
+ Error: The referenced section, 'x', was not defined.
+ <BLANKLINE>
+ Exit: True
+
+ >>> write('setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='zc.buildout.testexit', entry_points={
+ ... 'zc.buildout': ['default = testexitrecipe:x']})
+ ... ''')
+
+ >>> write('testexitrecipe.py',
+ ... '''
+ ... x y
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = x
+ ... develop = .
+ ...
+ ... [x]
+ ... recipe = zc.buildout.testexit
+ ... ''')
+
+ >>> call(buildout) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
+ Develop: '/sample-buildout/.'
+ While:
+ Installing.
+ Getting section x.
+ Initializing section x.
+ Loading zc.buildout recipe entry zc.buildout.testexit:default.
+ <BLANKLINE>
+ An internal error occurred due to a bug in either zc.buildout or in a
+ recipe being used:
+ Traceback (most recent call last):
+ ...
+ x y
+ ^
+ SyntaxError: invalid syntax
+ <BLANKLINE>
+ Exit: True
+ """
+
+def bug_59270_recipes_always_start_in_buildout_dir():
+ """
+ Recipes can rely on running from buildout directory
+
+ >>> mkdir('bad_start')
+ >>> write('bad_recipe.py',
+ ... '''
+ ... import os
+ ... class Bad:
+ ... def __init__(self, *_):
+ ... print os.getcwd()
+ ... def install(self):
+ ... print os.getcwd()
+ ... os.chdir('bad_start')
+ ... print os.getcwd()
+ ... return ()
+ ... ''')
+
+ >>> write('setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='bad.test',
+ ... entry_points={'zc.buildout': ['default=bad_recipe:Bad']},)
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = .
+ ... parts = b1 b2
+ ... [b1]
+ ... recipe = bad.test
+ ... [b2]
+ ... recipe = bad.test
+ ... ''')
+
+ >>> os.chdir('bad_start')
+ >>> print system(join(sample_buildout, 'bin', 'buildout')
+ ... +' -c '+join(sample_buildout, 'buildout.cfg')),
+ Develop: '/sample-buildout/.'
+ /sample-buildout
+ /sample-buildout
+ Installing b1.
+ /sample-buildout
+ /sample-buildout/bad_start
+ Installing b2.
+ /sample-buildout
+ /sample-buildout/bad_start
+
+ """
+
+def bug_61890_file_urls_dont_seem_to_work_in_find_dash_links():
+ """
+
+ This bug arises from the fact that setuptools is overly restrictive
+ about file urls, requiring that file urls pointing at directories
+ must end in a slash.
+
+ >>> dest = tmpdir('sample-install')
+ >>> import zc.buildout.easy_install
+ >>> sample_eggs = sample_eggs.replace(os.path.sep, '/')
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['demo==0.2'], dest,
+ ... links=['file://'+sample_eggs], index=link_server+'index/')
+
+
+ >>> for dist in ws:
+ ... print dist
+ demo 0.2
+ demoneeded 1.1
+
+ >>> ls(dest)
+ - demo-0.2-py2.4.egg
+ - demoneeded-1.1-py2.4.egg
+
+ """
+
+def bug_75607_buildout_should_not_run_if_it_creates_an_empty_buildout_cfg():
+ """
+ >>> remove('buildout.cfg')
+ >>> print system(buildout),
+ While:
+ Initializing.
+ Error: Couldn't open /sample-buildout/buildout.cfg
+
+
+
+ """
+
+def dealing_with_extremely_insane_dependencies():
+ r"""
+
+ There was a problem with analysis of dependencies taking a long
+ time, in part because the analysis would get repeated every time a
+ package was encountered in a dependency list. Now, we don't do
+ the analysis any more:
+
+ >>> import os
+ >>> for i in range(5):
+ ... p = 'pack%s' % i
+ ... deps = [('pack%s' % j) for j in range(5) if j is not i]
+ ... if i == 4:
+ ... deps.append('pack5')
+ ... mkdir(p)
+ ... write(p, 'setup.py',
+ ... 'from setuptools import setup\n'
+ ... 'setup(name=%r, install_requires=%r,\n'
+ ... ' url="u", author="a", author_email="e")\n'
+ ... % (p, deps))
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = pack0 pack1 pack2 pack3 pack4
+ ... parts = pack1
+ ...
+ ... [pack1]
+ ... recipe = zc.recipe.egg:eggs
+ ... eggs = pack0
+ ... ''')
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/pack0'
+ Develop: '/sample-buildout/pack1'
+ Develop: '/sample-buildout/pack2'
+ Develop: '/sample-buildout/pack3'
+ Develop: '/sample-buildout/pack4'
+ Installing pack1.
+ Couldn't find index page for 'pack5' (maybe misspelled?)
+ Getting distribution for 'pack5'.
+ While:
+ Installing pack1.
+ Getting distribution for 'pack5'.
+ Error: Couldn't find a distribution for 'pack5'.
+
+ However, if we run in verbose mode, we can see why packages were included:
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ We have a develop egg: zc.buildout 1.0.0
+ We have the best distribution that satisfies 'setuptools'.
+ Picked: setuptools = 0.6
+ Develop: '/sample-buildout/pack0'
+ Develop: '/sample-buildout/pack1'
+ Develop: '/sample-buildout/pack2'
+ Develop: '/sample-buildout/pack3'
+ Develop: '/sample-buildout/pack4'
+ ...Installing pack1.
+ Installing 'pack0'.
+ We have a develop egg: pack0 0.0.0
+ Getting required 'pack4'
+ required by pack0 0.0.0.
+ We have a develop egg: pack4 0.0.0
+ Getting required 'pack3'
+ required by pack0 0.0.0.
+ required by pack4 0.0.0.
+ We have a develop egg: pack3 0.0.0
+ Getting required 'pack2'
+ required by pack0 0.0.0.
+ required by pack3 0.0.0.
+ required by pack4 0.0.0.
+ We have a develop egg: pack2 0.0.0
+ Getting required 'pack1'
+ required by pack0 0.0.0.
+ required by pack2 0.0.0.
+ required by pack3 0.0.0.
+ required by pack4 0.0.0.
+ We have a develop egg: pack1 0.0.0
+ Getting required 'pack5'
+ required by pack4 0.0.0.
+ We have no distributions for pack5 that satisfies 'pack5'.
+ Couldn't find index page for 'pack5' (maybe misspelled?)
+ Getting distribution for 'pack5'.
+ While:
+ Installing pack1.
+ Getting distribution for 'pack5'.
+ Error: Couldn't find a distribution for 'pack5'.
+ """
+
+def read_find_links_to_load_extensions():
+ """
+We'll create a wacky buildout extension that is just another name for http:
+
+ >>> src = tmpdir('src')
+ >>> write(src, 'wacky_handler.py',
+ ... '''
+ ... import urllib2
+ ... class Wacky(urllib2.HTTPHandler):
+ ... wacky_open = urllib2.HTTPHandler.http_open
+ ... def install(buildout=None):
+ ... urllib2.install_opener(urllib2.build_opener(Wacky))
+ ... ''')
+ >>> write(src, 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='wackyextension', version='1',
+ ... py_modules=['wacky_handler'],
+ ... entry_points = {'zc.buildout.extension':
+ ... ['default = wacky_handler:install']
+ ... },
+ ... )
+ ... ''')
+ >>> print system(buildout+' setup '+src+' bdist_egg'),
+ ... # doctest: +ELLIPSIS
+ Running setup ...
+ creating 'dist/wackyextension-1-...
+
+Now we'll create a buildout that uses this extension to load other packages:
+
+ >>> wacky_server = link_server.replace('http', 'wacky')
+ >>> dist = 'file://' + join(src, 'dist').replace(os.path.sep, '/')
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = demo
+ ... extensions = wackyextension
+ ... find-links = %(wacky_server)s/demoneeded-1.0.zip
+ ... %(dist)s
+ ... [demo]
+ ... recipe = zc.recipe.egg
+ ... eggs = demoneeded
+ ... ''' % globals())
+
+When we run the buildout. it will load the extension from the dist
+directory and then use the wacky extension to load the demo package
+
+ >>> print system(buildout),
+ Getting distribution for 'wackyextension'.
+ Got wackyextension 1.
+ Installing demo.
+ Getting distribution for 'demoneeded'.
+ Got demoneeded 1.0.
+
+ """
+
+def distributions_from_local_find_links_make_it_to_download_cache():
+ """
+
+If we specify a local directory in find links, distors found there
+need to make it to the download cache.
+
+ >>> mkdir('test')
+ >>> write('test', 'setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='foo')
+ ... ''')
+
+ >>> print system(buildout+' setup test bdist_egg'), # doctest: +ELLIPSIS
+ Running setup script 'test/setup.py'.
+ ...
+
+
+ >>> mkdir('cache')
+ >>> old_cache = zc.buildout.easy_install.download_cache('cache')
+ >>> list(zc.buildout.easy_install.install(['foo'], 'eggs',
+ ... links=[join('test', 'dist')])) # doctest: +ELLIPSIS
+ [foo 0.0.0 ...
+
+ >>> ls('cache')
+ - foo-0.0.0-py2.4.egg
+
+ >>> _ = zc.buildout.easy_install.download_cache(old_cache)
+
+ """
+
+def create_egg(name, version, dest, install_requires=None,
+ dependency_links=None):
+ d = tempfile.mkdtemp()
+ if dest=='available':
+ extras = dict(x=['x'])
+ else:
+ extras = {}
+ if dependency_links:
+ links = 'dependency_links = %s, ' % dependency_links
+ else:
+ links = ''
+ if install_requires:
+ requires = 'install_requires = %s, ' % install_requires
+ else:
+ requires = ''
+ try:
+ open(os.path.join(d, 'setup.py'), 'w').write(
+ 'from setuptools import setup\n'
+ 'setup(name=%r, version=%r, extras_require=%r, zip_safe=True,\n'
+ ' %s %s py_modules=["setup"]\n)'
+ % (name, str(version), extras, requires, links)
+ )
+ zc.buildout.testing.bdist_egg(d, sys.executable, os.path.abspath(dest))
+ finally:
+ shutil.rmtree(d)
+
+def prefer_final_permutation(existing, available):
+ for d in ('existing', 'available'):
+ if os.path.exists(d):
+ shutil.rmtree(d)
+ os.mkdir(d)
+ for version in existing:
+ create_egg('spam', version, 'existing')
+ for version in available:
+ create_egg('spam', version, 'available')
+
+ zc.buildout.easy_install.clear_index_cache()
+ [dist] = list(
+ zc.buildout.easy_install.install(['spam'], 'existing', ['available'],
+ always_unzip=True)
+ )
+
+ if dist.extras:
+ print 'downloaded', dist.version
+ else:
+ print 'had', dist.version
+ sys.path_importer_cache.clear()
+
+def prefer_final():
+ """
+This test tests several permutations:
+
+Using different version numbers to work around zip importer cache problems. :(
+
+- With prefer final:
+
+ - no existing and newer dev available
+ >>> prefer_final_permutation((), [1, '2a1'])
+ downloaded 1
+
+ - no existing and only dev available
+ >>> prefer_final_permutation((), ['3a1'])
+ downloaded 3a1
+
+ - final existing and only dev acailable
+ >>> prefer_final_permutation([4], ['5a1'])
+ had 4
+
+ - final existing and newer final available
+ >>> prefer_final_permutation([6], [7])
+ downloaded 7
+
+ - final existing and same final available
+ >>> prefer_final_permutation([8], [8])
+ had 8
+
+ - final existing and older final available
+ >>> prefer_final_permutation([10], [9])
+ had 10
+
+ - only dev existing and final available
+ >>> prefer_final_permutation(['12a1'], [11])
+ downloaded 11
+
+ - only dev existing and no final available newer dev available
+ >>> prefer_final_permutation(['13a1'], ['13a2'])
+ downloaded 13a2
+
+ - only dev existing and no final available older dev available
+ >>> prefer_final_permutation(['15a1'], ['14a1'])
+ had 15a1
+
+ - only dev existing and no final available same dev available
+ >>> prefer_final_permutation(['16a1'], ['16a1'])
+ had 16a1
+
+- Without prefer final:
+
+ >>> _ = zc.buildout.easy_install.prefer_final(False)
+
+ - no existing and newer dev available
+ >>> prefer_final_permutation((), [18, '19a1'])
+ downloaded 19a1
+
+ - no existing and only dev available
+ >>> prefer_final_permutation((), ['20a1'])
+ downloaded 20a1
+
+ - final existing and only dev acailable
+ >>> prefer_final_permutation([21], ['22a1'])
+ downloaded 22a1
+
+ - final existing and newer final available
+ >>> prefer_final_permutation([23], [24])
+ downloaded 24
+
+ - final existing and same final available
+ >>> prefer_final_permutation([25], [25])
+ had 25
+
+ - final existing and older final available
+ >>> prefer_final_permutation([27], [26])
+ had 27
+
+ - only dev existing and final available
+ >>> prefer_final_permutation(['29a1'], [28])
+ had 29a1
+
+ - only dev existing and no final available newer dev available
+ >>> prefer_final_permutation(['30a1'], ['30a2'])
+ downloaded 30a2
+
+ - only dev existing and no final available older dev available
+ >>> prefer_final_permutation(['32a1'], ['31a1'])
+ had 32a1
+
+ - only dev existing and no final available same dev available
+ >>> prefer_final_permutation(['33a1'], ['33a1'])
+ had 33a1
+
+ >>> _ = zc.buildout.easy_install.prefer_final(True)
+
+ """
+
+def buildout_prefer_final_option():
+ """
+The prefer-final buildout option can be used for override the default
+preference for newer distributions.
+
+The default is prefer-final = false:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... eggs = demo
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ ...
+ Picked: demo = 0.4c1
+ ...
+ Picked: demoneeded = 1.2c1
+
+Here we see that the final versions of demo and demoneeded are used.
+We get the same behavior if we add prefer-final = false
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ... prefer-final = false
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... eggs = demo
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ ...
+ Picked: demo = 0.4c1
+ ...
+ Picked: demoneeded = 1.2c1
+
+If we specify prefer-final = true, we'll get the newest
+distributions:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ... prefer-final = true
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... eggs = demo
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing 'zc.buildout', 'setuptools'.
+ ...
+ Picked: demo = 0.3
+ ...
+ Picked: demoneeded = 1.1
+
+We get an error if we specify anything but true or false:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ... prefer-final = no
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg:eggs
+ ... eggs = demo
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ While:
+ Initializing.
+ Error: Invalid value for prefer-final option: no
+
+ """
+
+def buildout_prefer_final_build_system_option():
+ """
+The accept-buildout-test-releases buildout option can be used for overriding
+the default preference for final distributions for recipes, buildout
+extensions, and buildout itself. It is usually controlled via the bootstrap
+script rather than in the configuration file, but we will test the machinery
+using the file.
+
+Set up. This creates sdists for demorecipe 1.0 and 1.1b1, and for
+demoextension 1.0 and 1.1b1.
+
+ >>> create_sample_recipe_sdists(sample_eggs)
+ >>> create_sample_extension_sdists(sample_eggs)
+
+The default is accept-buildout-test-releases = false:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = demo
+ ... find-links = %(link_server)s
+ ... extensions = demoextension
+ ...
+ ... [demo]
+ ... recipe = demorecipe
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing ...
+ Picked: demoextension = 1.0
+ ...
+ Picked: demorecipe = 1.0
+ ...
+
+Here we see that the final versions of demorecipe and demoextension were used.
+
+We get the same behavior if we explicitly state that
+accept-buildout-test-releases = false.
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = demo
+ ... find-links = %(link_server)s
+ ... extensions = demoextension
+ ... accept-buildout-test-releases = false
+ ...
+ ... [demo]
+ ... recipe = demorecipe
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing ...
+ Picked: demoextension = 1.0
+ ...
+ Picked: demorecipe = 1.0
+ ...
+
+If we specify accept-buildout-test-releases = true, we'll get the newest
+distributions in the build system:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = demo
+ ... find-links = %(link_server)s
+ ... extensions = demoextension
+ ... accept-buildout-test-releases = true
+ ...
+ ... [demo]
+ ... recipe = demorecipe
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ Installing ...
+ Picked: demoextension = 1.1b1
+ ...
+ Picked: demorecipe = 1.1b1
+ ...
+
+We get an error if we specify anything but true or false:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = demo
+ ... find-links = %(link_server)s
+ ... extensions = demoextension
+ ... accept-buildout-test-releases = no
+ ...
+ ... [demo]
+ ... recipe = demorecipe
+ ... ''' % globals())
+
+ >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
+ While:
+ Initializing.
+ Error: Invalid value for accept-buildout-test-releases option: no
+
+ """
+
+def develop_with_modules():
+ """
+Distribution setup scripts can import modules in the distribution directory:
+
+ >>> mkdir('foo')
+ >>> write('foo', 'bar.py',
+ ... '''# empty
+ ... ''')
+
+ >>> write('foo', 'setup.py',
+ ... '''
+ ... import bar
+ ... from setuptools import setup
+ ... setup(name="foo")
+ ... ''')
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... develop = foo
+ ... parts =
+ ... ''')
+
+ >>> print system(join('bin', 'buildout')),
+ Develop: '/sample-buildout/foo'
+
+ >>> ls('develop-eggs')
+ - foo.egg-link
+ - z3c.recipe.scripts.egg-link
+ - zc.recipe.egg.egg-link
+
+ """
+
+def dont_pick_setuptools_if_version_is_specified_when_required_by_src_dist():
+ """
+When installing a source distribution, we got setuptools without
+honoring our version specification.
+
+ >>> mkdir('dist')
+ >>> write('setup.py',
+ ... '''
+ ... from setuptools import setup
+ ... setup(name='foo', version='1', py_modules=['foo'], zip_safe=True)
+ ... ''')
+ >>> write('foo.py', '')
+ >>> _ = system(buildout+' setup . sdist')
+
+ >>> if zc.buildout.easy_install.is_distribute:
+ ... distribute_version = 'distribute = %s' % (
+ ... pkg_resources.working_set.find(
+ ... pkg_resources.Requirement.parse('distribute')).version,)
+ ... else:
+ ... distribute_version = ''
+ ...
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = foo
+ ... find-links = dist
+ ... versions = versions
+ ... allow-picked-versions = false
+ ...
+ ... [versions]
+ ... setuptools = %s
+ ... foo = 1
+ ... %s
+ ...
+ ... [foo]
+ ... recipe = zc.recipe.egg
+ ... eggs = foo
+ ... ''' % (pkg_resources.working_set.find(
+ ... pkg_resources.Requirement.parse('setuptools')).version,
+ ... distribute_version))
+
+ >>> print system(buildout),
+ Installing foo.
+ Getting distribution for 'foo==1'.
+ Got foo 1.
+
+ """
+
+def pyc_and_pyo_files_have_correct_paths():
+ r"""
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... find-links = %(link_server)s
+ ... unzip = true
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = demo
+ ... interpreter = py
+ ... ''' % globals())
+
+ >>> _ = system(buildout)
+
+ >>> write('t.py',
+ ... '''
+ ... import eggrecipedemo, eggrecipedemoneeded
+ ... print eggrecipedemo.main.func_code.co_filename
+ ... print eggrecipedemoneeded.f.func_code.co_filename
+ ... ''')
+
+ >>> print system(join('bin', 'py')+ ' t.py'),
+ /sample-buildout/eggs/demo-0.4c1-py2.4.egg/eggrecipedemo.py
+ /sample-buildout/eggs/demoneeded-1.2c1-py2.4.egg/eggrecipedemoneeded.py
+
+ >>> import os
+ >>> for name in os.listdir('eggs'):
+ ... if name.startswith('demoneeded'):
+ ... ls('eggs', name)
+ d EGG-INFO
+ - eggrecipedemoneeded.py
+ - eggrecipedemoneeded.pyc
+ - eggrecipedemoneeded.pyo
+
+ """
+
+def dont_mess_with_standard_dirs_with_variable_refs():
+ """
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... eggs-directory = ${buildout:directory}/develop-eggs
+ ... parts =
+ ... ''' % globals())
+ >>> print system(buildout),
+
+ """
+
+def expand_shell_patterns_in_develop_paths():
+ """
+ Sometimes we want to include a number of eggs in some directory as
+ develop eggs, without explicitly listing all of them in our
+ buildout.cfg
+
+ >>> make_dist_that_requires(sample_buildout, 'sampley')
+ >>> make_dist_that_requires(sample_buildout, 'samplez')
+
+ Now, let's create a buildout that has a shell pattern that matches
+ both:
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... develop = sample*
+ ... find-links = %(link_server)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = sampley
+ ... samplez
+ ... ''' % globals())
+
+ We can see that both eggs were found:
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/sampley'
+ Develop: '/sample-buildout/samplez'
+ Installing eggs.
+
+ """
+
+def warn_users_when_expanding_shell_patterns_yields_no_results():
+ """
+ Sometimes shell patterns do not match anything, so we want to warn
+ our users about it...
+
+ >>> make_dist_that_requires(sample_buildout, 'samplea')
+
+ So if we have 2 patterns, one that has a matching directory, and
+ another one that does not
+
+ >>> write('buildout.cfg',
+ ... '''
+ ... [buildout]
+ ... parts = eggs
+ ... develop = samplea grumble*
+ ... find-links = %(link_server)s
+ ...
+ ... [eggs]
+ ... recipe = zc.recipe.egg
+ ... eggs = samplea
+ ... ''' % globals())
+
+ We should get one of the eggs, and a warning for the pattern that
+ did not match anything.
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/samplea'
+ Couldn't develop '/sample-buildout/grumble*' (not found)
+ Installing eggs.
+
+ """
+
+def make_sure_versions_dont_cancel_extras():
+ """
+ There was a bug that caused extras in requirements to be lost.
+
+ >>> open('setup.py', 'w').write('''
+ ... from setuptools import setup
+ ... setup(name='extraversiondemo', version='1.0',
+ ... url='x', author='x', author_email='x',
+ ... extras_require=dict(foo=['demo']), py_modules=['t'])
+ ... ''')
+ >>> open('README', 'w').close()
+ >>> open('t.py', 'w').close()
+
+ >>> sdist('.', sample_eggs)
+ >>> mkdir('dest')
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['extraversiondemo[foo]'], 'dest', links=[sample_eggs],
+ ... versions = dict(extraversiondemo='1.0')
+ ... )
+ >>> sorted(dist.key for dist in ws)
+ ['demo', 'demoneeded', 'extraversiondemo']
+ """
+
+def increment_buildout_options():
+ r"""
+ >>> write('b1.cfg', '''
+ ... [buildout]
+ ... parts = p1
+ ... x = 1
+ ... y = a
+ ... b
+ ...
+ ... [p1]
+ ... recipe = zc.buildout:debug
+ ... foo = ${buildout:x} ${buildout:y}
+ ... ''')
+
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... extends = b1.cfg
+ ... parts += p2
+ ... x += 2
+ ... y -= a
+ ...
+ ... [p2]
+ ... <= p1
+ ... ''')
+
+ >>> print system(buildout),
+ Installing p1.
+ foo='1\n2 b'
+ recipe='zc.buildout:debug'
+ Installing p2.
+ foo='1\n2 b'
+ recipe='zc.buildout:debug'
+ """
+
+def increment_buildout_with_multiple_extended_files_421022():
+ r"""
+ >>> write('foo.cfg', '''
+ ... [buildout]
+ ... foo-option = foo
+ ... [other]
+ ... foo-option = foo
+ ... ''')
+ >>> write('bar.cfg', '''
+ ... [buildout]
+ ... bar-option = bar
+ ... [other]
+ ... bar-option = bar
+ ... ''')
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... parts = p other
+ ... extends = bar.cfg foo.cfg
+ ... bar-option += baz
+ ... foo-option += ham
+ ...
+ ... [other]
+ ... recipe = zc.buildout:debug
+ ... bar-option += baz
+ ... foo-option += ham
+ ...
+ ... [p]
+ ... recipe = zc.buildout:debug
+ ... x = ${buildout:bar-option} ${buildout:foo-option}
+ ... ''')
+
+ >>> print system(buildout),
+ Installing p.
+ recipe='zc.buildout:debug'
+ x='bar\nbaz foo\nham'
+ Installing other.
+ bar-option='bar\nbaz'
+ foo-option='foo\nham'
+ recipe='zc.buildout:debug'
+ """
+
+def increment_on_command_line():
+ r"""
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... parts = p1
+ ... x = 1
+ ... y = a
+ ... b
+ ...
+ ... [p1]
+ ... recipe = zc.buildout:debug
+ ... foo = ${buildout:x} ${buildout:y}
+ ...
+ ... [p2]
+ ... <= p1
+ ... ''')
+
+ >>> print system(buildout+' buildout:parts+=p2 p1:foo+=bar'),
+ Installing p1.
+ foo='1 a\nb\nbar'
+ recipe='zc.buildout:debug'
+ Installing p2.
+ foo='1 a\nb\nbar'
+ recipe='zc.buildout:debug'
+ """
+
+######################################################################
+
+def make_py_with_system_install(make_py, sample_eggs):
+ py_path, site_packages_path = make_py()
+ create_sample_namespace_eggs(sample_eggs, site_packages_path)
+ return py_path
+
+def create_sample_namespace_eggs(dest, site_packages_path=None):
+ from zc.buildout.testing import write, mkdir
+ for pkg, version in (('version', '1.0'), ('version', '1.1'),
+ ('fortune', '1.0')):
+ tmp = tempfile.mkdtemp()
+ try:
+ write(tmp, 'README.txt', '')
+ mkdir(tmp, 'src')
+ mkdir(tmp, 'src', 'tellmy')
+ write(tmp, 'src', 'tellmy', '__init__.py',
+ "__import__("
+ "'pkg_resources').declare_namespace(__name__)\n")
+ mkdir(tmp, 'src', 'tellmy', pkg)
+ write(tmp, 'src', 'tellmy', pkg,
+ '__init__.py', '__version__=%r\n' % version)
+ write(
+ tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(\n"
+ " name='tellmy.%(pkg)s',\n"
+ " package_dir = {'': 'src'},\n"
+ " packages = ['tellmy', 'tellmy.%(pkg)s'],\n"
+ " install_requires = ['setuptools'],\n"
+ " namespace_packages=['tellmy'],\n"
+ " zip_safe=True, version=%(version)r,\n"
+ " author='bob', url='bob', author_email='bob')\n"
+ % locals()
+ )
+ zc.buildout.testing.sdist(tmp, dest)
+ if (site_packages_path and pkg == 'version' and version == '1.1'):
+ # We install the 1.1 version in site packages the way a
+ # system packaging system (debs, rpms) would do it.
+ zc.buildout.testing.sys_install(tmp, site_packages_path)
+ finally:
+ shutil.rmtree(tmp)
+
+def create_sample_extension_sdists(dest):
+ from zc.buildout.testing import write, mkdir
+ name = 'demoextension'
+ for version in ('1.0', '1.1b1'):
+ tmp = tempfile.mkdtemp()
+ try:
+ write(tmp, 'README.txt', '')
+ write(tmp, name + '.py',
+ "def ext(buildout):\n"
+ " pass\n"
+ "def unload(buildout):\n"
+ " pass\n"
+ % locals())
+ write(tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(\n"
+ " name = %(name)r,\n"
+ " py_modules = [%(name)r],\n"
+ " entry_points = {\n"
+ " 'zc.buildout.extension': "
+ "['ext = %(name)s:ext'],\n"
+ " 'zc.buildout.unloadextension': "
+ "['ext = %(name)s:unload'],\n"
+ " },\n"
+ " zip_safe=True, version=%(version)r,\n"
+ " author='bob', url='bob', author_email='bob')\n"
+ % locals())
+ zc.buildout.testing.sdist(tmp, dest)
+ finally:
+ shutil.rmtree(tmp)
+
+def create_sample_recipe_sdists(dest):
+ from zc.buildout.testing import write, mkdir
+ name = 'demorecipe'
+ for version in ('1.0', '1.1b1'):
+ tmp = tempfile.mkdtemp()
+ try:
+ write(tmp, 'README.txt', '')
+ write(tmp, name + '.py',
+ "import logging, os, zc.buildout\n"
+ "class Demorecipe:\n"
+ " def __init__(self, buildout, name, options):\n"
+ " self.name, self.options = name, options\n"
+ " def install(self):\n"
+ " return ()\n"
+ " def update(self):\n"
+ " pass\n"
+ % locals())
+ write(tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(\n"
+ " name = %(name)r,\n"
+ " py_modules = [%(name)r],\n"
+ " entry_points = {'zc.buildout': "
+ "['default = %(name)s:Demorecipe']},\n"
+ " zip_safe=True, version=%(version)r,\n"
+ " author='bob', url='bob', author_email='bob')\n"
+ % locals())
+ zc.buildout.testing.sdist(tmp, dest)
+ finally:
+ shutil.rmtree(tmp)
+
+def _write_eggrecipedemoneeded(tmp, minor_version, suffix=''):
+ from zc.buildout.testing import write
+ write(tmp, 'README.txt', '')
+ write(tmp, 'eggrecipedemoneeded.py',
+ 'y=%s\ndef f():\n pass' % minor_version)
+ write(
+ tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(name='demoneeded', py_modules=['eggrecipedemoneeded'],"
+ " zip_safe=True, version='1.%s%s', author='bob', url='bob', "
+ "author_email='bob')\n"
+ % (minor_version, suffix)
+ )
+
+def _write_eggrecipedemo(tmp, minor_version, suffix=''):
+ from zc.buildout.testing import write
+ write(tmp, 'README.txt', '')
+ write(
+ tmp, 'eggrecipedemo.py',
+ 'import eggrecipedemoneeded\n'
+ 'x=%s\n'
+ 'def main(): print x, eggrecipedemoneeded.y\n'
+ % minor_version)
+ write(
+ tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(name='demo', py_modules=['eggrecipedemo'],"
+ " install_requires = 'demoneeded',"
+ " entry_points={'console_scripts': "
+ "['demo = eggrecipedemo:main']},"
+ " zip_safe=True, version='0.%s%s')\n" % (minor_version, suffix)
+ )
+
+def create_sample_sys_install(site_packages_path):
+ for creator, minor_version in (
+ (_write_eggrecipedemoneeded, 1),
+ (_write_eggrecipedemo, 3)):
+ # Write the files and install in site_packages_path.
+ tmp = tempfile.mkdtemp()
+ try:
+ creator(tmp, minor_version)
+ zc.buildout.testing.sys_install(tmp, site_packages_path)
+ finally:
+ shutil.rmtree(tmp)
+
+def create_sample_eggs(test, executable=sys.executable):
+ from zc.buildout.testing import write
+ dest = test.globs['sample_eggs']
+ tmp = tempfile.mkdtemp()
+ try:
+ for i in (0, 1, 2):
+ suffix = i==2 and 'c1' or ''
+ _write_eggrecipedemoneeded(tmp, i, suffix)
+ zc.buildout.testing.sdist(tmp, dest)
+
+ write(
+ tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(name='other', zip_safe=False, version='1.0', "
+ "py_modules=['eggrecipedemoneeded'])\n"
+ )
+ zc.buildout.testing.bdist_egg(tmp, executable, dest)
+
+ os.remove(os.path.join(tmp, 'eggrecipedemoneeded.py'))
+
+ for i in (1, 2, 3, 4):
+ suffix = i==4 and 'c1' or ''
+ _write_eggrecipedemo(tmp, i, suffix)
+ zc.buildout.testing.bdist_egg(tmp, executable, dest)
+
+ write(tmp, 'eggrecipebigdemo.py', 'import eggrecipedemo')
+ write(
+ tmp, 'setup.py',
+ "from setuptools import setup\n"
+ "setup(name='bigdemo', "
+ " install_requires = 'demo',"
+ " py_modules=['eggrecipebigdemo'], "
+ " zip_safe=True, version='0.1')\n"
+ )
+ zc.buildout.testing.bdist_egg(tmp, executable, dest)
+
+ finally:
+ shutil.rmtree(tmp)
+
+extdemo_c = """
+#include <Python.h>
+#include <extdemo.h>
+
+static PyMethodDef methods[] = {{NULL}};
+
+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
+}
+"""
+
+extdemo_setup_py = """
+import os
+from distutils.core import setup, Extension
+
+if os.environ.get('test-variable'):
+ print "Have environment test-variable:", os.environ['test-variable']
+
+setup(name = "extdemo", version = "%s", url="http://www.zope.org",
+ author="Demo", author_email="demo@demo.com",
+ ext_modules = [Extension('extdemo', ['extdemo.c'])],
+ )
+"""
+
+def add_source_dist(test, version=1.4):
+
+ if 'extdemo' not in test.globs:
+ test.globs['extdemo'] = test.globs['tmpdir']('extdemo')
+
+ tmp = test.globs['extdemo']
+ write = test.globs['write']
+ try:
+ write(tmp, 'extdemo.c', extdemo_c);
+ write(tmp, 'setup.py', extdemo_setup_py % version);
+ write(tmp, 'README', "");
+ write(tmp, 'MANIFEST.in', "include *.c\n");
+ test.globs['sdist'](tmp, test.globs['sample_eggs'])
+ except:
+ shutil.rmtree(tmp)
+
+def easy_install_SetUp(test):
+ zc.buildout.testing.buildoutSetUp(test)
+ sample_eggs = test.globs['tmpdir']('sample_eggs')
+ test.globs['sample_eggs'] = sample_eggs
+ os.mkdir(os.path.join(sample_eggs, 'index'))
+ create_sample_eggs(test)
+ add_source_dist(test)
+ test.globs['link_server'] = test.globs['start_server'](
+ 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
+def makeNewRelease(project, ws, dest, version='99.99'):
+ dist = ws.find(pkg_resources.Requirement.parse(project))
+ eggname, oldver, pyver = egg_parse(
+ os.path.basename(dist.location)
+ ).groups()
+ dest = os.path.join(dest, "%s-%s-py%s.egg" % (eggname, version, pyver))
+ if os.path.isfile(dist.location):
+ shutil.copy(dist.location, dest)
+ zip = zipfile.ZipFile(dest, 'a')
+ zip.writestr(
+ 'EGG-INFO/PKG-INFO',
+ zip.read('EGG-INFO/PKG-INFO').replace("Version: %s" % oldver,
+ "Version: %s" % version)
+ )
+ zip.close()
+ else:
+ shutil.copytree(dist.location, dest)
+ info_path = os.path.join(dest, 'EGG-INFO', 'PKG-INFO')
+ info = open(info_path).read().replace("Version: %s" % oldver,
+ "Version: %s" % version)
+ open(info_path, 'w').write(info)
+
+def getWorkingSetWithBuildoutEgg(test):
+ sample_buildout = test.globs['sample_buildout']
+ eggs = os.path.join(sample_buildout, 'eggs')
+
+ # If the zc.buildout dist is a develop dist, convert it to a
+ # regular egg in the sample buildout
+ req = pkg_resources.Requirement.parse('zc.buildout')
+ dist = pkg_resources.working_set.find(req)
+ if dist.precedence == pkg_resources.DEVELOP_DIST:
+ # We have a develop egg, create a real egg for it:
+ here = os.getcwd()
+ os.chdir(os.path.dirname(dist.location))
+ assert os.spawnle(
+ os.P_WAIT, sys.executable,
+ zc.buildout.easy_install._safe_arg(sys.executable),
+ os.path.join(os.path.dirname(dist.location), 'setup.py'),
+ '-q', 'bdist_egg', '-d', eggs,
+ dict(os.environ,
+ PYTHONPATH=pkg_resources.working_set.find(
+ pkg_resources.Requirement.parse('setuptools')
+ ).location,
+ ),
+ ) == 0
+ os.chdir(here)
+ os.remove(os.path.join(eggs, 'zc.buildout.egg-link'))
+
+ # Rebuild the buildout script
+ ws = pkg_resources.WorkingSet([eggs])
+ ws.require('zc.buildout')
+ zc.buildout.easy_install.scripts(
+ ['zc.buildout'], ws, sys.executable,
+ os.path.join(sample_buildout, 'bin'))
+ else:
+ ws = pkg_resources.working_set
+ return ws
+
+def updateSetup(test):
+ zc.buildout.testing.buildoutSetUp(test)
+ new_releases = test.globs['tmpdir']('new_releases')
+ test.globs['new_releases'] = new_releases
+ ws = getWorkingSetWithBuildoutEgg(test)
+ # now let's make the new releases
+ makeNewRelease('zc.buildout', ws, new_releases)
+ makeNewRelease('zc.buildout', ws, new_releases, '100.0b1')
+ os.mkdir(os.path.join(new_releases, 'zc.buildout'))
+ if zc.buildout.easy_install.is_distribute:
+ makeNewRelease('distribute', ws, new_releases)
+ os.mkdir(os.path.join(new_releases, 'distribute'))
+ else:
+ makeNewRelease('setuptools', ws, new_releases)
+ os.mkdir(os.path.join(new_releases, 'setuptools'))
+
+def bootstrapSetup(test):
+ easy_install_SetUp(test)
+ sample_eggs = test.globs['sample_eggs']
+ ws = getWorkingSetWithBuildoutEgg(test)
+ makeNewRelease('zc.buildout', ws, sample_eggs)
+ makeNewRelease('zc.buildout', ws, sample_eggs, '100.0b1')
+ os.environ['bootstrap-testing-find-links'] = test.globs['link_server']
+
+normalize_bang = (
+ re.compile(re.escape('#!'+
+ zc.buildout.easy_install._safe_arg(sys.executable))),
+ '#!/usr/local/bin/python2.4',
+ )
+
+hide_distribute_additions = (re.compile('install_dir .+\n'), '')
+hide_zip_safe_message = (
+ # This comes in a different place in the output in Python 2.7. It's not
+ # important to our tests. Hide it.
+ re.compile(
+ '((?<=\n)\n)?zip_safe flag not set; analyzing archive contents...\n'),
+ '')
+hide_first_index_page_message = (
+ # This comes in a different place in the output in Python 2.7. It's not
+ # important to our tests. Hide it.
+ re.compile(
+ "Couldn't find index page for '[^']+' \(maybe misspelled\?\)\n"),
+ '')
+def test_suite():
+ test_suite = [
+ doctest.DocFileSuite(
+ 'buildout.txt', 'runsetup.txt', 'repeatable.txt', 'setup.txt',
+ setUp=zc.buildout.testing.buildoutSetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ zc.buildout.tests.hide_distribute_additions,
+ hide_zip_safe_message,
+ (re.compile('__buildout_signature__ = recipes-\S+'),
+ '__buildout_signature__ = recipes-SSSSSSSSSSS'),
+ (re.compile('executable = [\S ]+python\S*', re.I),
+ 'executable = python'),
+ (re.compile('[-d] (setuptools|distribute)-\S+[.]egg'),
+ 'setuptools.egg'),
+ (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+ 'zc.buildout.egg'),
+ (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
+ (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
+ (re.compile('Picked: (\S+) = \S+'),
+ 'Picked: \\1 = V.V'),
+ (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
+ 'We have a develop egg: zc.buildout X.X.'),
+ (re.compile(r'\\[\\]?'), '/'),
+ (re.compile('WindowsError'), 'OSError'),
+ (re.compile(r'\[Error \d+\] Cannot create a file '
+ r'when that file already exists: '),
+ '[Errno 17] File exists: '
+ ),
+ (re.compile('distribute'), 'setuptools'),
+ ])
+ ),
+ doctest.DocFileSuite(
+ 'debugging.txt',
+ setUp=zc.buildout.testing.buildoutSetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.tests.hide_distribute_additions,
+ (re.compile(r'\S+buildout.py'), 'buildout.py'),
+ (re.compile(r'line \d+'), 'line NNN'),
+ (re.compile(r'py\(\d+\)'), 'py(NNN)'),
+ ])
+ ),
+
+ doctest.DocFileSuite(
+ 'update.txt',
+ setUp=updateSetup,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ normalize_bang,
+ zc.buildout.tests.hide_distribute_additions,
+ (re.compile('99[.]99'), 'NINETYNINE.NINETYNINE'),
+ (re.compile('(zc.buildout|setuptools)-\d+[.]\d+\S*'
+ '-py\d.\d.egg'),
+ '\\1.egg'),
+ (re.compile('distribute-\d+[.]\d+\S*'
+ '-py\d.\d.egg'),
+ 'setuptools.egg'),
+ (re.compile('(zc.buildout|setuptools)( version)? \d+[.]\d+\S*'),
+ '\\1 V.V'),
+ (re.compile('distribute( version)? \d+[.]\d+\S*'),
+ 'setuptools V.V'),
+ (re.compile('[-d] (setuptools|distribute)'), '- setuptools'),
+ (re.compile('distribute'), 'setuptools'),
+ (re.compile("\nUnused options for buildout: "
+ "'(distribute|setuptools)\-version'\."),
+ '')
+ ])
+ ),
+
+ doctest.DocFileSuite(
+ 'easy_install.txt', 'downloadcache.txt', 'dependencylinks.txt',
+ 'allowhosts.txt', 'unzip.txt', 'upgrading_distribute.txt',
+ setUp=easy_install_SetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ normalize_bang,
+ hide_first_index_page_message,
+ zc.buildout.tests.hide_distribute_additions,
+ (re.compile('extdemo[.]pyd'), 'extdemo.so'),
+ (re.compile('[-d] (setuptools|distribute)-\S+[.]egg'),
+ 'setuptools.egg'),
+ (re.compile(r'\\[\\]?'), '/'),
+ (re.compile(r'\#!\S+\bpython\S*'), '#!/usr/bin/python'),
+ # Normalize generate_script's Windows interpreter to UNIX:
+ (re.compile(r'\nimport subprocess\n'), '\n'),
+ (re.compile('subprocess\\.call\\(argv, env=environ\\)'),
+ 'os.execve(sys.executable, argv, environ)'),
+ (re.compile('distribute'), 'setuptools'),
+ # Distribute unzips eggs by default.
+ (re.compile('\- demoneeded'), 'd demoneeded'),
+ ]+(sys.version_info < (2, 5) and [
+ (re.compile('.*No module named runpy.*', re.S), ''),
+ (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),
+ (re.compile('.*Error: what does not exist.*', re.S), ''),
+ ] or [])),
+ ),
+
+ doctest.DocFileSuite(
+ 'download.txt', 'extends-cache.txt',
+ setUp=easy_install_SetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
+ checker=renormalizing.RENormalizing([
+ (re.compile(' at -?0x[^>]+'), '<MEM ADDRESS>'),
+ (re.compile('http://localhost:[0-9]{4,5}/'),
+ 'http://localhost/'),
+ (re.compile('[0-9a-f]{32}'), '<MD5 CHECKSUM>'),
+ zc.buildout.testing.normalize_path,
+ ]),
+ ),
+
+ doctest.DocTestSuite(
+ setUp=easy_install_SetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ zc.buildout.tests.hide_distribute_additions,
+ hide_first_index_page_message,
+ (re.compile("buildout: Running \S*setup.py"),
+ 'buildout: Running setup.py'),
+ (re.compile('(setuptools|distribute)-\S+-'),
+ 'setuptools.egg'),
+ (re.compile('zc.buildout-\S+-'),
+ 'zc.buildout.egg'),
+ (re.compile('File "\S+one.py"'),
+ 'File "one.py"'),
+ (re.compile(r'We have a develop egg: (\S+) (\S+)'),
+ r'We have a develop egg: \1 V'),
+ (re.compile('Picked: (setuptools|distribute) = \S+'),
+ 'Picked: setuptools = V'),
+ (re.compile(r'\\[\\]?'), '/'),
+ (re.compile(
+ '-q develop -mxN -d "/sample-buildout/develop-eggs'),
+ '-q develop -mxN -d /sample-buildout/develop-eggs'
+ ),
+ (re.compile(r'^[*]...'), '...'),
+ # for bug_92891_bootstrap_crashes_with_egg_recipe_in_buildout_section
+ (re.compile(r"Unused options for buildout: 'eggs' 'scripts'\."),
+ "Unused options for buildout: 'scripts' 'eggs'."),
+ (re.compile('distribute'), 'setuptools'),
+ # Distribute unzips eggs by default.
+ (re.compile('\- demoneeded'), 'd demoneeded'),
+ ]),
+ ),
+ zc.buildout.testselectingpython.test_suite(),
+ zc.buildout.rmtree.test_suite(),
+ doctest.DocFileSuite(
+ 'windows.txt',
+ setUp=zc.buildout.testing.buildoutSetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ zc.buildout.tests.hide_distribute_additions,
+ (re.compile('__buildout_signature__ = recipes-\S+'),
+ '__buildout_signature__ = recipes-SSSSSSSSSSS'),
+ (re.compile('[-d] setuptools-\S+[.]egg'), 'setuptools.egg'),
+ (re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
+ 'zc.buildout.egg'),
+ (re.compile('creating \S*setup.cfg'), 'creating setup.cfg'),
+ (re.compile('hello\%ssetup' % os.path.sep), 'hello/setup'),
+ (re.compile('Picked: (\S+) = \S+'),
+ 'Picked: \\1 = V.V'),
+ (re.compile(r'We have a develop egg: zc.buildout (\S+)'),
+ 'We have a develop egg: zc.buildout X.X.'),
+ (re.compile(r'\\[\\]?'), '/'),
+ (re.compile('WindowsError'), 'OSError'),
+ (re.compile(r'\[Error 17\] Cannot create a file '
+ r'when that file already exists: '),
+ '[Errno 17] File exists: '
+ ),
+ ])
+ ),
+ doctest.DocFileSuite(
+ 'testing_bugfix.txt',
+ checker=renormalizing.RENormalizing([
+ # Python 2.7
+ (re.compile(
+ re.escape(
+ 'testrunner.logsupport.NullHandler instance at')),
+ 'testrunner.logsupport.NullHandler object at'),
+ (re.compile(re.escape('logging.StreamHandler instance at')),
+ 'logging.StreamHandler object at'),
+ ])
+ ),
+ ]
+
+ # adding bootstrap.txt doctest to the suite
+ # only if bootstrap.py is present
+ bootstrap_py = os.path.join(
+ os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(zc.buildout.__file__)
+ )
+ )
+ ),
+ 'bootstrap', 'bootstrap.py')
+
+ if os.path.exists(bootstrap_py):
+ test_suite.append(doctest.DocFileSuite(
+ 'bootstrap.txt',
+ setUp=bootstrapSetup,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ normalize_bang,
+ (re.compile('Downloading.*setuptools.*egg\n'), ''),
+ (re.compile('options:'), 'Options:'),
+ (re.compile('usage:'), 'Usage:'),
+ ]),
+ ))
+ test_suite.append(doctest.DocFileSuite(
+ 'virtualenv.txt',
+ setUp=easy_install_SetUp,
+ tearDown=zc.buildout.testing.buildoutTearDown,
+ checker=renormalizing.RENormalizing([
+ zc.buildout.testing.normalize_path,
+ zc.buildout.testing.normalize_endings,
+ zc.buildout.testing.normalize_script,
+ zc.buildout.testing.normalize_egg_py,
+ zc.buildout.tests.hide_distribute_additions,
+ (re.compile('(setuptools|distribute)-\S+-'),
+ 'setuptools.egg'),
+ (re.compile('zc.buildout-\S+-'),
+ 'zc.buildout.egg'),
+ (re.compile(re.escape('#!"/executable_buildout/bin/py"')),
+ '#!/executable_buildout/bin/py'), # Windows.
+ (re.compile(re.escape('/broken_s/')),
+ '/broken_S/'), # Windows.
+ ]),
+ ))
+
+ return unittest.TestSuite(test_suite)