Commit 2455d893 authored by jim's avatar jim

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.)


git-svn-id: http://svn.zope.org/repos/main/zc.buildout/trunk@69806 62d5b8a3-27da-0310-9561-8e5933582275
parent 825d1c54
......@@ -192,6 +192,8 @@ Change History
1.0.0b3
-------
- Added Windows support.
- Fixed some bugs in variable substitutions.
The characters "-", "." and " ", weren't allowed in section or
......
......@@ -31,14 +31,18 @@ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
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
assert os.spawnle(
os.P_WAIT, sys.executable, sys.executable,
'-c', 'from setuptools.command.easy_install import main; main()',
'-mqNxd', tmpeggs, 'zc.buildout',
{'PYTHONPATH':
ws.find(pkg_resources.Requirement.parse('setuptools')).location
},
'-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
dict(os.environ,
'PYTHONPATH'=
ws.find(pkg_resources.Requirement.parse('setuptools')).location
),
) == 0
ws.add_entry(tmpeggs)
......
##############################################################################
#############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved.
......@@ -139,6 +139,8 @@ class Buildout(dict):
options['installed'] = os.path.join(options['directory'],
options['installed'])
self._setup_logging()
def _dosubs(self, section, option, value, data, converted, seen):
key = section, option
r = converted.get(key)
......@@ -215,7 +217,7 @@ class Buildout(dict):
r = pkg_resources.Requirement.parse(name)
dist = pkg_resources.working_set.find(r)
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')
open(dest, 'w').write(dist.location)
entries.append(dist.location)
......@@ -351,9 +353,14 @@ class Buildout(dict):
os.chdir(os.path.dirname(setup))
os.spawnle(
os.P_WAIT, sys.executable, sys.executable,
setup, '-q', 'develop', '-m', '-x', '-N',
'-f', ' '.join(self._links),
'-d', self['buildout']['develop-eggs-directory'],
zc.buildout.easy_install._safe_arg(setup),
'-q', 'develop', '-m', '-x', '-N',
'-f', zc.buildout.easy_install._safe_arg(
' '.join(self._links)
),
'-d', zc.buildout.easy_install._safe_arg(
self['buildout']['develop-eggs-directory']
),
{'PYTHONPATH':
os.path.dirname(pkg_resources.__file__)},
)
......@@ -680,12 +687,6 @@ def main(args=None):
if verbosity:
options.append(('buildout', 'verbosity', str(verbosity)))
try:
buildout = Buildout(config_file, options)
buildout._setup_logging()
except UserError, v:
_error(str(v))
if args:
command = args.pop(0)
if command not in ('install', 'bootstrap'):
......@@ -695,6 +696,7 @@ def main(args=None):
try:
try:
buildout = Buildout(config_file, options)
getattr(buildout, command)(args)
except UserError, v:
_error(str(v))
......
......@@ -11,7 +11,7 @@ may refer to such an instance of the application informally as "a Foo
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:
1. Install the zc.buildout egg with easy_install and use the buildout
......@@ -23,6 +23,10 @@ buildout software and create a buildout instance:
The buildout script is installed into your buildout local scripts
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,
such as a subversion repository, that includes some software source
directories, buildout configuration files, and a copy of the buildout
......@@ -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
parts defined.
We have a sample buildout that has already been created for us. It
has the absolute minimum information. We have bin, develop-eggs, eggs
and parts directories, a configuration file, and an .installed.cfg
that contains information about previously-installed parts:
We have a sample buildout that we created using the bootstrap command
of an existing buildout (method 3 above). It has the absolute minimum
information. We have bin, develop-eggs, eggs and parts directories,
and a configuration file:
>>> ls(sample_buildout)
- .installed.cfg
d bin
- buildout.cfg
d develop-eggs
d eggs
d parts
The bin directory contains scripts. In the examples shown here, we've
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.
The bin directory contains scripts.
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
>>> 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:
......@@ -85,14 +88,6 @@ parts:
[buildout]
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
almost anything, such as a Python package, a program, a directory, or
even a configuration file.
......@@ -146,15 +141,18 @@ directory.
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.
When a buildout is run, it compares part-configuration data stored in
the installed.cfg file and the part-configuration data loaded from the
configuration files as modified by recipe constructors to decide if
the configuration of a part has changed. If the configuration has
changed, or if the recipe has changed, then the part is uninstalled
before reinstalling it. The 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.
When buildout is run, it saves configuration data for installed parts
in a file named installed.cfg. In subsequent runs, it compares
part-configuration data stored in the installed.cfg file and the
part-configuration data loaded from the configuration files as
modified by recipe constructors to decide if the configuration of a
part has changed. If the configuration has changed, or if the recipe
has changed, then the part is uninstalled before reinstalling it. The
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.
......@@ -284,8 +282,8 @@ We see that the recipe created the directory, as expected:
d parts
d recipes
In addition, .installed.cfg has been updated to reflect the part we
installed:
In addition, .installed.cfg has been created containing information
about the part we installed:
>>> cat(sample_buildout, '.installed.cfg')
[buildout]
......@@ -1193,17 +1191,11 @@ Note that a basic setup.cfg was created for us.
>>> ls(sample_bootstrapped, 'bin')
- buildout
- py_zc.buildout
- py-zc.buildout
>>> ls(sample_bootstrapped, 'eggs')
- setuptools-0.6b3-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.
- setuptools-0.6-py2.3.egg
- zc.buildout-1.0-py2.3.egg
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.
......@@ -108,6 +108,19 @@ def _satisfied(req, env):
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=(),
index = None,
executable=sys.executable,
......@@ -115,11 +128,10 @@ def _call_easy_install(spec, dest, links=(),
):
prefix = sys.exec_prefix + os.path.sep
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()',
'-mUNxd', dest)
args = ('-c', _easy_install_cmd, '-mUNxd', _safe_arg(dest))
if links:
args += ('-f', ' '.join(links))
args += ('-f', _safe_arg(' '.join(links)))
if index:
args += ('-i', index)
if always_unzip:
......@@ -135,7 +147,7 @@ def _call_easy_install(spec, dest, links=(),
if level <= logging.DEBUG:
logger.debug('Running easy_install:\n%s "%s"\npath=%s\n',
executable, '" "'.join(args), path)
args += (dict(os.environ, PYTHONPATH=path), )
sys.stdout.flush() # We want any pending output first
exit_code = os.spawnle(os.P_WAIT, executable, executable, *args)
......@@ -244,9 +256,7 @@ def install(specs, dest,
def _editable(spec, dest, links=(), index = None, executable=sys.executable):
prefix = sys.exec_prefix + os.path.sep
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()',
'-eb', dest)
args = ('-c', _easy_install_cmd, '-eb', _safe_arg(dest))
if links:
args += ('-f', ' '.join(links))
if index:
......@@ -317,7 +327,8 @@ def working_set(specs, executable, path):
def scripts(reqs, working_set, executable, dest, scripts=None):
reqs = [pkg_resources.Requirement.parse(r) 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 = []
for dist in working_set:
......@@ -331,10 +342,12 @@ def scripts(reqs, working_set, executable, dest, scripts=None):
sname = name
sname = os.path.join(dest, sname)
generated.append(sname)
_script(dist, 'console_scripts', name, path, sname, executable)
generated.extend(
_script(dist, 'console_scripts', name, path, sname,
executable)
)
name = 'py_'+dist.project_name
name = 'py-'+dist.project_name
if scripts is not None:
sname = scripts.get(name)
else:
......@@ -342,13 +355,23 @@ def scripts(reqs, working_set, executable, dest, scripts=None):
if sname is not None:
sname = os.path.join(dest, sname)
generated.append(sname)
_pyscript(path, sname, executable)
generated.extend(
_pyscript(path, sname, executable)
)
return generated
def _script(dist, group, name, path, dest, executable):
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(
python = executable,
path = path,
......@@ -361,13 +384,15 @@ def _script(dist, group, name, path, dest, executable):
os.chmod(dest, 0755)
except (AttributeError, os.error):
pass
generated.append(dest)
return generated
script_template = '''\
#!%(python)s
import sys
sys.path[0:0] = [
'%(path)s'
%(path)s
]
import %(module_name)s
......@@ -378,6 +403,15 @@ if __name__ == '__main__':
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(
python = executable,
path = path,
......@@ -386,21 +420,36 @@ def _pyscript(path, dest, executable):
os.chmod(dest,0755)
except (AttributeError, os.error):
pass
generated.append(dest)
return generated
py_script_template = '''\
#!%(python)s
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] = [
'%(path)s'
%(path)s
]
if len(sys.argv) > 1 and sys.argv[1:] != ['']:
sys.argv[:] = sys.argv[1:]
execfile(sys.argv[0])
_interactive = True
if len(sys.argv) > 1:
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:
>>> ls(bin)
- demo
- py_demo
- py-demo
The return value is a list of the scripts generated:
>>> import os
>>> scripts == [os.path.join(bin, 'demo'), os.path.join(bin, 'py_demo')]
>>> import os, sys
>>> 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
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
<BLANKLINE>
import sys
......@@ -234,26 +245,40 @@ Some things to note:
- The module for the script entry point is imported and the entry
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:
>>> 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
import sys
<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] = [
'/tmp/tmp5zS2Afsample-install/demo-0.3-py2.4.egg',
'/tmp/tmp5zS2Afsample-install/demoneeded-1.1-py2.4.egg'
]
<BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']:
sys.argv[:] = sys.argv[1:]
execfile(sys.argv[0])
_interactive = True
if len(sys.argv) > 1:
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.
......@@ -264,7 +289,12 @@ original script names to new script names.
>>> bin = mkdtemp()
>>> scripts = zc.buildout.easy_install.scripts(
... ['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
>>> ls(bin)
- run
......
......@@ -23,6 +23,8 @@ import tempfile, threading, time, urllib2, unittest
from zope.testing import doctest, renormalizing
import pkg_resources
import zc.buildout.buildout
def cat(dir, *names):
path = os.path.join(dir, *names)
print open(path).read(),
......@@ -57,11 +59,10 @@ def system(command, input=''):
def get(url):
return urllib2.urlopen(url).read()
def buildoutSetUp(test, clear_home=True):
if clear_home:
# we both need to make sure that HOME isn't set and be prepared
# to restore whatever it was after the test.
test.globs['_oldhome'] = os.environ.pop('HOME', None)
def buildoutSetUp(test):
# we both need to make sure that HOME isn't set and be prepared
# to restore whatever it was after the test.
test.globs['_oldhome'] = os.environ.pop('HOME', None)
temporary_directories = []
def mkdtemp(*args):
......@@ -70,29 +71,15 @@ def buildoutSetUp(test, clear_home=True):
return d
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(
"[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(
__here = os.getcwd(),
......@@ -103,18 +90,17 @@ def buildoutSetUp(test, clear_home=True):
write = write,
system = system,
get = get,
__original_wd__ = os.getcwd(),
__temporary_directories__ = temporary_directories,
__tearDown__ = [],
mkdtemp = mkdtemp,
))
def buildoutTearDown(test):
os.chdir(test.globs['__here'])
for d in test.globs['__temporary_directories__']:
shutil.rmtree(d)
for f in test.globs['__tearDown__']:
f()
os.chdir(test.globs['__original_wd__'])
if test.globs.get('_oldhome') is not None:
os.environ['HOME'] = test.globs['_oldhome']
......@@ -188,12 +174,45 @@ def create_sample_eggs(test, executable=sys.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):
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')
p23 = find_python('2.3')
p24 = find_python('2.4')
create_sample_eggs(test, executable=p23)
create_sample_eggs(test, executable=p24)
test.globs['python2_3_executable'] = p23
......@@ -205,7 +224,7 @@ extdemo_c = """
#include <Python.h>
#include <extdemo.h>
static PyMethodDef methods[] = {};
static PyMethodDef methods[] = {{NULL}};
PyMODINIT_FUNC
initextdemo(void)
......@@ -238,9 +257,14 @@ def add_source_dist(test):
os.path.join(tmp, 'setup.py'), '-q', 'sdist')
os.chdir(here)
assert status == 0
if sys.platform == 'win32':
sname = 'extdemo-1.4.zip'
else:
sname = 'extdemo-1.4.tar.gz'
shutil.move(
os.path.join(tmp, 'dist', 'extdemo-1.4.tar.gz'),
os.path.join(test.globs['sample_eggs'], 'extdemo-1.4.tar.gz'),
os.path.join(tmp, 'dist', sname),
os.path.join(test.globs['sample_eggs'], sname),
)
def make_tree(test):
......@@ -311,6 +335,8 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
self.send_header('Content-Type', 'application/zip')
elif name.endswith('.gz'):
self.send_header('Content-Type', 'application/x-gzip')
elif name.endswith('.zip'):
self.send_header('Content-Type', 'application/x-gzip')
else:
self.send_header('Content-Type', 'text/html')
self.end_headers()
......
......@@ -20,6 +20,10 @@ import os, re, shutil, sys, unittest
from zope.testing import doctest, renormalizing
import zc.buildout.testing
os_path_sep = os.path.sep
if os_path_sep == '\\':
os_path_sep *= 2
def buildout_error_handling():
r"""Buildout error handling
......@@ -218,7 +222,7 @@ def linkerSetUp(test):
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(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.add_source_dist(test)
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
......@@ -286,13 +290,18 @@ def test_suite():
checker=renormalizing.RENormalizing([
(re.compile('__buildout_signature__ = recipes-\S+'),
'__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'),
(re.compile('\S+sample-(\w+)'), r'/sample-\1'),
(re.compile('executable = \S+python\S*'),
'executable = python'),
(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('(\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():
tearDown=zc.buildout.testing.buildoutTearDown,
checker=PythonNormalizing([
(re.compile("'%(sep)s\S+sample-install%(sep)s(dist%(sep)s)?"
% dict(sep=os.path.sep)),
(re.compile("'"
"(\w:)?"
"[%(sep)s/]\S+sample-install[%(sep)s/]"
"[%(sep)s/]?(dist"
"[%(sep)s/])?"
% dict(sep=os_path_sep)),
'/sample-eggs/'),
(re.compile("([d-] ((ext)?demo(needed)?|other)"
"-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]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(
......
- Use setuptools PackageIndex objects to improve performance by
deciding whether we need to download anything without using
easy_install.
- tests
- distribution dependency links
......@@ -5,14 +10,10 @@
- offline mode (there is an indirect test in the testrunner tests)
- Windows support
- Load from urls
- control python for develop (probbaly a new recipe)
- Better error reporting.
- proper handling of extras
- Common recipes
......
......@@ -82,6 +82,8 @@ Now, if we look at the buildout eggs directory:
>>> ls(sample_buildout, 'eggs')
- demo-0.2-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
as the egg for demoneeded, wich demo requires. (We also see an egg
......@@ -95,12 +97,13 @@ installed as well:
>>> ls(sample_buildout, 'bin')
- buildout
- demo
- py_demo
- py-demo
- py-zc.buildout
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.
This is useful for testing.
This is useful for debugging and testing.
If we run the demo script, it prints out some minimal data:
......@@ -113,15 +116,16 @@ modules installed.
We can also run the py_demo script. Here we'll just print out
the bits if the path added to reflect the eggs:
>>> print system(os.path.join(sample_buildout, 'bin', 'py_demo'),
... """for p in sys.path[:2]:
... print p
>>> print system(os.path.join(sample_buildout, 'bin', 'py-demo'),
... """import os, sys
... for p in sys.path:
... if 'demo' in p:
... print os.path.basename(p)
...
... """).replace('>>> ', '').replace('... ', ''),
... # doctest: +ELLIPSIS
<BLANKLINE>
/tmp/tmpcy8MvGbuildout-tests/eggs/demo-0.2-py2.3.egg
/tmp/tmpcy8MvGbuildout-tests/eggs/demoneeded-1.0-py2.3.egg
<BLANKLINE>
... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
demo-0.2-py2.4.egg
demoneeded-1.1-py2.4.egg
The recipe gets the most recent distribution that satisfies the
specification. For example, We remove the restriction on demo:
......@@ -145,6 +149,8 @@ Then we'll get a new demo egg:
- demo-0.2-py2.3.egg
- demo-0.3-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
defaulted to the part name.
......@@ -176,6 +182,7 @@ arguments:
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
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')
- buildout
- foo
- py-zc.buildout
Offline mode
------------
......
......@@ -37,24 +37,16 @@ install the demo package using Python 2.3.
... parts = demo
... eggs-directory = eggs
...
... [python2.3]
... executable = %(python23)s
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo <0.3
... find-links = %(server)s
... index = %(server)s/index
... python = python2.3
... """ % dict(server=link_server))
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.)
... """ % dict(server=link_server, python23=python2_3_executable))
Now, if we run the buildout:
......@@ -68,13 +60,20 @@ we'll get the Python 2.3 eggs for demo and demoneeded:
>>> ls(sample_buildout, 'eggs')
- demo-0.2-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:
>>> 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
True
>>> print f.read(),
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE>
import sys
sys.path[0:0] = [
......@@ -87,25 +86,40 @@ And the generated scripts invoke Python 2.3:
if __name__ == '__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
True
>>> print f.read(),
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
import sys
<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] = [
'/tmp/tmpiIJY3Ysample-buildout/eggs/demo-0.2-py2.3.egg',
'/tmp/tmpiIJY3Ysample-buildout/eggs/demoneeded-1.1-py2.3.egg'
'/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.3.egg',
'/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.3.egg'
]
<BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']:
sys.argv[:] = sys.argv[1:]
execfile(sys.argv[0])
_interactive = True
if len(sys.argv) > 1:
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:
......@@ -121,7 +135,11 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs:
... find-links = %(server)s
... index = %(server)s/index
... python = python2.4
... """ % dict(server=link_server))
...
... [python2.4]
... executable = %(python24)s
...
... """ % dict(server=link_server, python24=python2_4_executable))
>>> print system(buildout),
......@@ -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
- demoneeded-1.1-py2.3.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
True
>>> print f.read(),
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE>
import sys
sys.path[0:0] = [
......@@ -147,22 +170,39 @@ If we change the Python version to 2.4, we'll use Python 2.4 eggs:
if __name__ == '__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
True
>>> print f.read(),
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
import sys
<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] = [
'/tmp/tmpiIJY3Ysample-buildout/eggs/demo-0.2-py2.4.egg',
'/tmp/tmpiIJY3Ysample-buildout/eggs/demoneeded-1.1-py2.4.egg'
'/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.4.egg',
'/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.4.egg'
]
<BLANKLINE>
if len(sys.argv) > 1 and sys.argv[1:] != ['']:
sys.argv[:] = sys.argv[1:]
execfile(sys.argv[0])
_interactive = True
if len(sys.argv) > 1:
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
import unittest
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):
if level == 0:
return d
......@@ -33,7 +37,7 @@ def setUp(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'],
'develop-eggs', 'zc.recipe.egg.egg-link'),
......@@ -61,10 +65,14 @@ def test_suite():
checker=renormalizing.RENormalizing([
(re.compile('(\S+[/%(sep)s]| )'
'(\\w+-)[^ \t\n%(sep)s/]+.egg'
% dict(sep=os.path.sep)
% dict(sep=os_path_sep)
),
'\\2-VVV-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(
......@@ -73,8 +81,13 @@ def test_suite():
checker=renormalizing.RENormalizing([
(re.compile('_b = \S+sample-buildout.bin'),
'_b = sample-buildout/bin'),
(re.compile('__buildout_signature__ = \S+'),
'__buildout_signature__ = sample-6aWMvV2EJ9Ijq+bR8ugArQ=='),
(re.compile('__buildout_signature__ = '
'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'),
'_d = sample-buildout/develop-eggs'),
(re.compile('_e = \S+sample-buildout.eggs'),
......@@ -93,9 +106,14 @@ def test_suite():
'selecting-python.txt',
setUp=setUpPython, tearDown=zc.buildout.testing.buildoutTearDown,
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'),
(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(
......@@ -105,6 +123,7 @@ def test_suite():
(re.compile("(d ((ext)?demo(needed)?|other)"
"-\d[.]\d-py)\d[.]\d(-[^. \t\n]+)?[.]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:
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
- 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:
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
- testdemo
We can run the test script to run our demo test:
......
......@@ -32,7 +32,6 @@ class TestRunner:
)
self.egg = zc.recipe.egg.Egg(buildout, name, options)
def install(self):
options = self.options
requirements, ws = self.egg.working_set(('zope.testing', ))
......@@ -46,31 +45,43 @@ class TestRunner:
locations = [dist.location for dist in ws
if dist.project_name in project_names]
result = []
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(
PYTHON=options['executable'],
PATH="',\n '".join(path),
TESTPATH="',\n '--test-path', '".join(locations),
PATH=repr(path)[1:-1].replace(', ', ',\n '),
TESTPATH=repr(locations)[1:-1].replace(
', ', ",\n '--test-path', "),
))
try:
os.chmod(script, 0755)
except (AttributeError, os.error):
pass
return script
result.append(script)
return result
tests_template = """#!%(PYTHON)s
import sys
sys.path[0:0] = [
'%(PATH)s',
%(PATH)s,
]
from zope.testing import testrunner
defaults = [
'--test-path', '%(TESTPATH)s',
'--test-path', %(TESTPATH)s,
]
sys.exit(testrunner.run(defaults))
......
......@@ -19,7 +19,7 @@ import zc.recipe.egg
import unittest
import zope.testing
from zope.testing import doctest
from zope.testing import doctest, renormalizing
def dirname(d, level=1):
if level == 0:
......@@ -50,6 +50,10 @@ def test_suite():
doctest.DocFileSuite(
'README.txt',
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