Commit f09b1a13 authored by Jim Fulton's avatar Jim Fulton

continue factoring

parent 1ac5425b
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
$Id$
"""
import os, shutil, sys, tempfile, urllib2
tmpeggs = tempfile.mkdtemp()
ez = {}
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
).read() in ez
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', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
dict(os.environ,
'PYTHONPATH'=
ws.find(pkg_resources.Requirement.parse('setuptools')).location
),
) == 0
ws.add_entry(tmpeggs)
ws.require('zc.buildout')
import zc.buildout.buildout
zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
shutil.rmtree(tmpeggs)
[buildout]
develop = zc.recipe.egg_ zc.recipe.testrunner zc.buildoutsupport
parts = test
# prevent slow access to cheeseshop:
index = http://download.zope.org
[test]
recipe = zc.recipe.testrunner
eggs =
zc.buildout
zc.recipe.egg
zc.recipe.testrunner
##############################################################################
#
# Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap the buildout project itself.
This is different from a normal boostrapping process because the
buildout egg itself is installed as a develop egg.
$Id$
"""
import os, sys, urllib2
for d in 'eggs', 'develop-eggs', 'bin':
if not os.path.exists(d):
os.mkdir(d)
ez = {}
exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
).read() in ez
ez['use_setuptools'](to_dir='eggs', download_delay=0)
import pkg_resources
os.spawnle(os.P_WAIT, sys.executable, sys.executable, 'setup.py',
'-q', 'develop', '-m', '-x', '-d', 'develop-eggs',
{'PYTHONPATH': os.path.dirname(pkg_resources.__file__)},
)
pkg_resources.working_set.add_entry('src')
import zc.buildout.easy_install
zc.buildout.easy_install.scripts(
['zc.buildout'], pkg_resources.working_set , sys.executable, 'bin')
sys.exit(os.spawnl(os.P_WAIT, 'bin/buildout', 'bin/buildout'))
This directory is (an experimental) place to manage Launchpad
specification bodies. Files here are referenced (via svn.zope.org
URLs) from the buildout project specifications in Launchpad,
https://features.launchpad.net/products/zc.buildout.
Repeatable (taggable) buildouts
===============================
It's important to be able to tag a buildout in a software repository
in such a way that, months, or even years later, the buildout tag can
be checked out and used to construct the same collection of parts,
with the same versions. (Note that parts could still behave
differently due to changes in parts of the environment, such as system
libraries, not controlled by the buildout.)
A feature of the buildout is it's use of eggs and the automatic
resolution of dependencies. The latest versions of dependencies are
automatically downloaded and installed. This is great during
development or when using the buildout for casual software
development, but it doesn't work very well for reproducing an old
buildout.
What's needed is some way to, when needed, record information about
the versions of eggs (and any other bits) who's versions are
determined dynamically.
Proposal
--------
We'll add a buildout option, create-repeatable. The option will
specify a file into which option information should be saved to create
a repeatable buildout. The data will be saved in a form that can be
used by the buildout or recipes in a later run. To make a tagged
buildout, a user would run the buildout with the create-repeatable
option set to a file name and then modify the buildout to be
extended-by this file.
Consider the following example buildout.cfg::
[buildout]
parts = foo
[foo]
recipe = zc.recipe.eggs
eggs = foo
eek
Now assume that:
- The current version of foo is 1.1
- Foo depends on bar =, which depends on baz. The current versions of
bar and bas are 1.1 and 2.1.
- The current version of eek is 1.5
- eek depends on ook, which is as version 1.3.
- zc.recipe.egg is at version 1.0b5
If we run the buildout with the command-line option::
buildout:create-repeatable=reapeatable.cfg
we'll get a repeatable.cfg file that looks something like::
[foo]
recipe = zc.recipe.eggs ==1.0b5
static = true
eggs = foo ==1.1
bar ==1.1
baz ==2.1
eek ==1.5
ook ==1.3
The file contains options for the foo part. The buildout software
itself added an entry for the recipe that fixes the recipe version
at the version used by the buildout.
The zc.recipe.eggs recipe added the eggs option that lists the
specifoc releases that were assembled.
Finally the buildout.cfg file can be modified to use the
repeatable.cfg file::
[buildout]
parts = foo
extended-by: repeatable.cfg
[foo]
recipe = zc.recipe.eggs
eggs = foo
eek
When the buildout is run, the options in repeatable.cfg will override
the onces in buildout.cfg, providing a repeatable buildout
Python API
----------
The recipe API will grow a repeatable method that is called after the
install method and is passed a dictionary that a recipe can store
option data in. A recipe instance will only be able to provide repeatable data
for it's part.
- Use setuptools PackageIndex objects to improve performance by
deciding whether we need to download anything without using
easy_install.
- tests
- distribution dependency links
- offline mode (there is an indirect test in the testrunner tests)
- Load from urls
- control python for develop (probbaly a new recipe)
- proper handling of extras
- Common recipes
- configure-make-make-install
- download, checkout
- Should it be possible to provide multiple recipies?
Or should recipies be combined through inheritence (or
composition)?
- Python
- Some way to freeze versions so we can have reproducable buildouts.
Maybe simple approach:
- Egg recipe outputs dependency info with debug logging
- Egg recipe has option to specify dependencies. When used,
don't automatically fetch newer data.
- Option to search python path for distros
- Part dependencies
- custom uninstall
- Fix develop so thet ordinary eggs fetched as dependencies end up
in eggs directory.
"fixed" except that fix is ineffective due to setuptools bug. :(
- spelling :)
- document recipe initialization order
Issues
- Should we include setuptools and buildout eggs for buildout process
in environment when searching for requirements?
- We don't want to look for new versions of setuptools all the time.
For now, we always use a local dist if there is one. Needs more
thought.
Buildout Egg-Installation Recipe
================================
The egg-installation recipe installes eggs into a buildout eggs
directory. It also generates scripts in a buildout bin directory with
egg paths baked into them.
The recipe provides the following options:
eggs
A list of eggs to install given as one ore more setuptools
requirement strings. Each string must be given on a separate
line.
find-links
One or more addresses of link servers to be searched for
distributions. This is optional. If not specified, links
specified in the buildout section will be used, if any.
index
The optional address of a distribution index server. If not
specified, then the option from the buildout section will be
used. If not specified in the part data or in the buildout
section, then http://www.python.org/pypi is used.
python
The name of a section defining the Python executable to use.
This defaults to buildout.
scripts
Control which scripts are generated. The value should be a list of
zero or more tokens. Each token is either a name, or a name
followed by an '=' and a new name. Only the named scripts are
generated. If no tokens are given, then script generation is
disabled. If the option isn't given at all, then all scripts
defined by the named eggs will be generated.
Custom eggs
-----------
The zc.recipe.egg:custom recipe supports building custom eggs,
currently with specialized options for building extensions.
extra-paths
Extra paths to include in a generates script.
To do
-----
- Some way to freeze the egg-versions used. This includes some way to
record which versions were selected dynamially and then a way to
require that the recorded versions be used in a later run.
- More control over script generation. In particular, some way to
specify data t be recored in the script.
Change History
==============
1.0.0a2
-------
Added a new recipe for building custom eggs from source distributions,
specifying custom distutils build_ext options.
1.0.0a1
-------
Initial public version
from setuptools import setup, find_packages
name = "zc.recipe.egg"
setup(
name = name,
version = "1.0.0a2",
author = "Jim Fulton",
author_email = "jim@zope.com",
description = "Recipe for installing Python package distributions as eggs",
long_description = open('README.txt').read(),
license = "ZPL 2.1",
keywords = "development build",
url='http://svn.zope.org/zc.buildout',
packages = find_packages('src'),
include_package_data = True,
package_dir = {'':'src'},
namespace_packages = ['zc', 'zc.recipe'],
install_requires = ['zc.buildout', 'setuptools'],
tests_require = ['zope.testing'],
test_suite = name+'.tests.test_suite',
entry_points = {'zc.buildout': ['default = %s:Egg' % name,
'custom = %s:Custom' % name,
]
},
dependency_links = ['http://download.zope.org/distribution/'],
)
__import__('pkg_resources').declare_namespace(__name__)
__import__('pkg_resources').declare_namespace(__name__)
Installation of distributions as eggs
=====================================
The zc.recipe.egg recipe can be used to install various types if
distutils distributions as eggs. It takes a number of options:
eggs
A list of eggs to install given as one ore more setuptools
requirement strings. Each string must be given on a separate
line.
find-links
A list of URLs, files, or directories to search for distributions.
index
The URL of an index server, or almost any other valid URL. :)
If not specified, the Python Package Index,
http://cheeseshop.python.org/pypi, is used. You can specify an
alternate index with this option. If you use the links option and
if the links point to the needed distributions, then the index can
be anything and will be largely ignored. In the examples, here,
we'll just point to an empty directory on our link server. This
will make our examples run a little bit faster.
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.
scripts
Control which scripts are generated. The value should be a list of
zero or more tokens. Each token is either a name, or a name
followed by an '=' and a new name. Only the named scripts are
generated. If no tokens are given, then script generation is
disabled. If the option isn't given at all, then all scripts
defined by the named eggs will be generated.
extra-paths
Extra paths to include in a generates script.
We have a link server that has a number of eggs:
>>> print get(link_server),
<html><body>
<a href="demo-0.1-py2.3.egg">demo-0.1-py2.3.egg</a><br>
<a href="demo-0.2-py2.3.egg">demo-0.2-py2.3.egg</a><br>
<a href="demo-0.3-py2.3.egg">demo-0.3-py2.3.egg</a><br>
<a href="demoneeded-1.0-py2.3.egg">demoneeded-1.0-py2.3.egg</a><br>
<a href="demoneeded-1.1-py2.3.egg">demoneeded-1.1-py2.3.egg</a><br>
<a href="index/">index/</a><br>
<a href="other-1.0-py2.3.egg">other-1.0-py2.3.egg</a><br>
</body></html>
We have a sample buildout. Let's update it's configuration file to
install the demo package.
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo<0.3
... find-links = %(server)s
... index = %(server)s/index
... """ % dict(server=link_server))
In this example, we limited ourself to revisions before 0.3. We also
specified where to find distributions using the find-links option.
Let's run the buildout:
>>> import os
>>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout),
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
link for the recipe. This egg link was actually created as part of
the sample buildout setup. Normally, when using the recipe, you'll get
a regular egg installation.)
The demo egg also defined a script and we see that the script was
installed as well:
>>> ls(sample_buildout, 'bin')
- buildout
- 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
the path for demo and any eggs it depends on included in sys.path.
This is useful for debugging and testing.
If we run the demo script, it prints out some minimal data:
>>> print system(os.path.join(sample_buildout, 'bin', 'demo')),
2 1
The value it prints out happens to be some values defined in the
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'),
... """import os, sys
... for p in sys.path:
... if 'demo' in p:
... print os.path.basename(p)
...
... """).replace('>>> ', '').replace('... ', ''),
... # 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:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... find-links = %(server)s
... index = %(server)s/index
... """ % dict(server=link_server))
>>> print system(buildout),
Then we'll get a new demo egg:
>>> ls(sample_buildout, 'eggs')
- 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.
The script is updated too:
>>> print system(os.path.join(sample_buildout, 'bin', 'demo')),
3 1
You can control which scripts get generated using the scripts option.
For example, to suppress scripts, use the scripts option without any
arguments:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... find-links = %(server)s
... index = %(server)s/index
... scripts =
... """ % dict(server=link_server))
>>> print system(buildout),
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
You can also control the name used for scripts:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... find-links = %(server)s
... index = %(server)s/index
... scripts = demo=foo
... """ % dict(server=link_server))
>>> print system(buildout),
>>> ls(sample_buildout, 'bin')
- buildout
- foo
- py-zc.buildout
If we need to include extra paths in a script, we can use the
extra-paths option:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... find-links = %(server)s
... index = %(server)s/index
... scripts = demo=foo
... extra-paths =
... /foo/bar
... /spam/eggs
... """ % dict(server=link_server))
>>> print system(buildout),
Let's look at the script that was generated:
>>> cat(sample_buildout, 'bin', 'foo') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.3
<BLANKLINE>
import sys
sys.path[0:0] = [
'/tmp/xyzsample-install/demo-0.3-py2.3.egg',
'/tmp/xyzsample-install/demoneeded-1.1-py2.3.egg',
'/foo/bar',
'/spam/eggs'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
Offline mode
------------
If the buildout offline option is set to "true", then no attempt will
be made to contact an index server:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
... offline = true
...
... [demo]
... recipe = zc.recipe.egg
... index = eek!
... scripts = demo=foo
... """ % dict(server=link_server))
>>> print system(buildout),
from zc.recipe.egg.egg import Egg
from zc.recipe.egg.custom import Custom
Egg Recipe API for other Recipes
================================
It is common for recipes to accept a collection of egg specifications
and generate scripts based on the resulting working sets. The egg
recipe provides an API that other recipes can use.
A recipe can reuse the egg recipe, supporting the eggs, find-links,
index, and python options. This is done by creating an egg recipe
instance in a recipes's contructor. In the recipe's install script,
the egg-recipe instance's working_set method is used to collect the
requested eggs and working set.
To illustrate, we create a sample recipe that is a very thin layer
around the egg recipe:
>>> mkdir(sample_buildout, 'sample')
>>> write(sample_buildout, 'sample', 'sample.py',
... """
... import logging, os
... import zc.recipe.egg
...
... class Sample:
...
... def __init__(self, buildout, name, options):
... self.egg = zc.recipe.egg.Egg(buildout, name, options)
... self.name = name
... self.options = options
...
... def install(self):
... extras = self.options['extras'].split()
... requirements, ws = self.egg.working_set(extras)
... print 'Part:', self.name
... print 'Egg requirements:'
... for r in requirements:
... print r
... print 'Working set:'
... for d in ws:
... print d
... """)
Here we instantiated the egg recipe in the constructor, saving it in
an attribute. This also initialized the options dictionary.
In our install method, we called the working_set method on the
instance we saved. The working_set method takes an optional sequence
of extra requirements to be included in the working set.
>>> write(sample_buildout, 'sample', 'setup.py',
... """
... from setuptools import setup
...
... setup(
... name = "sample",
... entry_points = {'zc.buildout': ['default = sample:Sample']},
... install_requires = 'zc.recipe.egg',
... )
... """)
>>> write(sample_buildout, 'sample', 'README.txt', " ")
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... develop = sample
... parts = sample-part
...
... [sample-part]
... recipe = sample
... eggs = demo<0.3
... find-links = %(server)s
... index = %(server)sindex
... extras = other
... """ % dict(server=link_server))
>>> import os
>>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout),
Part: sample-part
Egg requirements:
demo<0.3
Working set:
demo 0.2
other 1.0
demoneeded 1.1
We can see that the options were augmented with additionl data
computed by the egg recipe by looking at .installed.cfg:
>>> cat(sample_buildout, '.installed.cfg')
[buildout]
parts = sample-part
<BLANKLINE>
[sample-part]
__buildout_installed__ =
__buildout_signature__ = sample-6aWMvV2EJ9Ijq+bR8ugArQ==
zc.recipe.egg-cAsnudgkduAa/Fd+WJIM6Q==
setuptools-0.6-py2.4.egg
zc.buildout-+rYeCcmFuD1K/aB77XTj5A==
_b = /tmp/tmpb7kP9bsample-buildout/bin
_d = /tmp/tmpb7kP9bsample-buildout/develop-eggs
_e = /tmp/tmpb7kP9bsample-buildout/eggs
eggs = demo<0.3
executable = /usr/local/bin/python2.3
extras = other
find-links = http://localhost:27071/
index = http://localhost:27071/index
recipe = sample
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Install packages as eggs
$Id$
"""
import os, re, zipfile
import zc.buildout.easy_install
class Custom:
def __init__(self, buildout, name, options):
self.buildout = buildout
self.name = name
self.options = options
links = options.get('find-links',
buildout['buildout'].get('find-links'))
if links:
links = links.split()
options['find-links'] = '\n'.join(links)
else:
links = ()
self.links = links
index = options.get('index', buildout['buildout'].get('index'))
if index is not None:
options['index'] = index
self.index = index
options['_b'] = buildout['buildout']['bin-directory']
options['_e'] = buildout['buildout']['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']
build_ext = {}
for be_option in ('include-dirs', 'library-dirs', 'rpath'):
value = options.get(be_option)
if value is None:
continue
value = [
os.path.join(
buildout['buildout']['directory'],
v.strip()
)
for v in value.strip().split('\n')
if v.strip()
]
build_ext[be_option] = ':'.join(value)
options[be_option] = ':'.join(value)
self.build_ext = build_ext
def install(self):
if self.buildout['buildout'].get('offline') == 'true':
return
options = self.options
distribution = options.get('eggs', self.name).strip()
build_ext = dict([
(k, options[k])
for k in ('include-dirs', 'library-dirs', 'rpath')
if k in options
])
zc.buildout.easy_install.build(
distribution, options['_d'], self.build_ext,
self.links, self.index, options['executable'], [options['_e']],
)
Custon eggs
===========
Sometimes, It's necessary to provide extra control over how an egg is
created. This is commonly true for eggs with extension modules that
need to access libraries or include files.
The zc.recipe.egg:custom recipe can be used to define an egg with
custom build parameters. The currently defined parameters are:
include-dirs
A new-line separated list of directories to search for include
files.
library-dirs
A new-line separated list of directories to search for libraries
to link with.
rpath
A new-line separated list of directories to search for dynamic libraries
at run time.
In addition, the following options can be used to specify the egg:
egg
An eggs to install given as a setuptools requirement string.
This defaults to the part name.
find-links
A list of URLs, files, or directories to search for distributions.
index
The URL of an index server, or almost any other valid URL. :)
If not specified, the Python Package Index,
http://cheeseshop.python.org/pypi, is used. You can specify an
alternate index with this option. If you use the links option and
if the links point to the needed distributions, then the index can
be anything and will be largely ignored. In the examples, here,
we'll just point to an empty directory on our link server. This
will make our examples run a little bit faster.
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.
To illustrate this, we'll define a buildout that builds an egg for a
package that has a simple extension module::
#include <Python.h>
#include <extdemo.h>
static PyMethodDef methods[] = {};
PyMODINIT_FUNC
initextdemo(void)
{
PyObject *d;
d = Py_InitModule3("extdemo", methods, "");
PyDict_SetItemString(d, "val", PyInt_FromLong(EXTDEMO));
}
The extension depends on a system-dependnt include file, extdemo.h,
that defines a constant, EXTDEMO, that is exposed by the extension.
The extension module is available as a source distribution,
extdemo-1.4.tar.gz, on a distribution server.
We have a sample buildout that we'll add an include directory to with
the necessary include file:
>>> mkdir(sample_buildout, 'include')
>>> import os
>>> open(os.path.join(sample_buildout, 'include', 'extdemo.h'), 'w').write(
... "#define EXTDEMO 42\n")
We'll also update the buildout configuration file to define a part for
the egg:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = extdemo
...
... [extdemo]
... recipe = zc.recipe.egg:custom
... find-links = %(server)s
... index = %(server)s/index
... include-dirs = include
... """ % dict(server=link_server))
>>> os.chdir(sample_buildout)
>>> buildout = os.path.join(sample_buildout, 'bin', 'buildout')
>>> print system(buildout),
zip_safe flag not set; analyzing archive contents...
We got the zip_safe warning because the source distribution we used
wasn't setuptools based and thus didn't set the option.
The egg is created in the develop-eggs directory *not* the eggs
directory because it depends on buildout-specific parameters and the
eggs directory can be shared across multiple buildouts.
>>> ls(sample_buildout, 'develop-eggs')
d extdemo-1.4-py2.4-unix-i686.egg
- zc.recipe.egg.egg-link
Note that no scripts or dependencies are installed. To install
dependencies or scripts for a custom egg, define another part and use
the zc.recipe.egg recipe, listing the custom egg as one of the eggs to
be installed. The zc.recipe.egg recipe will use the installed egg.
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Install packages as eggs
$Id$
"""
import os, re, zipfile
import zc.buildout.easy_install
class Egg:
def __init__(self, buildout, name, options):
self.buildout = buildout
self.name = name
self.options = options
links = options.get('find-links',
buildout['buildout'].get('find-links'))
if links:
links = links.split()
options['find-links'] = '\n'.join(links)
else:
links = ()
self.links = links
index = options.get('index', buildout['buildout'].get('index'))
if index is not None:
options['index'] = index
self.index = index
self.extra_paths = [
os.path.join(buildout['buildout']['directory'], p.strip())
for p in options.get('extra-paths', '').split('\n')
if p.strip()
]
if self.extra_paths:
options['extra-paths'] = '\n'.join(self.extra_paths)
options['_b'] = buildout['buildout']['bin-directory']
options['_e'] = buildout['buildout']['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 working_set(self, extra=()):
"""Separate method to just get the working set
This is intended for reuse by similar recipes.
"""
options = self.options
distributions = [
r.strip()
for r in options.get('eggs', self.name).split('\n')
if r.strip()]
orig_distributions = distributions[:]
distributions.extend(extra)
if self.buildout['buildout'].get('offline') == 'true':
ws = zc.buildout.easy_install.working_set(
distributions, options['executable'],
[options['_d'], options['_e']]
)
else:
ws = zc.buildout.easy_install.install(
distributions, options['_e'],
links = self.links,
index = self.index,
executable = options['executable'],
always_unzip=options.get('unzip') == 'true',
path=[options['_d']]
)
return orig_distributions, ws
def install(self):
distributions, ws = self.working_set()
options = self.options
scripts = options.get('scripts')
if scripts or scripts is None:
if scripts is not None:
scripts = scripts.split()
scripts = dict([
('=' in s) and s.split('=', 1) or (s, s)
for s in scripts
])
return zc.buildout.easy_install.scripts(
distributions, ws, options['executable'],
options['_b'],
scripts=scripts,
extra_paths=self.extra_paths)
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 link server:
>>> print get(link_server),
<html><body>
<a href="demo-0.1-py2.3.egg">demo-0.1-py2.3.egg</a><br>
<a href="demo-0.1-py2.4.egg">demo-0.1-py2.4.egg</a><br>
<a href="demo-0.2-py2.3.egg">demo-0.2-py2.3.egg</a><br>
<a href="demo-0.2-py2.4.egg">demo-0.2-py2.4.egg</a><br>
<a href="demo-0.3-py2.3.egg">demo-0.3-py2.3.egg</a><br>
<a href="demo-0.3-py2.4.egg">demo-0.3-py2.4.egg</a><br>
<a href="demoneeded-1.0-py2.3.egg">demoneeded-1.0-py2.3.egg</a><br>
<a href="demoneeded-1.0-py2.4.egg">demoneeded-1.0-py2.4.egg</a><br>
<a href="demoneeded-1.1-py2.3.egg">demoneeded-1.1-py2.3.egg</a><br>
<a href="demoneeded-1.1-py2.4.egg">demoneeded-1.1-py2.4.egg</a><br>
<a href="index/">index/</a><br>
<a href="other-1.0-py2.3.egg">other-1.0-py2.3.egg</a><br>
<a href="other-1.0-py2.4.egg">other-1.0-py2.4.egg</a><br>
</body></html>
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
...
... [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, python23=python2_3_executable))
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.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:
>>> 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(), # doctest: +NORMALIZE_WHITESPACE
<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.1-py2.3.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> 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(), # doctest: +NORMALIZE_WHITESPACE
import sys
<BLANKLINE>
sys.path[0:0] = [
'/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.3.egg',
'/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.3.egg'
]
<BLANKLINE>
_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:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
... eggs-directory = eggs
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo <0.3
... find-links = %(server)s
... index = %(server)s/index
... python = python2.4
...
... [python2.4]
... executable = %(python24)s
...
... """ % dict(server=link_server, python24=python2_4_executable))
>>> print system(buildout),
>>> ls(sample_buildout, 'eggs')
- demo-0.2-py2.3.egg
- 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
>>> 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(), # doctest: +NORMALIZE_WHITESPACE
<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.1-py2.4.egg'
]
<BLANKLINE>
import eggrecipedemo
<BLANKLINE>
if __name__ == '__main__':
eggrecipedemo.main()
>>> 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(), # doctest: +NORMALIZE_WHITESPACE
import sys
<BLANKLINE>
sys.path[0:0] = [
'/tmp/tmp5zS2Afsample-buildout/eggs/demo-0.2-py2.4.egg',
'/tmp/tmp5zS2Afsample-buildout/eggs/demoneeded-1.1-py2.4.egg'
]
<BLANKLINE>
_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()
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import os, re, shutil, sys
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
return dirname(os.path.dirname(d), level-1)
def setUp(test):
zc.buildout.testing.buildoutSetUp(test)
open(os.path.join(test.globs['sample_buildout'],
'develop-eggs', 'zc.recipe.egg.egg-link'),
'w').write(dirname(__file__, 4))
zc.buildout.testing.create_sample_eggs(test)
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
def setUpPython(test):
zc.buildout.testing.buildoutSetUp(test)
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)
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
def setUpCustom(test):
zc.buildout.testing.buildoutSetUp(test)
open(os.path.join(test.globs['sample_buildout'],
'develop-eggs', 'zc.recipe.egg.egg-link'),
'w').write(dirname(__file__, 4))
zc.buildout.testing.create_sample_eggs(test)
zc.buildout.testing.add_source_dist(test)
zc.buildout.testing.setUpServer(test, zc.buildout.testing.make_tree(test))
def test_suite():
return unittest.TestSuite((
#doctest.DocTestSuite(),
doctest.DocFileSuite(
'README.txt',
setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([
(re.compile('(\S+[/%(sep)s]| )'
'(\\w+-)[^ \t\n%(sep)s/]+.egg'
% 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'),
(re.compile('#![^\n]+python[^\n]*\n'), '#!python\n'),
])
),
doctest.DocFileSuite(
'api.txt',
setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([
(re.compile('_b = \S+sample-buildout.bin'),
'_b = sample-buildout/bin'),
(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'),
'_e = sample-buildout/eggs'),
(re.compile('executable = \S+python\S*'),
'executable = python'),
(re.compile('index = \S+python\S+'),
'executable = python'),
(re.compile('find-links = http://localhost:\d+/'),
'find-links = http://localhost:8080/'),
(re.compile('index = http://localhost:\d+/index'),
'index = http://localhost:8080/index'),
])
),
doctest.DocFileSuite(
'selecting-python.txt',
setUp=setUpPython, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([
(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(
'custom.txt',
setUp=setUpCustom, tearDown=zc.buildout.testing.buildoutTearDown,
checker=renormalizing.RENormalizing([
(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'), ''),
]),
),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Test-Runner Recipe
==================
This recipe generates zope.testing test-runenr scripts for testing a
collection of eggs. The eggs must already be installed (using the
zc.recipe.egg recipe)
The test-runner recipe has 2 options:
- The eggs option takes the names of the eggs to be
tested. These are not installed by the recipe. They must be
installed by some other recipe (or using the buildout develop
option). The distributions are in the form os setuptools
requirements. Multiple distributions must be listed on separate
lines. This option is required.
- The script option gives the name of the script to generate, in the
buildout bin directory. Of the option isn't used, the part name
will be used.
To do
-----
- Support specifying testrunner defaults (e.g. verbosity, test file
patterns, etc.)
from setuptools import setup, find_packages
name = "zc.recipe.testrunner"
setup(
name = name,
version = "1.0.0a1",
author = "Jim Fulton",
author_email = "jim@zope.com",
description = "ZC Buildout recipe for creating test runners",
long_description=open('README.txt').read(),
license = "ZPL 2.1",
keywords = "development build testing",
url='http://svn.zope.org/zc.buildout',
packages = find_packages('src'),
include_package_data = True,
package_dir = {'':'src'},
namespace_packages = ['zc', 'zc.recipe'],
install_requires = ['zc.buildout', 'zope.testing', 'setuptools',
'zc.recipe.egg',
],
test_suite = name+'.tests.test_suite',
entry_points = {'zc.buildout': ['default = %s:TestRunner' % name]},
dependency_links = ['http://download.zope.org/distribution/'],
)
__import__('pkg_resources').declare_namespace(__name__)
__import__('pkg_resources').declare_namespace(__name__)
Test-Runner Recipe
==================
The test-runner recipe, zc.recipe.testrunner, creates a test runner
for a project.
The test-runner recipe has 2 options:
eggs
The eggs option specified a list of eggs to test given as one ore
more setuptools requirement strings. Each string must be given on
a separate line.
script
The script option gives the name of the script to generate, in the
buildout bin directory. Of the option isn't used, the part name
will be used.
(Note that, at this time, due to limitations in the Zope test runner,
the distributions cannot be zip files. TODO: Fix the test runner!)
To illustrate this, we'll create a pair of projects in our sample
buildout:
>>> mkdir(sample_buildout, 'demo')
>>> mkdir(sample_buildout, 'demo', 'demo')
>>> write(sample_buildout, 'demo', 'demo', '__init__.py', '')
>>> write(sample_buildout, 'demo', 'demo', 'tests.py',
... '''
... import unittest
...
... class TestDemo(unittest.TestCase):
... def test(self):
... pass
...
... def test_suite():
... return unittest.makeSuite(TestDemo)
... ''')
>>> write(sample_buildout, 'demo', 'setup.py',
... """
... from setuptools import setup
...
... setup(name = "demo")
... """)
>>> write(sample_buildout, 'demo', 'README.txt', '')
>>> mkdir(sample_buildout, 'demo2')
>>> mkdir(sample_buildout, 'demo2', 'demo2')
>>> write(sample_buildout, 'demo2', 'demo2', '__init__.py', '')
>>> write(sample_buildout, 'demo2', 'demo2', 'tests.py',
... '''
... import unittest
...
... class Demo2Tests(unittest.TestCase):
... def test2(self):
... pass
...
... def test_suite():
... return unittest.makeSuite(Demo2Tests)
... ''')
>>> write(sample_buildout, 'demo2', 'setup.py',
... """
... from setuptools import setup
...
... setup(name = "demo2", install_requires= ['demoneeded'])
... """)
>>> write(sample_buildout, 'demo2', 'README.txt', '')
Demo 2 depends on demoneeded:
>>> mkdir(sample_buildout, 'demoneeded')
>>> mkdir(sample_buildout, 'demoneeded', 'demoneeded')
>>> write(sample_buildout, 'demoneeded', 'demoneeded', '__init__.py', '')
>>> write(sample_buildout, 'demoneeded', 'demoneeded', 'tests.py',
... '''
... import unittest
...
... class TestNeeded(unittest.TestCase):
... def test_needed(self):
... pass
...
... def test_suite():
... return unittest.makeSuite(TestNeeded)
... ''')
>>> write(sample_buildout, 'demoneeded', 'setup.py',
... """
... from setuptools import setup
...
... setup(name = "demoneeded")
... """)
>>> write(sample_buildout, 'demoneeded', 'README.txt', '')
We'll update our buildout to install the demo project as a
develop egg and to create the test script:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... develop = demo demoneeded demo2
... parts = testdemo
... offline = true
...
... [testdemo]
... recipe = zc.recipe.testrunner
... eggs =
... demo
... demo2
... script = test
... """)
Note that we specified both demo and demo2 in the eggs
option and that we put them on separate lines.
We also specified the offline option to run the buildout in offline mode.
Now when we run the buildout:
>>> import os
>>> os.chdir(sample_buildout)
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
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:
>>> print system(os.path.join(sample_buildout, 'bin', 'test') + ' -vv'),
Running tests at level 1
Running unit tests:
Running:
test (demo.tests.TestDemo)
test2 (demo2.tests.Demo2Tests)
Ran 2 tests with 0 failures and 0 errors in 0.000 seconds.
Note that we didn't run the demoneeded tests. Tests are only run for
the eggs listed, not for their dependencies.
If we leave the script option out of the configuration, then the test
script will get it's name from the part:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... develop = demo
... parts = testdemo
... offline = true
...
... [testdemo]
... recipe = zc.recipe.testrunner
... eggs = demo
... """)
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
>>> ls(sample_buildout, 'bin')
- buildout
- py-zc.buildout
- testdemo
We can run the test script to run our demo test:
>>> print system(os.path.join(sample_buildout, 'bin', 'testdemo')),
Running unit tests:
Ran 1 tests with 0 failures and 0 errors in 0.000 seconds.
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A few built-in recipes
$Id$
"""
import os, sys
import pkg_resources
import zc.buildout.easy_install
import zc.recipe.egg
class TestRunner:
def __init__(self, buildout, name, options):
self.buildout = buildout
self.name = name
self.options = options
options['script'] = os.path.join(buildout['buildout']['bin-directory'],
options.get('script', self.name),
)
self.egg = zc.recipe.egg.Egg(buildout, name, options)
def install(self):
options = self.options
requirements, ws = self.egg.working_set(('zope.testing', ))
path = [dist.location for dist in ws]
project_names = [
pkg_resources.Requirement.parse(r).project_name
for r in requirements
]
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=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
result.append(script)
return result
tests_template = """#!%(PYTHON)s
import sys
sys.path[0:0] = [
%(PATH)s,
]
from zope.testing import testrunner
defaults = [
'--test-path', %(TESTPATH)s,
]
sys.exit(testrunner.run(defaults))
"""
##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import os, re, shutil, sys, tempfile
import pkg_resources
import zc.buildout.testing
import zc.recipe.egg
import unittest
import zope.testing
from zope.testing import doctest, renormalizing
def dirname(d, level=1):
if level == 0:
return d
return dirname(os.path.dirname(d), level-1)
def setUp(test):
zc.buildout.testing.buildoutSetUp(test)
open(os.path.join(test.globs['sample_buildout'],
'eggs', 'zc.recipe.testrunner.egg-link'),
'w').write(dirname(__file__, 4))
open(os.path.join(test.globs['sample_buildout'],
'eggs', 'zc.recipe.egg.egg-link'),
'w').write(dirname(zc.recipe.egg.__file__, 4))
# XXX assumes that zope.testing egg is a directory
open(os.path.join(test.globs['sample_buildout'],
'eggs', 'zope.testing.egg-link'),
'w').write(dirname(zope.testing.__file__, 3))
def tearDown(test):
zc.buildout.testing.buildoutTearDown(test)
def test_suite():
return unittest.TestSuite((
#doctest.DocTestSuite(),
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'),
])
),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
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