Commit b208f821 authored by Jim Fulton's avatar Jim Fulton

Added Windows support.

The sample buildout is now created using the bootstrapping mechanism
and so has a normal layout.

No longer read ~/.buildout/.default.cfg, when running tests.
(We do read a ~/.buildout/.default.cfg when testing that function, but
we manipulate HOME so that we control what is read.)
parent 140ff4e4
...@@ -192,6 +192,8 @@ Change History ...@@ -192,6 +192,8 @@ Change History
1.0.0b3 1.0.0b3
------- -------
- Added Windows support.
- Fixed some bugs in variable substitutions. - Fixed some bugs in variable substitutions.
The characters "-", "." and " ", weren't allowed in section or The characters "-", "." and " ", weren't allowed in section or
......
...@@ -31,14 +31,18 @@ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) ...@@ -31,14 +31,18 @@ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
import pkg_resources import pkg_resources
cmd = 'from setuptools.command.easy_install import main; main()'
if sys.platform == 'win32':
cmd = '"%s"' % cmd # work around spawn lamosity on windows
ws = pkg_resources.working_set ws = pkg_resources.working_set
assert os.spawnle( assert os.spawnle(
os.P_WAIT, sys.executable, sys.executable, os.P_WAIT, sys.executable, sys.executable,
'-c', 'from setuptools.command.easy_install import main; main()', '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
'-mqNxd', tmpeggs, 'zc.buildout', dict(os.environ,
{'PYTHONPATH': 'PYTHONPATH'=
ws.find(pkg_resources.Requirement.parse('setuptools')).location ws.find(pkg_resources.Requirement.parse('setuptools')).location
}, ),
) == 0 ) == 0
ws.add_entry(tmpeggs) ws.add_entry(tmpeggs)
......
############################################################################## #############################################################################
# #
# Copyright (c) 2005 Zope Corporation and Contributors. # Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved. # All Rights Reserved.
...@@ -139,6 +139,8 @@ class Buildout(dict): ...@@ -139,6 +139,8 @@ class Buildout(dict):
options['installed'] = os.path.join(options['directory'], options['installed'] = os.path.join(options['directory'],
options['installed']) options['installed'])
self._setup_logging()
def _dosubs(self, section, option, value, data, converted, seen): def _dosubs(self, section, option, value, data, converted, seen):
key = section, option key = section, option
r = converted.get(key) r = converted.get(key)
...@@ -215,7 +217,7 @@ class Buildout(dict): ...@@ -215,7 +217,7 @@ class Buildout(dict):
r = pkg_resources.Requirement.parse(name) r = pkg_resources.Requirement.parse(name)
dist = pkg_resources.working_set.find(r) dist = pkg_resources.working_set.find(r)
if dist.precedence == pkg_resources.DEVELOP_DIST: if dist.precedence == pkg_resources.DEVELOP_DIST:
dest = os.path.join(self['buildout']['develop-eggs-directory'], dest = os.path.join(self['buildout']['eggs-directory'],
name+'.egg-link') name+'.egg-link')
open(dest, 'w').write(dist.location) open(dest, 'w').write(dist.location)
entries.append(dist.location) entries.append(dist.location)
...@@ -351,9 +353,14 @@ class Buildout(dict): ...@@ -351,9 +353,14 @@ class Buildout(dict):
os.chdir(os.path.dirname(setup)) os.chdir(os.path.dirname(setup))
os.spawnle( os.spawnle(
os.P_WAIT, sys.executable, sys.executable, os.P_WAIT, sys.executable, sys.executable,
setup, '-q', 'develop', '-m', '-x', '-N', zc.buildout.easy_install._safe_arg(setup),
'-f', ' '.join(self._links), '-q', 'develop', '-m', '-x', '-N',
'-d', self['buildout']['develop-eggs-directory'], '-f', zc.buildout.easy_install._safe_arg(
' '.join(self._links)
),
'-d', zc.buildout.easy_install._safe_arg(
self['buildout']['develop-eggs-directory']
),
{'PYTHONPATH': {'PYTHONPATH':
os.path.dirname(pkg_resources.__file__)}, os.path.dirname(pkg_resources.__file__)},
) )
...@@ -680,12 +687,6 @@ def main(args=None): ...@@ -680,12 +687,6 @@ def main(args=None):
if verbosity: if verbosity:
options.append(('buildout', 'verbosity', str(verbosity))) options.append(('buildout', 'verbosity', str(verbosity)))
try:
buildout = Buildout(config_file, options)
buildout._setup_logging()
except UserError, v:
_error(str(v))
if args: if args:
command = args.pop(0) command = args.pop(0)
if command not in ('install', 'bootstrap'): if command not in ('install', 'bootstrap'):
...@@ -695,6 +696,7 @@ def main(args=None): ...@@ -695,6 +696,7 @@ def main(args=None):
try: try:
try: try:
buildout = Buildout(config_file, options)
getattr(buildout, command)(args) getattr(buildout, command)(args)
except UserError, v: except UserError, v:
_error(str(v)) _error(str(v))
......
...@@ -11,7 +11,7 @@ may refer to such an instance of the application informally as "a Foo ...@@ -11,7 +11,7 @@ may refer to such an instance of the application informally as "a Foo
buildout". buildout".
This document describes how to define buildouts using buildout This document describes how to define buildouts using buildout
configuration files and recipes. There are two ways to set up the configuration files and recipes. There are three ways to set up the
buildout software and create a buildout instance: buildout software and create a buildout instance:
1. Install the zc.buildout egg with easy_install and use the buildout 1. Install the zc.buildout egg with easy_install and use the buildout
...@@ -23,6 +23,10 @@ buildout software and create a buildout instance: ...@@ -23,6 +23,10 @@ buildout software and create a buildout instance:
The buildout script is installed into your buildout local scripts The buildout script is installed into your buildout local scripts
area. area.
3. Use a buildoput command from an already installed buildout to
bootstrap a new buildout. (See the section on bootstraping later
in this document.)
Often, a software project will be managed in a software repository, Often, a software project will be managed in a software repository,
such as a subversion repository, that includes some software source such as a subversion repository, that includes some software source
directories, buildout configuration files, and a copy of the buildout directories, buildout configuration files, and a copy of the buildout
...@@ -31,28 +35,27 @@ project from the repository and run the bootstrap script which ...@@ -31,28 +35,27 @@ project from the repository and run the bootstrap script which
installs setuptools and zc.buildout into the checkout as well as any installs setuptools and zc.buildout into the checkout as well as any
parts defined. parts defined.
We have a sample buildout that has already been created for us. It We have a sample buildout that we created using the bootstrap command
has the absolute minimum information. We have bin, develop-eggs, eggs of an existing buildout (method 3 above). It has the absolute minimum
and parts directories, a configuration file, and an .installed.cfg information. We have bin, develop-eggs, eggs and parts directories,
that contains information about previously-installed parts: and a configuration file:
>>> ls(sample_buildout) >>> ls(sample_buildout)
- .installed.cfg
d bin d bin
- buildout.cfg - buildout.cfg
d develop-eggs d develop-eggs
d eggs d eggs
d parts d parts
The bin directory contains scripts. In the examples shown here, we've The bin directory contains scripts.
used a hybrid approach for creating the buildout to ease automated
setup. We have a buildout script in our buildout script directory,
but the zc.buildout and setuptools eggs actually live elsewhere.
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- py-zc.buildout
>>> ls(sample_buildout, 'eggs') >>> ls(sample_buildout, 'eggs')
- setuptools-0.6-py2.4.egg
- zc.buildout-1.0-py2.4.egg
The develop-eggs and parts directories are initially empty: The develop-eggs and parts directories are initially empty:
...@@ -85,14 +88,6 @@ parts: ...@@ -85,14 +88,6 @@ parts:
[buildout] [buildout]
parts = parts =
The file .installed.cfg contains information about previously installed
parts. Because this is a new buildout, this file isn't very
interesting:
>>> cat(sample_buildout, '.installed.cfg')
[buildout]
parts =
A part is simply something to be created by a buildout. It can be A part is simply something to be created by a buildout. It can be
almost anything, such as a Python package, a program, a directory, or almost anything, such as a Python package, a program, a directory, or
even a configuration file. even a configuration file.
...@@ -146,15 +141,18 @@ directory. ...@@ -146,15 +141,18 @@ directory.
Any time we use data from another section, it is important to reflect Any time we use data from another section, it is important to reflect
that data in the recipe's options when the recipe is constructed. that data in the recipe's options when the recipe is constructed.
When a buildout is run, it compares part-configuration data stored in
the installed.cfg file and the part-configuration data loaded from the When buildout is run, it saves configuration data for installed parts
configuration files as modified by recipe constructors to decide if in a file named installed.cfg. In subsequent runs, it compares
the configuration of a part has changed. If the configuration has part-configuration data stored in the installed.cfg file and the
changed, or if the recipe has changed, then the part is uninstalled part-configuration data loaded from the configuration files as
before reinstalling it. The buildout only looks at the part's modified by recipe constructors to decide if the configuration of a
options, so any data used to configure the part needs to be reflected part has changed. If the configuration has changed, or if the recipe
in the part's options. It is the job of a recipe constructor to make has changed, then the part is uninstalled before reinstalling it. The
sure that the options include all rel event data. buildout only looks at the part's options, so any data used to
configure the part needs to be reflected in the part's options. It is
the job of a recipe constructor to make sure that the options include
all rel event data.
Of course, parts are also uninstalled if they are no-longer used. Of course, parts are also uninstalled if they are no-longer used.
...@@ -284,8 +282,8 @@ We see that the recipe created the directory, as expected: ...@@ -284,8 +282,8 @@ We see that the recipe created the directory, as expected:
d parts d parts
d recipes d recipes
In addition, .installed.cfg has been updated to reflect the part we In addition, .installed.cfg has been created containing information
installed: about the part we installed:
>>> cat(sample_buildout, '.installed.cfg') >>> cat(sample_buildout, '.installed.cfg')
[buildout] [buildout]
...@@ -1193,17 +1191,11 @@ Note that a basic setup.cfg was created for us. ...@@ -1193,17 +1191,11 @@ Note that a basic setup.cfg was created for us.
>>> ls(sample_bootstrapped, 'bin') >>> ls(sample_bootstrapped, 'bin')
- buildout - buildout
- py_zc.buildout - py-zc.buildout
>>> ls(sample_bootstrapped, 'eggs') >>> ls(sample_bootstrapped, 'eggs')
- setuptools-0.6b3-py2.3.egg - setuptools-0.6-py2.3.egg
- zc.buildout-1.0-py2.3.egg
>>> ls(sample_bootstrapped, 'develop-eggs')
- zc.buildout.egg-link
Note that, in this example, we were using a development egg for the
buildout, and the zc.buildout egg ended up as an egg link.
Also not that the buildout script was installed but not run. To run Note that the buildout script was installed but not run. To run
the buildout, we'd have to run the installed buildout script. the buildout, we'd have to run the installed buildout script.
...@@ -108,6 +108,19 @@ def _satisfied(req, env): ...@@ -108,6 +108,19 @@ def _satisfied(req, env):
return None return None
if sys.platform == 'win32':
# work around spawn lamosity on windows
# XXX need safe quoting (see the subproces.list2cmdline) and test
def _safe_arg(arg):
return '"%s"' % arg
else:
_safe_arg = str
_easy_install_cmd = _safe_arg(
'from setuptools.command.easy_install import main; main()'
)
def _call_easy_install(spec, dest, links=(), def _call_easy_install(spec, dest, links=(),
index = None, index = None,
executable=sys.executable, executable=sys.executable,
...@@ -115,11 +128,10 @@ def _call_easy_install(spec, dest, links=(), ...@@ -115,11 +128,10 @@ def _call_easy_install(spec, dest, links=(),
): ):
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)])
args = (
'-c', 'from setuptools.command.easy_install import main; main()', args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(dest))
'-mUNxd', dest)
if links: if links:
args += ('-f', ' '.join(links)) args += ('-f', _safe_arg(' '.join(links)))
if index: if index:
args += ('-i', index) args += ('-i', index)
if always_unzip: if always_unzip:
...@@ -135,7 +147,7 @@ def _call_easy_install(spec, dest, links=(), ...@@ -135,7 +147,7 @@ def _call_easy_install(spec, dest, links=(),
if level <= logging.DEBUG: if level <= logging.DEBUG:
logger.debug('Running easy_install:\n%s "%s"\npath=%s\n', logger.debug('Running easy_install:\n%s "%s"\npath=%s\n',
executable, '" "'.join(args), path) executable, '" "'.join(args), path)
args += (dict(os.environ, PYTHONPATH=path), ) args += (dict(os.environ, PYTHONPATH=path), )
sys.stdout.flush() # We want any pending output first sys.stdout.flush() # We want any pending output first
exit_code = os.spawnle(os.P_WAIT, executable, executable, *args) exit_code = os.spawnle(os.P_WAIT, executable, executable, *args)
...@@ -244,9 +256,7 @@ def install(specs, dest, ...@@ -244,9 +256,7 @@ def install(specs, dest,
def _editable(spec, dest, links=(), index = None, executable=sys.executable): def _editable(spec, dest, links=(), index = None, executable=sys.executable):
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)])
args = ( args = ('-c', _easy_install_cmd, '-eb', _safe_arg(dest))
'-c', 'from setuptools.command.easy_install import main; main()',
'-eb', dest)
if links: if links:
args += ('-f', ' '.join(links)) args += ('-f', ' '.join(links))
if index: if index:
...@@ -317,7 +327,8 @@ def working_set(specs, executable, path): ...@@ -317,7 +327,8 @@ def working_set(specs, executable, path):
def scripts(reqs, working_set, executable, dest, scripts=None): def scripts(reqs, working_set, executable, dest, scripts=None):
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 working_set]) path = repr([dist.location for dist in working_set])
path = path[1:-1].replace(',', ',\n ')
generated = [] generated = []
for dist in working_set: for dist in working_set:
...@@ -331,10 +342,12 @@ def scripts(reqs, working_set, executable, dest, scripts=None): ...@@ -331,10 +342,12 @@ def scripts(reqs, working_set, executable, dest, scripts=None):
sname = name sname = name
sname = os.path.join(dest, sname) sname = os.path.join(dest, sname)
generated.append(sname) generated.extend(
_script(dist, 'console_scripts', name, path, sname, executable) _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:
sname = scripts.get(name) sname = scripts.get(name)
else: else:
...@@ -342,13 +355,23 @@ def scripts(reqs, working_set, executable, dest, scripts=None): ...@@ -342,13 +355,23 @@ def scripts(reqs, working_set, executable, dest, 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.extend(
_pyscript(path, sname, executable) _pyscript(path, sname, executable)
)
return generated return generated
def _script(dist, group, name, path, dest, executable): def _script(dist, group, name, path, dest, executable):
entry_point = dist.get_entry_info(group, name) entry_point = dist.get_entry_info(group, name)
generated = []
if sys.platform == 'win32':
# generate exe file and give the script a magic name:
open(dest+'.exe', 'wb').write(
pkg_resources.resource_string('setuptools', 'cli.exe')
)
generated.append(dest+'.exe')
dest += '-script.py'
open(dest, 'w').write(script_template % dict( open(dest, 'w').write(script_template % dict(
python = executable, python = executable,
path = path, path = path,
...@@ -361,13 +384,15 @@ def _script(dist, group, name, path, dest, executable): ...@@ -361,13 +384,15 @@ def _script(dist, group, name, path, dest, executable):
os.chmod(dest, 0755) os.chmod(dest, 0755)
except (AttributeError, os.error): except (AttributeError, os.error):
pass pass
generated.append(dest)
return generated
script_template = '''\ script_template = '''\
#!%(python)s #!%(python)s
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
'%(path)s' %(path)s
] ]
import %(module_name)s import %(module_name)s
...@@ -378,6 +403,15 @@ if __name__ == '__main__': ...@@ -378,6 +403,15 @@ if __name__ == '__main__':
def _pyscript(path, dest, executable): def _pyscript(path, dest, executable):
generated = []
if sys.platform == 'win32':
# generate exe file and give the script a magic name:
open(dest+'.exe', 'wb').write(
pkg_resources.resource_string('setuptools', 'cli.exe')
)
generated.append(dest+'.exe')
dest += '-script.py'
open(dest, 'w').write(py_script_template % dict( open(dest, 'w').write(py_script_template % dict(
python = executable, python = executable,
path = path, path = path,
...@@ -386,21 +420,36 @@ def _pyscript(path, dest, executable): ...@@ -386,21 +420,36 @@ def _pyscript(path, dest, executable):
os.chmod(dest,0755) os.chmod(dest,0755)
except (AttributeError, os.error): except (AttributeError, os.error):
pass pass
generated.append(dest)
return generated
py_script_template = '''\ py_script_template = '''\
#!%(python)s #!%(python)s
import sys import sys
if len(sys.argv) == 1:
import os
# Restart with -i
os.execl(sys.executable, sys.executable, '-i', sys.argv[0], '')
sys.path[0:0] = [ sys.path[0:0] = [
'%(path)s' %(path)s
] ]
if len(sys.argv) > 1 and sys.argv[1:] != ['']: _interactive = True
sys.argv[:] = sys.argv[1:] if len(sys.argv) > 1:
execfile(sys.argv[0]) import getopt
_options, _args = getopt.getopt(sys.argv[1:], 'ic:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
if _args:
sys.argv[:] = _args
execfile(sys.argv[0])
if _interactive:
import code
code.interact(banner="", local=globals())
''' '''
...@@ -203,17 +203,28 @@ The bin directory now contains 2 generated scripts: ...@@ -203,17 +203,28 @@ The bin directory now contains 2 generated scripts:
>>> ls(bin) >>> ls(bin)
- demo - demo
- py_demo - py-demo
The return value is a list of the scripts generated: The return value is a list of the scripts generated:
>>> import os >>> import os, sys
>>> scripts == [os.path.join(bin, 'demo'), os.path.join(bin, 'py_demo')] >>> if sys.platform == 'win32':
... scripts == [os.path.join(bin, 'demo.exe'),
... os.path.join(bin, 'demo-script.py'),
... os.path.join(bin, 'py-demo.exe'),
... os.path.join(bin, 'py-demo-script.py')]
... else:
... scripts == [os.path.join(bin, 'demo'),
... os.path.join(bin, 'py-demo')]
True True
The demo script run the entry point defined in the demo egg: The demo script run the entry point defined in the demo egg:
>>> cat(bin, 'demo') >>> if sys.platform == 'win32':
... cat(bin, 'demo-script.py')
... else:
... cat(bin, 'demo')
... # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.3 #!/usr/local/bin/python2.3
<BLANKLINE> <BLANKLINE>
import sys import sys
...@@ -234,26 +245,40 @@ Some things to note: ...@@ -234,26 +245,40 @@ Some things to note:
- The module for the script entry point is imported and the entry - The module for the script entry point is imported and the entry
point, in this case, 'main', is run. point, in this case, 'main', is run.
The py_demo script simply run the Python interactive interpreter with The py-demo script simply run the Python interactive interpreter with
the path set: the path set:
>>> cat(bin, 'py_demo') >>> if sys.platform == 'win32':
... cat(bin, 'py-demo-script.py')
... else:
... cat(bin, 'py-demo')
... # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.4 #!/usr/local/bin/python2.4
import sys import sys
<BLANKLINE> <BLANKLINE>
if len(sys.argv) == 1:
import os
# Restart with -i
os.execl(sys.executable, sys.executable, '-i', sys.argv[0], '')
<BLANKLINE>
sys.path[0:0] = [ sys.path[0:0] = [
'/tmp/tmp5zS2Afsample-install/demo-0.3-py2.4.egg', '/tmp/tmp5zS2Afsample-install/demo-0.3-py2.4.egg',
'/tmp/tmp5zS2Afsample-install/demoneeded-1.1-py2.4.egg' '/tmp/tmp5zS2Afsample-install/demoneeded-1.1-py2.4.egg'
] ]
<BLANKLINE> <BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']: _interactive = True
sys.argv[:] = sys.argv[1:] if len(sys.argv) > 1:
execfile(sys.argv[0]) import getopt
_options, _args = getopt.getopt(sys.argv[1:], 'ic:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
<BLANKLINE>
if _args:
sys.argv[:] = _args
execfile(sys.argv[0])
<BLANKLINE>
if _interactive:
import code
code.interact(banner="", local=globals())
If invoked with a script name and arguments, it will run that script, instead. If invoked with a script name and arguments, it will run that script, instead.
...@@ -264,7 +289,12 @@ original script names to new script names. ...@@ -264,7 +289,12 @@ original script names to new script names.
>>> bin = mkdtemp() >>> bin = mkdtemp()
>>> scripts = zc.buildout.easy_install.scripts( >>> scripts = zc.buildout.easy_install.scripts(
... ['demo==0.1'], ws, python2_4_executable, bin, dict(demo='run')) ... ['demo==0.1'], ws, python2_4_executable, bin, dict(demo='run'))
>>> scripts == [os.path.join(bin, 'run')]
>>> if sys.platform == 'win32':
... scripts == [os.path.join(bin, 'run.exe'),
... os.path.join(bin, 'run-script.py')]
... else:
... scripts == [os.path.join(bin, 'run')]
True True
>>> ls(bin) >>> ls(bin)
- run - run
......
...@@ -23,6 +23,8 @@ import tempfile, threading, time, urllib2, unittest ...@@ -23,6 +23,8 @@ import tempfile, threading, time, urllib2, unittest
from zope.testing import doctest, renormalizing from zope.testing import doctest, renormalizing
import pkg_resources import pkg_resources
import zc.buildout.buildout
def cat(dir, *names): def cat(dir, *names):
path = os.path.join(dir, *names) path = os.path.join(dir, *names)
print open(path).read(), print open(path).read(),
...@@ -57,11 +59,10 @@ def system(command, input=''): ...@@ -57,11 +59,10 @@ def system(command, input=''):
def get(url): def get(url):
return urllib2.urlopen(url).read() return urllib2.urlopen(url).read()
def buildoutSetUp(test, clear_home=True): def buildoutSetUp(test):
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)
temporary_directories = [] temporary_directories = []
def mkdtemp(*args): def mkdtemp(*args):
...@@ -70,29 +71,15 @@ def buildoutSetUp(test, clear_home=True): ...@@ -70,29 +71,15 @@ def buildoutSetUp(test, clear_home=True):
return d return d
sample = mkdtemp('sample-buildout') sample = mkdtemp('sample-buildout')
for name in ('bin', 'eggs', 'develop-eggs', 'parts'):
os.mkdir(os.path.join(sample, name))
# make sure we can import zc.buildout and setuptools
import zc.buildout, setuptools
# Generate buildout script
dest = os.path.join(sample, 'bin', 'buildout')
open(dest, 'w').write(
script_template % dict(python=sys.executable, path=sys.path)
)
try:
os.chmod(dest, 0755)
except (AttributeError, os.error):
pass
# Create a basic buildout.cfg to avoid a warning from buildout:
open(os.path.join(sample, 'buildout.cfg'), 'w').write( open(os.path.join(sample, 'buildout.cfg'), 'w').write(
"[buildout]\nparts =\n" "[buildout]\nparts =\n"
) )
open(os.path.join(sample, '.installed.cfg'), 'w').write(
"[buildout]\nparts =\n" # Use the buildout bootstrap command to create a buildout
) zc.buildout.buildout.Buildout(os.path.join(sample, 'buildout.cfg'), ()
).bootstrap([])
test.globs.update(dict( test.globs.update(dict(
__here = os.getcwd(), __here = os.getcwd(),
...@@ -103,18 +90,17 @@ def buildoutSetUp(test, clear_home=True): ...@@ -103,18 +90,17 @@ def buildoutSetUp(test, clear_home=True):
write = write, write = write,
system = system, system = system,
get = get, get = get,
__original_wd__ = os.getcwd(),
__temporary_directories__ = temporary_directories, __temporary_directories__ = temporary_directories,
__tearDown__ = [], __tearDown__ = [],
mkdtemp = mkdtemp, mkdtemp = mkdtemp,
)) ))
def buildoutTearDown(test): def buildoutTearDown(test):
os.chdir(test.globs['__here'])
for d in test.globs['__temporary_directories__']: for d in test.globs['__temporary_directories__']:
shutil.rmtree(d) shutil.rmtree(d)
for f in test.globs['__tearDown__']: for f in test.globs['__tearDown__']:
f() f()
os.chdir(test.globs['__original_wd__'])
if test.globs.get('_oldhome') is not None: if test.globs.get('_oldhome') is not None:
os.environ['HOME'] = test.globs['_oldhome'] os.environ['HOME'] = test.globs['_oldhome']
...@@ -188,12 +174,45 @@ def create_sample_eggs(test, executable=sys.executable): ...@@ -188,12 +174,45 @@ def create_sample_eggs(test, executable=sys.executable):
) )
runsetup(sample, executable) runsetup(sample, executable)
def find_python(version):
e = os.environ.get('PYTHON%s' % version)
if e is not None:
return e
if sys.platform == 'win32':
e = '\Python%s%s\python.exe' % tuple(version.split('.'))
if os.path.exists(e):
return e
else:
i, o = os.popen4('python%s -c "import sys; print sys.executable"'
% version)
i.close()
e = o.read().strip()
o.close()
if os.path.exists(e):
return e
i, o = os.popen4(
'python -c "import sys; print \'%s.%s\' % sys.version_info[:2]"'
)
i.close()
e = o.read().strip()
o.close()
if e == version:
i, o = os.popen4('python -c "import sys; print sys.executable"')
i.close()
e = o.read().strip()
o.close()
if os.path.exists(e):
return e
raise ValueError(
"Couldn't figure out the exectable for Python %(version)s.\n"
"Set the environment variable PYTHON%(version)s to the location\n"
"of the Python %(version)s executable before running the tests."
)
def multi_python(test): def multi_python(test):
defaults = ConfigParser.RawConfigParser() p23 = find_python('2.3')
defaults.readfp(open(os.path.join(os.environ['HOME'], p24 = find_python('2.4')
'.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=p23)
create_sample_eggs(test, executable=p24) create_sample_eggs(test, executable=p24)
test.globs['python2_3_executable'] = p23 test.globs['python2_3_executable'] = p23
...@@ -205,7 +224,7 @@ extdemo_c = """ ...@@ -205,7 +224,7 @@ extdemo_c = """
#include <Python.h> #include <Python.h>
#include <extdemo.h> #include <extdemo.h>
static PyMethodDef methods[] = {}; static PyMethodDef methods[] = {{NULL}};
PyMODINIT_FUNC PyMODINIT_FUNC
initextdemo(void) initextdemo(void)
...@@ -238,9 +257,14 @@ def add_source_dist(test): ...@@ -238,9 +257,14 @@ def add_source_dist(test):
os.path.join(tmp, 'setup.py'), '-q', 'sdist') os.path.join(tmp, 'setup.py'), '-q', 'sdist')
os.chdir(here) os.chdir(here)
assert status == 0 assert status == 0
if sys.platform == 'win32':
sname = 'extdemo-1.4.zip'
else:
sname = 'extdemo-1.4.tar.gz'
shutil.move( shutil.move(
os.path.join(tmp, 'dist', 'extdemo-1.4.tar.gz'), os.path.join(tmp, 'dist', sname),
os.path.join(test.globs['sample_eggs'], 'extdemo-1.4.tar.gz'), os.path.join(test.globs['sample_eggs'], sname),
) )
def make_tree(test): def make_tree(test):
...@@ -311,6 +335,8 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler): ...@@ -311,6 +335,8 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
self.send_header('Content-Type', 'application/zip') self.send_header('Content-Type', 'application/zip')
elif name.endswith('.gz'): elif name.endswith('.gz'):
self.send_header('Content-Type', 'application/x-gzip') self.send_header('Content-Type', 'application/x-gzip')
elif name.endswith('.zip'):
self.send_header('Content-Type', 'application/x-gzip')
else: else:
self.send_header('Content-Type', 'text/html') self.send_header('Content-Type', 'text/html')
self.end_headers() self.end_headers()
......
...@@ -20,6 +20,10 @@ import os, re, shutil, sys, unittest ...@@ -20,6 +20,10 @@ import os, re, shutil, sys, unittest
from zope.testing import doctest, renormalizing from zope.testing import doctest, renormalizing
import zc.buildout.testing import zc.buildout.testing
os_path_sep = os.path.sep
if os_path_sep == '\\':
os_path_sep *= 2
def buildout_error_handling(): def buildout_error_handling():
r"""Buildout error handling r"""Buildout error handling
...@@ -218,7 +222,7 @@ def linkerSetUp(test): ...@@ -218,7 +222,7 @@ def linkerSetUp(test):
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test)) zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
def easy_install_SetUp(test): def easy_install_SetUp(test):
zc.buildout.testing.buildoutSetUp(test, clear_home=False) zc.buildout.testing.buildoutSetUp(test)
zc.buildout.testing.multi_python(test) zc.buildout.testing.multi_python(test)
zc.buildout.testing.add_source_dist(test) zc.buildout.testing.add_source_dist(test)
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test)) zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
...@@ -286,13 +290,18 @@ def test_suite(): ...@@ -286,13 +290,18 @@ def test_suite():
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
(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/\2'), r'/sample-\1/\2'),
(re.compile('\S+sample-(\w+)'), r'/sample-\1'), (re.compile('\S+sample-(\w+)'), r'/sample-\1'),
(re.compile('executable = \S+python\S*'), (re.compile('executable = \S+python\S*'),
'executable = python'), 'executable = python'),
(re.compile('setuptools-\S+[.]egg'), 'setuptools.egg'), (re.compile('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('creating \S*setup.cfg'), 'creating setup.cfg'),
(re.compile('(\n?)- ([a-zA-Z_.-]+)-script.py\n- \\2.exe\n'),
'\\1- \\2\n'),
(re.compile("(\w)%s(\w)" % os_path_sep), r"\1/\2"),
]) ])
), ),
...@@ -302,12 +311,20 @@ def test_suite(): ...@@ -302,12 +311,20 @@ def test_suite():
tearDown=zc.buildout.testing.buildoutTearDown, tearDown=zc.buildout.testing.buildoutTearDown,
checker=PythonNormalizing([ checker=PythonNormalizing([
(re.compile("'%(sep)s\S+sample-install%(sep)s(dist%(sep)s)?" (re.compile("'"
% dict(sep=os.path.sep)), "(\w:)?"
"[%(sep)s/]\S+sample-install[%(sep)s/]"
"[%(sep)s/]?(dist"
"[%(sep)s/])?"
% dict(sep=os_path_sep)),
'/sample-eggs/'), '/sample-eggs/'),
(re.compile("([d-] ((ext)?demo(needed)?|other)" (re.compile("([d-] ((ext)?demo(needed)?|other)"
"-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]egg"), "-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]egg"),
'\\1V.V.egg'), '\\1V.V.egg'),
(re.compile('(\n?)- ([a-zA-Z_.-]+)-script.py\n- \\2.exe\n'),
'\\1- \\2\n'),
(re.compile('extdemo-1[.]4[.]tar[.]gz'), 'extdemo-1.4.zip'),
(re.compile('#!\S+python\S+'), '#!python'),
]), ]),
), ),
doctest.DocTestSuite( doctest.DocTestSuite(
......
- Use setuptools PackageIndex objects to improve performance by
deciding whether we need to download anything without using
easy_install.
- tests - tests
- distribution dependency links - distribution dependency links
...@@ -5,14 +10,10 @@ ...@@ -5,14 +10,10 @@
- offline mode (there is an indirect test in the testrunner tests) - offline mode (there is an indirect test in the testrunner tests)
- Windows support
- Load from urls - Load from urls
- control python for develop (probbaly a new recipe) - control python for develop (probbaly a new recipe)
- Better error reporting.
- proper handling of extras - proper handling of extras
- Common recipes - Common recipes
......
...@@ -82,6 +82,8 @@ Now, if we look at the buildout eggs directory: ...@@ -82,6 +82,8 @@ 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.1-py2.3.egg - demoneeded-1.1-py2.3.egg
- setuptools-0.6-py2.3.egg
- zc.buildout-1.0-py2.3.egg
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
...@@ -95,12 +97,13 @@ installed as well: ...@@ -95,12 +97,13 @@ installed as well:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- demo - demo
- py_demo - py-demo
- py-zc.buildout
Here, in addition to the buildout script, we see the demo script, Here, in addition to the buildout script, we see the demo script,
demo, and we see a script, py_demo, for giving us a Python prompt with demo, and we see a script, py-demo, for giving us a Python prompt with
the path for demo and any eggs it depends on included in sys.path. the path for demo and any eggs it depends on included in sys.path.
This is useful for testing. This is useful for debugging and testing.
If we run the demo script, it prints out some minimal data: If we run the demo script, it prints out some minimal data:
...@@ -113,15 +116,16 @@ modules installed. ...@@ -113,15 +116,16 @@ modules installed.
We can also run the py_demo script. Here we'll just print out We can also run the py_demo script. Here we'll just print out
the bits if the path added to reflect the eggs: the bits if the path added to reflect the eggs:
>>> print system(os.path.join(sample_buildout, 'bin', 'py_demo'), >>> print system(os.path.join(sample_buildout, 'bin', 'py-demo'),
... """for p in sys.path[:2]: ... """import os, sys
... print p ... for p in sys.path:
... if 'demo' in p:
... print os.path.basename(p)
...
... """).replace('>>> ', '').replace('... ', ''), ... """).replace('>>> ', '').replace('... ', ''),
... # doctest: +ELLIPSIS ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
<BLANKLINE> demo-0.2-py2.4.egg
/tmp/tmpcy8MvGbuildout-tests/eggs/demo-0.2-py2.3.egg demoneeded-1.1-py2.4.egg
/tmp/tmpcy8MvGbuildout-tests/eggs/demoneeded-1.0-py2.3.egg
<BLANKLINE>
The recipe gets the most recent distribution that satisfies the The recipe gets the most recent distribution that satisfies the
specification. For example, We remove the restriction on demo: specification. For example, We remove the restriction on demo:
...@@ -145,6 +149,8 @@ Then we'll get a new demo egg: ...@@ -145,6 +149,8 @@ Then we'll get a new demo egg:
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demo-0.3-py2.3.egg - demo-0.3-py2.3.egg
- demoneeded-1.0-py2.3.egg - demoneeded-1.0-py2.3.egg
- setuptools-0.6-py2.4.egg
- zc.buildout-1.0-py2.4.egg
Note that we removed the eggs option, and the eggs Note that we removed the eggs option, and the eggs
defaulted to the part name. defaulted to the part name.
...@@ -176,6 +182,7 @@ arguments: ...@@ -176,6 +182,7 @@ arguments:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- py-zc.buildout
You can also control the name used for scripts: You can also control the name used for scripts:
...@@ -196,6 +203,7 @@ You can also control the name used for scripts: ...@@ -196,6 +203,7 @@ You can also control the name used for scripts:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- foo - foo
- py-zc.buildout
Offline mode Offline mode
------------ ------------
......
...@@ -37,24 +37,16 @@ install the demo package using Python 2.3. ...@@ -37,24 +37,16 @@ install the demo package using Python 2.3.
... parts = demo ... parts = demo
... eggs-directory = eggs ... eggs-directory = eggs
... ...
... [python2.3]
... executable = %(python23)s
...
... [demo] ... [demo]
... recipe = zc.recipe.egg ... recipe = zc.recipe.egg
... eggs = demo <0.3 ... eggs = demo <0.3
... find-links = %(server)s ... find-links = %(server)s
... index = %(server)s/index ... index = %(server)s/index
... python = python2.3 ... python = python2.3
... """ % dict(server=link_server)) ... """ % dict(server=link_server, python23=python2_3_executable))
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: Now, if we run the buildout:
...@@ -68,13 +60,20 @@ we'll get the Python 2.3 eggs for demo and demoneeded: ...@@ -68,13 +60,20 @@ we'll get the Python 2.3 eggs for demo and demoneeded:
>>> ls(sample_buildout, 'eggs') >>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg - demo-0.2-py2.3.egg
- demoneeded-1.1-py2.3.egg - demoneeded-1.1-py2.3.egg
- setuptools-0.6-py2.4.egg
- zc.buildout-1.0-py2.4.egg
And the generated scripts invoke Python 2.3: And the generated scripts invoke Python 2.3:
>>> f = open(os.path.join(sample_buildout, 'bin', 'demo')) >>> import sys
>>> if sys.platform == 'win32':
... script_name = 'demo-script.py'
... else:
... script_name = 'demo'
>>> f = open(os.path.join(sample_buildout, 'bin', script_name))
>>> f.readline().strip() == '#!' + python2_3_executable >>> f.readline().strip() == '#!' + python2_3_executable
True True
>>> print f.read(), >>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE> <BLANKLINE>
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
...@@ -87,25 +86,40 @@ And the generated scripts invoke Python 2.3: ...@@ -87,25 +86,40 @@ And the generated scripts invoke Python 2.3:
if __name__ == '__main__': if __name__ == '__main__':
eggrecipedemo.main() eggrecipedemo.main()
>>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo')) >>> if sys.platform == 'win32':
... f = open(os.path.join(sample_buildout, 'bin', 'py-demo-script.py'))
... else:
... f = open(os.path.join(sample_buildout, 'bin', 'py-demo'))
>>> f.readline().strip() == '#!' + python2_3_executable >>> f.readline().strip() == '#!' + python2_3_executable
True True
>>> print f.read(), >>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
import sys import sys
<BLANKLINE> <BLANKLINE>
if len(sys.argv) == 1:
import os
# Restart with -i
os.execl(sys.executable, sys.executable, '-i', sys.argv[0], '')
<BLANKLINE>
sys.path[0:0] = [ sys.path[0:0] = [
'/tmp/tmpiIJY3Ysample-buildout/eggs/demo-0.2-py2.3.egg', '/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.3.egg',
'/tmp/tmpiIJY3Ysample-buildout/eggs/demoneeded-1.1-py2.3.egg' '/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.3.egg'
] ]
<BLANKLINE> <BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']: _interactive = True
sys.argv[:] = sys.argv[1:] if len(sys.argv) > 1:
execfile(sys.argv[0]) import getopt
_options, _args = getopt.getopt(sys.argv[1:], 'ic:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
<BLANKLINE>
if _args:
sys.argv[:] = _args
execfile(sys.argv[0])
<BLANKLINE>
if _interactive:
import code
code.interact(banner="", local=globals())
>>> f.close()
If we change the Python version to 2.4, we'll use Python 2.4 eggs: If we change the Python version to 2.4, we'll use Python 2.4 eggs:
...@@ -121,7 +135,11 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs: ...@@ -121,7 +135,11 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs:
... find-links = %(server)s ... find-links = %(server)s
... index = %(server)s/index ... index = %(server)s/index
... python = python2.4 ... python = python2.4
... """ % dict(server=link_server)) ...
... [python2.4]
... executable = %(python24)s
...
... """ % dict(server=link_server, python24=python2_4_executable))
>>> print system(buildout), >>> print system(buildout),
...@@ -130,11 +148,16 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs: ...@@ -130,11 +148,16 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs:
- demo-0.2-py2.4.egg - demo-0.2-py2.4.egg
- demoneeded-1.1-py2.3.egg - demoneeded-1.1-py2.3.egg
- demoneeded-1.1-py2.4.egg - demoneeded-1.1-py2.4.egg
- setuptools-0.6-py2.4.egg
- zc.buildout-1.0-py2.4.egg
>>> f = open(os.path.join(sample_buildout, 'bin', 'demo')) >>> if sys.platform == 'win32':
... f = open(os.path.join(sample_buildout, 'bin', 'demo-script.py'))
... else:
... f = open(os.path.join(sample_buildout, 'bin', 'demo'))
>>> f.readline().strip() == '#!' + python2_4_executable >>> f.readline().strip() == '#!' + python2_4_executable
True True
>>> print f.read(), >>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE> <BLANKLINE>
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
...@@ -147,22 +170,39 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs: ...@@ -147,22 +170,39 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs:
if __name__ == '__main__': if __name__ == '__main__':
eggrecipedemo.main() eggrecipedemo.main()
>>> f = open(os.path.join(sample_buildout, 'bin', 'py_demo')) >>> f.close()
>>> if sys.platform == 'win32':
... f = open(os.path.join(sample_buildout, 'bin', 'py-demo-script.py'))
... else:
... f = open(os.path.join(sample_buildout, 'bin', 'py-demo'))
>>> f.readline().strip() == '#!' + python2_4_executable >>> f.readline().strip() == '#!' + python2_4_executable
True True
>>> print f.read(), >>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
import sys import sys
<BLANKLINE> <BLANKLINE>
if len(sys.argv) == 1:
import os
# Restart with -i
os.execl(sys.executable, sys.executable, '-i', sys.argv[0], '')
<BLANKLINE>
sys.path[0:0] = [ sys.path[0:0] = [
'/tmp/tmpiIJY3Ysample-buildout/eggs/demo-0.2-py2.4.egg', '/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.4.egg',
'/tmp/tmpiIJY3Ysample-buildout/eggs/demoneeded-1.1-py2.4.egg' '/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.4.egg'
] ]
<BLANKLINE> <BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']: _interactive = True
sys.argv[:] = sys.argv[1:] if len(sys.argv) > 1:
execfile(sys.argv[0]) import getopt
_options, _args = getopt.getopt(sys.argv[1:], 'ic:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
<BLANKLINE>
if _args:
sys.argv[:] = _args
execfile(sys.argv[0])
<BLANKLINE>
if _interactive:
import code
code.interact(banner="", local=globals())
>>> f.close()
...@@ -18,6 +18,10 @@ import zc.buildout.testing ...@@ -18,6 +18,10 @@ import zc.buildout.testing
import unittest import unittest
from zope.testing import doctest, renormalizing from zope.testing import doctest, renormalizing
os_path_sep = os.path.sep
if os_path_sep == '\\':
os_path_sep *= 2
def dirname(d, level=1): def dirname(d, level=1):
if level == 0: if level == 0:
return d return d
...@@ -33,7 +37,7 @@ def setUp(test): ...@@ -33,7 +37,7 @@ def setUp(test):
def setUpPython(test): def setUpPython(test):
zc.buildout.testing.buildoutSetUp(test, clear_home=False) zc.buildout.testing.buildoutSetUp(test)
open(os.path.join(test.globs['sample_buildout'], open(os.path.join(test.globs['sample_buildout'],
'develop-eggs', 'zc.recipe.egg.egg-link'), 'develop-eggs', 'zc.recipe.egg.egg-link'),
...@@ -61,10 +65,14 @@ def test_suite(): ...@@ -61,10 +65,14 @@ def test_suite():
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
(re.compile('(\S+[/%(sep)s]| )' (re.compile('(\S+[/%(sep)s]| )'
'(\\w+-)[^ \t\n%(sep)s/]+.egg' '(\\w+-)[^ \t\n%(sep)s/]+.egg'
% dict(sep=os.path.sep) % dict(sep=os_path_sep)
), ),
'\\2-VVV-egg'), '\\2-VVV-egg'),
(re.compile('-py\d[.]\d.egg'), '-py2.4.egg'), (re.compile('-py\d[.]\d.egg'), '-py2.4.egg'),
(re.compile('zc.buildout(-\S+)?[.]egg(-link)?'),
'zc.buildout.egg'),
(re.compile('(\n?)- ([a-zA-Z_.-]+)-script.py\n- \\2.exe\n'),
'\\1- \\2\n'),
]) ])
), ),
doctest.DocFileSuite( doctest.DocFileSuite(
...@@ -73,8 +81,13 @@ def test_suite(): ...@@ -73,8 +81,13 @@ def test_suite():
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
(re.compile('_b = \S+sample-buildout.bin'), (re.compile('_b = \S+sample-buildout.bin'),
'_b = sample-buildout/bin'), '_b = sample-buildout/bin'),
(re.compile('__buildout_signature__ = \S+'), (re.compile('__buildout_signature__ = '
'__buildout_signature__ = sample-6aWMvV2EJ9Ijq+bR8ugArQ=='), 'sample-\S+\s+'
'zc.recipe.egg-\S+\s+'
'setuptools-\S+\s+'
'zc.buildout-\S+\s*'
),
'__buildout_signature__ = sample- zc.recipe.egg-'),
(re.compile('_d = \S+sample-buildout.develop-eggs'), (re.compile('_d = \S+sample-buildout.develop-eggs'),
'_d = sample-buildout/develop-eggs'), '_d = sample-buildout/develop-eggs'),
(re.compile('_e = \S+sample-buildout.eggs'), (re.compile('_e = \S+sample-buildout.eggs'),
...@@ -93,9 +106,14 @@ def test_suite(): ...@@ -93,9 +106,14 @@ def test_suite():
'selecting-python.txt', 'selecting-python.txt',
setUp=setUpPython, tearDown=zc.buildout.testing.buildoutTearDown, setUp=setUpPython, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([ checker=renormalizing.RENormalizing([
(re.compile('\S+sample-(\w+)%s(\S+)' % os.path.sep), (re.compile('\S+sample-(\w+)[%(sep)s/](\S+)'
% dict(sep=os_path_sep)),
r'/sample-\1/\2'), r'/sample-\1/\2'),
(re.compile('\S+sample-(\w+)'), r'/sample-\1'), (re.compile('\S+sample-(\w+)'), r'/sample-\1'),
(re.compile('- ([a-zA-Z_0-9.]+)(-\S+)?[.]egg(-link)?'),
'\\1.egg'),
(re.compile(r'\\\\'), '/'),
(re.compile(r'/\\'), '/'),
]), ]),
), ),
doctest.DocFileSuite( doctest.DocFileSuite(
...@@ -105,6 +123,7 @@ def test_suite(): ...@@ -105,6 +123,7 @@ def test_suite():
(re.compile("(d ((ext)?demo(needed)?|other)" (re.compile("(d ((ext)?demo(needed)?|other)"
"-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]egg"), "-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]egg"),
'\\1V.V.egg'), '\\1V.V.egg'),
(re.compile('extdemo.c\n.+\\extdemo.exp\n'), ''),
]), ]),
), ),
......
...@@ -129,6 +129,7 @@ We get a test script installed in our bin directory: ...@@ -129,6 +129,7 @@ We get a test script installed in our bin directory:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- py-zc.buildout
- test - test
We can run the test script to run our demo test: We can run the test script to run our demo test:
...@@ -163,6 +164,7 @@ script will get it's name from the part: ...@@ -163,6 +164,7 @@ script will get it's name from the part:
>>> ls(sample_buildout, 'bin') >>> ls(sample_buildout, 'bin')
- buildout - buildout
- py-zc.buildout
- testdemo - testdemo
We can run the test script to run our demo test: We can run the test script to run our demo test:
......
...@@ -32,7 +32,6 @@ class TestRunner: ...@@ -32,7 +32,6 @@ class TestRunner:
) )
self.egg = zc.recipe.egg.Egg(buildout, name, options) self.egg = zc.recipe.egg.Egg(buildout, name, options)
def install(self): def install(self):
options = self.options options = self.options
requirements, ws = self.egg.working_set(('zope.testing', )) requirements, ws = self.egg.working_set(('zope.testing', ))
...@@ -46,31 +45,43 @@ class TestRunner: ...@@ -46,31 +45,43 @@ class TestRunner:
locations = [dist.location for dist in ws locations = [dist.location for dist in ws
if dist.project_name in project_names] if dist.project_name in project_names]
result = []
script = options['script'] script = options['script']
if sys.platform == 'win32':
# generate exe file and give the script a magic name:
open(script+'.exe', 'wb').write(
pkg_resources.resource_string('setuptools', 'cli.exe')
)
result.append(script+'.exe')
script += '-script.py'
open(script, 'w').write(tests_template % dict( open(script, 'w').write(tests_template % dict(
PYTHON=options['executable'], PYTHON=options['executable'],
PATH="',\n '".join(path), PATH=repr(path)[1:-1].replace(', ', ',\n '),
TESTPATH="',\n '--test-path', '".join(locations), TESTPATH=repr(locations)[1:-1].replace(
', ', ",\n '--test-path', "),
)) ))
try: try:
os.chmod(script, 0755) os.chmod(script, 0755)
except (AttributeError, os.error): except (AttributeError, os.error):
pass pass
return script result.append(script)
return result
tests_template = """#!%(PYTHON)s tests_template = """#!%(PYTHON)s
import sys import sys
sys.path[0:0] = [ sys.path[0:0] = [
'%(PATH)s', %(PATH)s,
] ]
from zope.testing import testrunner from zope.testing import testrunner
defaults = [ defaults = [
'--test-path', '%(TESTPATH)s', '--test-path', %(TESTPATH)s,
] ]
sys.exit(testrunner.run(defaults)) sys.exit(testrunner.run(defaults))
......
...@@ -19,7 +19,7 @@ import zc.recipe.egg ...@@ -19,7 +19,7 @@ import zc.recipe.egg
import unittest import unittest
import zope.testing import zope.testing
from zope.testing import doctest from zope.testing import doctest, renormalizing
def dirname(d, level=1): def dirname(d, level=1):
if level == 0: if level == 0:
...@@ -50,6 +50,10 @@ def test_suite(): ...@@ -50,6 +50,10 @@ def test_suite():
doctest.DocFileSuite( doctest.DocFileSuite(
'README.txt', 'README.txt',
setUp=setUp, tearDown=tearDown, setUp=setUp, tearDown=tearDown,
checker=renormalizing.RENormalizing([
(re.compile('(\n?)- ([a-zA-Z_.-]+)-script.py\n- \\2.exe\n'),
'\\1- \\2\n'),
])
), ),
)) ))
......
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