Commit 6826e364 authored by Jim Fulton's avatar Jim Fulton

Allow custom python interpreters (other than the one used to run the

buildout) to be used.
parent 6eb9bab7
Installation of distributions as eggs Installation of distributions as eggs
===================================== =====================================
The zc.recipe.egg ewcipe can be used to install various types if The zc.recipe.egg recipe can be used to install various types if
distutils distributions as eggs. It takes a number of options: distutils distributions as eggs. It takes a number of options:
distribution distribution
...@@ -14,6 +14,18 @@ distribution ...@@ -14,6 +14,18 @@ distribution
find-links find-links
A list of URLs, files, or directories to search for distributions. A list of URLs, files, or directories to search for distributions.
python
The name of a section to get the Python executable from.
If not specified, then the buildout python option is used. The
Python executable is found in the executable option of the named
section.
unzip
The value of this option must be either true or false. If the value
is true, then the installed egg will be unzipped. Note that this is
only effective when an egg is installed. If a zipped egg already
exists in the eggs directory, it will not be unzipped.
To illustrate this, we've created a directory with some sample eggs: To illustrate this, we've created a directory with some sample eggs:
>>> ls(sample_eggs) >>> ls(sample_eggs)
...@@ -51,7 +63,6 @@ Now, if we look at the buildout eggs directory: ...@@ -51,7 +63,6 @@ Now, if we look at the buildout eggs directory:
>>> ls(sample_buildout, 'eggs') >>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
- zc.recipe.egg.egg-link
We see that we got an egg for demo that met the requirement, as well We see that we got an egg for demo that met the requirement, as well
as the egg for demoneeded, wich demo requires. (We also see an egg as the egg for demoneeded, wich demo requires. (We also see an egg
...@@ -94,7 +105,7 @@ the bits if the path added to reflect the eggs: ...@@ -94,7 +105,7 @@ the bits if the path added to reflect the eggs:
<BLANKLINE> <BLANKLINE>
The recipe gets the most recent distribution that satisfies the The recipe gets the most recent distribution that satisfies the
specification. For example, if we remove the restriction on demo: specification. For example, We remove the restriction on demo:
>>> write(sample_buildout, 'buildout.cfg', >>> write(sample_buildout, 'buildout.cfg',
... """ ... """
...@@ -104,9 +115,11 @@ specification. For example, if we remove the restriction on demo: ...@@ -104,9 +115,11 @@ specification. For example, if we remove the restriction on demo:
... [demo] ... [demo]
... recipe = zc.recipe.egg ... recipe = zc.recipe.egg
... find-links = %s ... find-links = %s
... unzip = true
... """ % sample_eggs) ... """ % sample_eggs)
and rerun the buildout: We also used the unzip uption to request a directory, rather than
a zip file.
>>> print system(runscript), >>> print system(runscript),
...@@ -114,9 +127,8 @@ Then we'll get a new demo egg: ...@@ -114,9 +127,8 @@ Then we'll get a new demo egg:
>>> ls(sample_buildout, 'eggs') >>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demo-0.3-py2.3.egg d demo-0.3-py2.3.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
- zc.recipe.egg.egg-link
Note that we removed the distribution option, and the distribution Note that we removed the distribution option, and the distribution
defaulted to the part name. defaulted to the part name.
...@@ -150,7 +162,6 @@ arguments: ...@@ -150,7 +162,6 @@ arguments:
You can also control the name used for scripts: You can also control the name used for scripts:
>>> write(sample_buildout, 'buildout.cfg', >>> write(sample_buildout, 'buildout.cfg',
... """ ... """
... [buildout] ... [buildout]
...@@ -167,3 +178,4 @@ You can also control the name used for scripts: ...@@ -167,3 +178,4 @@ You can also control the name used for scripts:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- foo - foo
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
$Id$ $Id$
""" """
import os import os, zipfile
import zc.buildout.egglinker import zc.buildout.egglinker
import zc.buildout.easy_install import zc.buildout.easy_install
...@@ -41,12 +41,20 @@ class Egg: ...@@ -41,12 +41,20 @@ class Egg:
options['_e'] = buildout['buildout']['eggs-directory'] options['_e'] = buildout['buildout']['eggs-directory']
options['_d'] = buildout['buildout']['develop-eggs-directory'] options['_d'] = buildout['buildout']['develop-eggs-directory']
assert options.get('unzip') in ('true', 'false', None)
python = options.get('python', buildout['buildout']['python'])
options['executable'] = buildout[python]['executable']
def install(self): def install(self):
options = self.options options = self.options
distribution = options.get('distribution', self.name) distribution = options.get('distribution', self.name)
zc.buildout.easy_install.install( zc.buildout.easy_install.install(
distribution, options['_e'], self.links) distribution, options['_e'], self.links, options['executable'],
always_unzip=options.get('unzip') == 'true')
eggss = [options['_d'], options['_e']]
scripts = options.get('scripts') scripts = options.get('scripts')
if scripts or scripts is None: if scripts or scripts is None:
if scripts is not None: if scripts is not None:
...@@ -56,6 +64,6 @@ class Egg: ...@@ -56,6 +64,6 @@ class Egg:
for s in scripts for s in scripts
]) ])
return zc.buildout.egglinker.scripts( return zc.buildout.egglinker.scripts(
[distribution], [distribution], options['_b'], eggss,
options['_b'], [options['_d'], options['_e']], scripts=scripts) scripts=scripts, executable=options['executable'])
Controlling which Python to use
-------------------------------
The following assumes that your $HOME/.buildout/default.cfg has
python2.3 and python2.4 sections that define Python 2.3 and Python 2.4
executables.
We can specify the python to use by specifying the name of a section
to read the Python executable from. The default is the section
defined by the python buildout option.
We have a directory with some sample eggs:
>>> ls(sample_eggs)
- demo-0.1-py2.3.egg
- demo-0.1-py2.4.egg
- demo-0.2-py2.3.egg
- demo-0.2-py2.4.egg
- demo-0.3-py2.3.egg
- demo-0.3-py2.4.egg
- demoneeded-1.0-py2.3.egg
- demoneeded-1.0-py2.4.egg
We have a sample buildout. Let's update it's configuration file to
install the demo package using Python 2.3.
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
... eggs-directory = eggs
...
... [demo]
... recipe = zc.recipe.egg
... distribution = demo <0.3
... find-links = %s
... python = python2.3
... """ % sample_eggs)
In our default.cfg file in the .buildout subdirectiry of our
directory, we have something like::
[python2.3]
executable = /usr/bin/python
[python2.4]
executable = /usr/local/bin/python2.4
(Of course, the paths will vary from system to system.)
Now, if we run the buildout:
>>> import os
>>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout),
we'll get the Python 2.3 eggs for demo and demoneeded:
>>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg
- demoneeded-1.0-py2.3.egg
And the generated scripts invoke Python 2.3:
>>> f = open(os.path.join(sample_buildout, 'bin', 'demo'))
>>> f.readline().strip() == '#!' + python2_3_executable
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/private/tmp/tmpOEtRO8sample-buildout/eggs/demo-0.2-py2.3.egg',
'/private/tmp/tmpOEtRO8sample-buildout/eggs/demoneeded-1.0-py2.3.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo'))
>>> f.readline().strip() == '#!' + python2_3_executable + ' -i'
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/tmp/tmpOBTxDMsample-buildout/eggs/demo-0.2-py2.3.egg',
'/tmp/tmpOBTxDMsample-buildout/eggs/demoneeded-1.0-py2.3.egg'
]
If we change the Python version to 2.4, we'll use Python 2.4 eggs:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
... eggs-directory = eggs
...
... [demo]
... recipe = zc.recipe.egg
... distribution = demo <0.3
... find-links = %s
... python = python2.4
... """ % sample_eggs)
>>> print system(buildout),
>>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg
- demo-0.2-py2.4.egg
- demoneeded-1.0-py2.3.egg
- demoneeded-1.0-py2.4.egg
>>> f = open(os.path.join(sample_buildout, 'bin', 'demo'))
>>> f.readline().strip() == '#!' + python2_4_executable
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/private/tmp/tmpOEtRO8sample-buildout/eggs/demo-0.2-py2.4.egg',
'/private/tmp/tmpOEtRO8sample-buildout/eggs/demoneeded-1.0-py2.4.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo'))
>>> f.readline().strip() == '#!' + python2_4_executable + ' -i'
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/tmp/tmpOBTxDMsample-buildout/eggs/demo-0.2-py2.4.egg',
'/tmp/tmpOBTxDMsample-buildout/eggs/demoneeded-1.0-py2.4.egg'
]
...@@ -26,7 +26,7 @@ def dirname(d, level=1): ...@@ -26,7 +26,7 @@ def dirname(d, level=1):
def setUp(test): def setUp(test):
zc.buildout.testing.buildoutSetUp(test) zc.buildout.testing.buildoutSetUp(test)
open(os.path.join(test.globs['sample_buildout'], open(os.path.join(test.globs['sample_buildout'],
'eggs', 'zc.recipe.egg.egg-link'), 'develop-eggs', 'zc.recipe.egg.egg-link'),
'w').write(dirname(__file__, 4)) 'w').write(dirname(__file__, 4))
zc.buildout.testing.create_sample_eggs(test) zc.buildout.testing.create_sample_eggs(test)
...@@ -34,6 +34,14 @@ def tearDown(test): ...@@ -34,6 +34,14 @@ def tearDown(test):
shutil.rmtree(test.globs['_sample_eggs_container']) shutil.rmtree(test.globs['_sample_eggs_container'])
zc.buildout.testing.buildoutTearDown(test) zc.buildout.testing.buildoutTearDown(test)
def setUpPython(test):
zc.buildout.testing.buildoutSetUp(test, clear_home=False)
open(os.path.join(test.globs['sample_buildout'],
'develop-eggs', 'zc.recipe.egg.egg-link'),
'w').write(dirname(__file__, 4))
zc.buildout.testing.multi_python(test)
def test_suite(): def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
...@@ -49,7 +57,15 @@ def test_suite(): ...@@ -49,7 +57,15 @@ def test_suite():
'\\2-VVV-egg') '\\2-VVV-egg')
]) ])
), ),
doctest.DocFileSuite(
'selecting-python.txt',
setUp=setUpPython, tearDown=tearDown,
checker=renormalizing.RENormalizing([
(re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep),
r'/sample-\1/\2'),
(re.compile('\S+sample-(\w+)'), r'/sample-\1'),
]),
),
)) ))
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -22,11 +22,16 @@ $Id$ ...@@ -22,11 +22,16 @@ $Id$
import os, sys import os, sys
def install(spec, dest, links, python=sys.executable): def install(spec, dest, links, executable=sys.executable, always_unzip=False):
prefix = sys.exec_prefix + os.path.sep prefix = sys.exec_prefix + os.path.sep
path = os.pathsep.join([p for p in sys.path if not p.startswith(prefix)]) path = os.pathsep.join([p for p in sys.path if not p.startswith(prefix)])
os.spawnle( args = (
os.P_WAIT, python, python,
'-c', 'from setuptools.command.easy_install import main; main()', '-c', 'from setuptools.command.easy_install import main; main()',
'-mqxd', dest, '-f', ' '.join(links), spec, '-mqxd', dest)
dict(PYTHONPATH=path)) if links:
args += ('-f', ' '.join(links))
if always_unzip:
args += ('-Z', )
args += (spec, dict(PYTHONPATH=path))
os.spawnle(os.P_WAIT, executable, executable, *args)
...@@ -20,9 +20,13 @@ For example, given the sample eggs: ...@@ -20,9 +20,13 @@ For example, given the sample eggs:
>>> ls(sample_eggs) >>> ls(sample_eggs)
- demo-0.1-py2.3.egg - demo-0.1-py2.3.egg
- demo-0.1-py2.4.egg
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demo-0.2-py2.4.egg
- demo-0.3-py2.3.egg - demo-0.3-py2.3.egg
- demo-0.3-py2.4.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
- demoneeded-1.0-py2.4.egg
let's make directory and install the demo egg to it: let's make directory and install the demo egg to it:
...@@ -34,3 +38,28 @@ let's make directory and install the demo egg to it: ...@@ -34,3 +38,28 @@ let's make directory and install the demo egg to it:
- demo-0.3-py2.3.egg - demo-0.3-py2.3.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
We can specify an alternate Python executable, and we can specify
that, when we retrieve (or create) an egg, it should be unzipped.
>>> import shutil
>>> shutil.rmtree(dest)
>>> dest = tempfile.mkdtemp()
>>> zc.buildout.easy_install.install(
... 'demo', dest, [sample_eggs],
... always_unzip=True, executable= python2_3_executable)
>>> ls(dest)
d demo-0.3-py2.3.egg
d demoneeded-1.0-py2.3.egg
>>> shutil.rmtree(dest)
>>> dest = tempfile.mkdtemp()
>>> zc.buildout.easy_install.install(
... 'demo', dest, [sample_eggs],
... always_unzip=True, executable= python2_4_executable)
>>> ls(dest)
d demo-0.3-py2.4.egg
d demoneeded-1.0-py2.4.egg
...@@ -25,28 +25,44 @@ $Id$ ...@@ -25,28 +25,44 @@ $Id$
# XXX need to deal with extras # XXX need to deal with extras
import os import os
import re
import sys import sys
import pkg_resources import pkg_resources
def distributions(reqs, eggss): _versions = {sys.executable: '%d.%d' % sys.version_info[:2]}
env = pkg_resources.Environment(eggss) def _get_version(executable):
try:
return _versions[executable]
except KeyError:
i, o = os.popen4(executable + ' -V')
i.close()
version = o.read().strip()
o.close()
pystring, version = version.split()
assert pystring == 'Python'
version = re.match('(\d[.]\d)[.]\d$', version).group(1)
_versions[executable] = version
return version
def distributions(reqs, eggss, executable=sys.executable):
env = pkg_resources.Environment(eggss, python=_get_version(executable))
ws = pkg_resources.WorkingSet() ws = pkg_resources.WorkingSet()
reqs = [pkg_resources.Requirement.parse(r) for r in reqs] reqs = [pkg_resources.Requirement.parse(r) for r in reqs]
return ws.resolve(reqs, env=env) return ws.resolve(reqs, env=env)
def path(reqs, eggss): def path(reqs, eggss, executable=sys.executable):
dists = distributions(reqs, eggss) dists = distributions(reqs, eggss, executable)
return [dist.location for dist in dists] return [dist.location for dist in dists]
def location(spec, eggss): def location(spec, eggss, executable=sys.executable):
env = pkg_resources.Environment(eggss) env = pkg_resources.Environment(eggss, python=_get_version(executable))
req = pkg_resources.Requirement.parse(spec) req = pkg_resources.Requirement.parse(spec)
dist = env.best_match(req, pkg_resources.WorkingSet()) dist = env.best_match(req, pkg_resources.WorkingSet())
return dist.location return dist.location
def scripts(reqs, dest, eggss, scripts=None): def scripts(reqs, dest, eggss, scripts=None, executable=sys.executable):
dists = distributions(reqs, eggss) dists = distributions(reqs, eggss, executable)
reqs = [pkg_resources.Requirement.parse(r) for r in reqs] reqs = [pkg_resources.Requirement.parse(r) for r in reqs]
projects = [r.project_name for r in reqs] projects = [r.project_name for r in reqs]
path = "',\n '".join([dist.location for dist in dists]) path = "',\n '".join([dist.location for dist in dists])
...@@ -64,7 +80,7 @@ def scripts(reqs, dest, eggss, scripts=None): ...@@ -64,7 +80,7 @@ def scripts(reqs, dest, eggss, scripts=None):
sname = os.path.join(dest, sname) sname = os.path.join(dest, sname)
generated.append(sname) generated.append(sname)
_script(dist, 'console_scripts', name, path, sname) _script(dist, 'console_scripts', name, path, sname, executable)
name = 'py_'+dist.project_name name = 'py_'+dist.project_name
if scripts is not None: if scripts is not None:
...@@ -75,14 +91,14 @@ def scripts(reqs, dest, eggss, scripts=None): ...@@ -75,14 +91,14 @@ def scripts(reqs, dest, eggss, scripts=None):
if sname is not None: if sname is not None:
sname = os.path.join(dest, sname) sname = os.path.join(dest, sname)
generated.append(sname) generated.append(sname)
_pyscript(path, sname) _pyscript(path, sname, executable)
return generated return generated
def _script(dist, group, name, path, dest): def _script(dist, group, name, path, dest, executable):
entry_point = dist.get_entry_info(group, name) entry_point = dist.get_entry_info(group, name)
open(dest, 'w').write(script_template % dict( open(dest, 'w').write(script_template % dict(
python = sys.executable, python = executable,
path = path, path = path,
project = dist.project_name, project = dist.project_name,
name = name, name = name,
...@@ -109,9 +125,9 @@ if __name__ == '__main__': ...@@ -109,9 +125,9 @@ if __name__ == '__main__':
''' '''
def _pyscript(path, dest): def _pyscript(path, dest, executable):
open(dest, 'w').write(py_script_template % dict( open(dest, 'w').write(py_script_template % dict(
python = sys.executable, python = executable,
path = path, path = path,
)) ))
try: try:
......
...@@ -17,9 +17,13 @@ We have a directory with some sample eggs in it: ...@@ -17,9 +17,13 @@ We have a directory with some sample eggs in it:
>>> ls(sample_eggs) >>> ls(sample_eggs)
- demo-0.1-py2.3.egg - demo-0.1-py2.3.egg
- demo-0.1-py2.4.egg
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demo-0.2-py2.4.egg
- demo-0.3-py2.3.egg - demo-0.3-py2.3.egg
- demo-0.3-py2.4.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
- demoneeded-1.0-py2.4.egg
The demo package depends on the demoneeded package. The demo package depends on the demoneeded package.
...@@ -66,8 +70,8 @@ The demo script run the entry point defined in the demo egg: ...@@ -66,8 +70,8 @@ The demo script run the entry point defined in the demo egg:
<BLANKLINE> <BLANKLINE>
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
'/tmp/tmpuR5-n7eggtest/dist/demo-0.1-py2.3.egg', '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg',
'/tmp/tmpuR5-n7eggtest/dist/demoneeded-1.0-py2.3.egg' '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg'
] ]
<BLANKLINE> <BLANKLINE>
import eggrecipedemo import eggrecipedemo
...@@ -90,8 +94,8 @@ the path set: ...@@ -90,8 +94,8 @@ the path set:
<BLANKLINE> <BLANKLINE>
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
'/tmp/tmpuR5-n7eggtest/dist/demo-0.1-py2.3.egg', '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg',
'/tmp/tmpuR5-n7eggtest/dist/demoneeded-1.0-py2.3.egg' '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg'
] ]
An additional argumnet can be passed to define which scripts to install An additional argumnet can be passed to define which scripts to install
...@@ -112,8 +116,6 @@ original script names to new script names. ...@@ -112,8 +116,6 @@ original script names to new script names.
>>> print system(os.path.join(bin, 'run')), >>> print system(os.path.join(bin, 'run')),
1 1 1 1
>>> shutil.rmtree(bin)
Sometimes we need more control over script generation. Some Sometimes we need more control over script generation. Some
lower-level APIs are available to help us generate scripts ourselves. lower-level APIs are available to help us generate scripts ourselves.
These apis are a little bit higher level than those provided by These apis are a little bit higher level than those provided by
...@@ -124,15 +126,15 @@ of requirements from a sequence of egg directories: ...@@ -124,15 +126,15 @@ of requirements from a sequence of egg directories:
>>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs]) >>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs])
... # doctest: +NORMALIZE_WHITESPACE ... # doctest: +NORMALIZE_WHITESPACE
['/tmp/tmpQeJjpkeggtest/dist/demo-0.1-py2.4.egg', ['/tmp/xyzsample-eggs/demo-0.1-py2.3.egg',
'/tmp/tmpQeJjpkeggtest/dist/demoneeded-1.0-py2.4.egg'] '/tmp/xyzsample-eggs/demoneeded-1.0-py2.3.egg']
The location method returns the distribution location for an egg that The location method returns the distribution location for an egg that
satisfies a requirement: satisfies a requirement:
>>> zc.buildout.egglinker.location('demo==0.1', [sample_eggs]) >>> zc.buildout.egglinker.location('demo==0.1', [sample_eggs])
'/tmp/tmpQeJjpkeggtest/dist/demo-0.1-py2.4.egg' '/tmp/xyzsample-eggs/demo-0.1-py2.3.egg'
The distributions function can retrieve a list of distributions found The distributions function can retrieve a list of distributions found
ineg directories that match a sequence of requirements: ineg directories that match a sequence of requirements:
...@@ -141,3 +143,73 @@ ineg directories that match a sequence of requirements: ...@@ -141,3 +143,73 @@ ineg directories that match a sequence of requirements:
... zc.buildout.egglinker.distributions(['demo==0.1'], [sample_eggs])] ... zc.buildout.egglinker.distributions(['demo==0.1'], [sample_eggs])]
[('demo', '0.1'), ('demoneeded', '1.0')] [('demo', '0.1'), ('demoneeded', '1.0')]
Using a custom Python interpreter
---------------------------------
You can pass an executable argument to egglinker methods:
>>> scripts = zc.buildout.egglinker.scripts(
... ['demo==0.1'], bin, [sample_eggs],
... executable=python2_3_executable)
>>> f = open(os.path.join(bin, 'demo'))
>>> f.readline().strip() == '#!' + python2_3_executable
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/tmp/sample-eggs/dist/demo-0.1-py2.3.egg',
'/tmp/sample-eggs/dist/demoneeded-1.0-py2.3.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs],
... python2_3_executable)
... # doctest: +NORMALIZE_WHITESPACE
['/tmp/sample-eggs/dist/demo-0.1-py2.3.egg',
'/tmp/sample-eggs/dist/demoneeded-1.0-py2.3.egg']
>>> zc.buildout.egglinker.location('demo==0.1', [sample_eggs],
... python2_3_executable)
'/tmp/sample-eggs/demo-0.1-py2.3.egg'
>>> [(d.project_name, d.version) for d in
... zc.buildout.egglinker.distributions(
... ['demo==0.1'], [sample_eggs], python2_3_executable)]
[('demo', '0.1'), ('demoneeded', '1.0')]
>>> scripts = zc.buildout.egglinker.scripts(
... ['demo==0.1'], bin, [sample_eggs],
... executable=python2_4_executable)
>>> f = open(os.path.join(bin, 'demo'))
>>> f.readline().strip() == '#!' + python2_4_executable
True
>>> print f.read(),
<BLANKLINE>
import sys
sys.path[0:0] = [
'/tmp/sample-eggs/dist/demo-0.1-py2.4.egg',
'/tmp/sample-eggs/dist/demoneeded-1.0-py2.4.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> zc.buildout.egglinker.path(['demo==0.1'], [sample_eggs],
... python2_4_executable)
... # doctest: +NORMALIZE_WHITESPACE
['/tmp/sample-eggs/dist/demo-0.1-py2.4.egg',
'/tmp/sample-eggs/dist/demoneeded-1.0-py2.4.egg']
>>> shutil.rmtree(bin)
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""XXX short summary goes here. """Various test-support utility functions
$Id$ $Id$
""" """
import os, re, shutil, sys, tempfile, unittest import ConfigParser, os, re, shutil, sys, tempfile, unittest
from zope.testing import doctest, renormalizing from zope.testing import doctest, renormalizing
import pkg_resources import pkg_resources
...@@ -52,7 +52,8 @@ def system(command, input=''): ...@@ -52,7 +52,8 @@ def system(command, input=''):
i.close() i.close()
return o.read() return o.read()
def buildoutSetUp(test): def buildoutSetUp(test, clear_home=True):
if clear_home:
# we both need to make sure that HOME isn't set and be prepared # we both need to make sure that HOME isn't set and be prepared
# to restore whatever it was after the test. # to restore whatever it was after the test.
test.globs['_oldhome'] = os.environ.pop('HOME', None) test.globs['_oldhome'] = os.environ.pop('HOME', None)
...@@ -96,7 +97,7 @@ def buildoutSetUp(test): ...@@ -96,7 +97,7 @@ def buildoutSetUp(test):
def buildoutTearDown(test): def buildoutTearDown(test):
shutil.rmtree(test.globs['sample_buildout']) shutil.rmtree(test.globs['sample_buildout'])
os.chdir(test.globs['__original_wd__']) os.chdir(test.globs['__original_wd__'])
if test.globs['_oldhome'] is not None: if test.globs.get('_oldhome') is not None:
os.environ['HOME'] = test.globs['_oldhome'] os.environ['HOME'] = test.globs['_oldhome']
...@@ -110,12 +111,12 @@ from pkg_resources import load_entry_point ...@@ -110,12 +111,12 @@ from pkg_resources import load_entry_point
sys.exit(load_entry_point('zc.buildout', 'console_scripts', 'buildout')()) sys.exit(load_entry_point('zc.buildout', 'console_scripts', 'buildout')())
''' '''
def runsetup(d): def runsetup(d, executable):
here = os.getcwd() here = os.getcwd()
try: try:
os.chdir(d) os.chdir(d)
os.spawnle( os.spawnle(
os.P_WAIT, sys.executable, sys.executable, os.P_WAIT, executable, executable,
'setup.py', '-q', 'bdist_egg', 'setup.py', '-q', 'bdist_egg',
{'PYTHONPATH': os.path.dirname(pkg_resources.__file__)}, {'PYTHONPATH': os.path.dirname(pkg_resources.__file__)},
) )
...@@ -123,11 +124,15 @@ def runsetup(d): ...@@ -123,11 +124,15 @@ def runsetup(d):
finally: finally:
os.chdir(here) os.chdir(here)
def create_sample_eggs(test): def create_sample_eggs(test, executable=sys.executable):
if '_sample_eggs_container' in test.globs:
sample = test.globs['_sample_eggs_container']
else:
sample = tempfile.mkdtemp('sample-eggs') sample = tempfile.mkdtemp('sample-eggs')
test.globs['_sample_eggs_container'] = sample test.globs['_sample_eggs_container'] = sample
test.globs['sample_eggs'] = os.path.join(sample, 'dist') test.globs['sample_eggs'] = os.path.join(sample, 'dist')
write(sample, 'README.txt', '') write(sample, 'README.txt', '')
write(sample, 'eggrecipedemobeeded.py', 'y=1\n') write(sample, 'eggrecipedemobeeded.py', 'y=1\n')
write( write(
sample, 'setup.py', sample, 'setup.py',
...@@ -135,8 +140,9 @@ def create_sample_eggs(test): ...@@ -135,8 +140,9 @@ def create_sample_eggs(test):
"setup(name='demoneeded', py_modules=['eggrecipedemobeeded']," "setup(name='demoneeded', py_modules=['eggrecipedemobeeded'],"
" zip_safe=True, version='1.0')\n" " zip_safe=True, version='1.0')\n"
) )
runsetup(sample) runsetup(sample, executable)
os.remove(os.path.join(sample, 'eggrecipedemobeeded.py')) os.remove(os.path.join(sample, 'eggrecipedemobeeded.py'))
for i in (1, 2, 3): for i in (1, 2, 3):
write( write(
sample, 'eggrecipedemo.py', sample, 'eggrecipedemo.py',
...@@ -152,4 +158,17 @@ def create_sample_eggs(test): ...@@ -152,4 +158,17 @@ def create_sample_eggs(test):
" entry_points={'console_scripts': ['demo = eggrecipedemo:main']}," " entry_points={'console_scripts': ['demo = eggrecipedemo:main']},"
" zip_safe=True, version='0.%s')\n" % i " zip_safe=True, version='0.%s')\n" % i
) )
runsetup(sample) runsetup(sample, executable)
def multi_python(test):
defaults = ConfigParser.RawConfigParser()
defaults.readfp(open(os.path.join(os.environ['HOME'],
'.buildout', 'default.cfg')))
p23 = defaults.get('python2.3', 'executable')
p24 = defaults.get('python2.4', 'executable')
create_sample_eggs(test, executable=p23)
create_sample_eggs(test, executable=p24)
test.globs['python2_3_executable'] = p23
test.globs['python2_4_executable'] = p24
...@@ -64,8 +64,8 @@ It is an error to create a variable-reference cycle: ...@@ -64,8 +64,8 @@ It is an error to create a variable-reference cycle:
''' '''
def linkerSetUp(test): def linkerSetUp(test):
zc.buildout.testing.buildoutSetUp(test) zc.buildout.testing.buildoutSetUp(test, clear_home=False)
zc.buildout.testing.create_sample_eggs(test) zc.buildout.testing.multi_python(test)
def linkerTearDown(test): def linkerTearDown(test):
shutil.rmtree(test.globs['_sample_eggs_container']) shutil.rmtree(test.globs['_sample_eggs_container'])
...@@ -76,6 +76,61 @@ def buildoutTearDown(test): ...@@ -76,6 +76,61 @@ def buildoutTearDown(test):
shutil.rmtree(test.globs['home']) shutil.rmtree(test.globs['home'])
zc.buildout.testing.buildoutTearDown(test) zc.buildout.testing.buildoutTearDown(test)
class PythonNormalizing(renormalizing.RENormalizing):
def _transform(self, want, got):
if '/xyzsample-eggs/' in want:
got = got.replace('-py2.4.egg', '-py2.3.egg')
firstg = got.split('\n')[0]
firstw = want.split('\n')[0]
if firstg.startswith('#!') and firstw.startswith('#!'):
firstg = ' '.join(firstg.split()[1:])
got = firstg + '\n' + '\n'.join(got.split('\n')[1:])
firstw = ' '.join(firstw.split()[1:])
want = firstw + '\n' + '\n'.join(want.split('\n')[1:])
for pattern, repl in self.patterns:
want = pattern.sub(repl, want)
got = pattern.sub(repl, got)
return want, got
def check_output(self, want, got, optionflags):
if got == want:
return True
want, got = self._transform(want, got)
if got == want:
return True
return doctest.OutputChecker.check_output(self, want, got, optionflags)
def output_difference(self, example, got, optionflags):
want = example.want
# If want is empty, use original outputter. This is useful
# when setting up tests for the first time. In that case, we
# generally use the differencer to display output, which we evaluate
# by hand.
if not want.strip():
return doctest.OutputChecker.output_difference(
self, example, got, optionflags)
# Dang, this isn't as easy to override as we might wish
original = want
want, got = self._transform(want, got)
# temporarily hack example with normalized want:
example.want = want
result = doctest.OutputChecker.output_difference(
self, example, got, optionflags)
example.want = original
return result
def test_suite(): def test_suite():
return unittest.TestSuite(( return unittest.TestSuite((
doctest.DocFileSuite( doctest.DocFileSuite(
...@@ -86,22 +141,23 @@ def test_suite(): ...@@ -86,22 +141,23 @@ def test_suite():
(re.compile('__buildout_signature__ = recipes-\S+'), (re.compile('__buildout_signature__ = recipes-\S+'),
'__buildout_signature__ = recipes-SSSSSSSSSSS'), '__buildout_signature__ = recipes-SSSSSSSSSSS'),
(re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep), (re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep),
r'/sample-\1/\3'), r'/sample-\1/\2'),
(re.compile('\S+sample-(\w+)'), (re.compile('\S+sample-(\w+)'), r'/sample-\1'),
r'/sample-\1/\3'), (re.compile('executable = \S+python\S*'),
(re.compile('executable = \S+python\S*'), 'executable = python'), 'executable = python'),
]) ])
), ),
doctest.DocFileSuite( doctest.DocFileSuite(
'egglinker.txt', 'easy_install.txt', 'egglinker.txt', 'easy_install.txt',
setUp=linkerSetUp, tearDown=linkerTearDown, setUp=linkerSetUp, tearDown=linkerTearDown,
checker=renormalizing.RENormalizing([
(re.compile('(\S+[/%(sep)s]| )' checker=PythonNormalizing([
'(\\w+-)[^ \t\n%(sep)s/]+.egg' (re.compile("'%(sep)s\S+sample-eggs%(sep)s(dist%(sep)s)?"
% dict(sep=os.path.sep) % dict(sep=os.path.sep)),
), '/sample-eggs/'),
'\\2-VVV-egg'), (re.compile("(- demo(needed)?-\d[.]\d-py)\d[.]\d[.]egg"),
(re.compile('\S+%spython(\d.\d)?' % os.path.sep), 'python') '\\1V.V.egg'),
]), ]),
), ),
doctest.DocTestSuite( doctest.DocTestSuite(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment