[Checkins] SVN: zc.buildout/trunk/src/zc/buildout/ On two of my machines, I got spurious test failures when a test failed
Jim Fulton
jim at zope.com
Tue Mar 17 09:09:27 EDT 2009
Log message for revision 98178:
On two of my machines, I got spurious test failures when a test failed
to see that a file removed by a subprocess was actually removed.
Added logic to wait for the file to disappear. :/
Whitespace cleanup. (Although there are a few required trailing white
spaces in buildout.txt.
Changed:
U zc.buildout/trunk/src/zc/buildout/buildout.txt
U zc.buildout/trunk/src/zc/buildout/testing.py
-=-
Modified: zc.buildout/trunk/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/trunk/src/zc/buildout/buildout.txt 2009-03-17 13:09:25 UTC (rev 98177)
+++ zc.buildout/trunk/src/zc/buildout/buildout.txt 2009-03-17 13:09:27 UTC (rev 98178)
@@ -8,7 +8,7 @@
buildout" is the collection of configuration and application-specific
software that allows an instance of the application to be created. We
may refer to such an instance of the application informally as "a Foo
-buildout".
+buildout".
This document describes how to define buildouts using buildout
configuration files and recipes. There are three ways to set up the
@@ -39,7 +39,7 @@
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)
d bin
- buildout.cfg
@@ -89,7 +89,7 @@
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.
+even a configuration file.
Recipes
-------
@@ -97,7 +97,7 @@
A part is created by a recipe. Recipes are always installed as Python
eggs. They can be downloaded from a package server, such as the
Python Package Index, or they can be developed as part of a project
-using a "develop" egg.
+using a "develop" egg.
A develop egg is a special kind of egg that gets installed as an "egg
link" that contains the name of a source directory. Develop eggs
@@ -113,7 +113,7 @@
and then we'll create a source file for our mkdir recipe:
- >>> write(sample_buildout, 'recipes', 'mkdir.py',
+ >>> write(sample_buildout, 'recipes', 'mkdir.py',
... """
... import logging, os, zc.buildout
...
@@ -130,8 +130,8 @@
... 'Cannot create %s. %s is not a directory.',
... options['path'], os.path.dirname(options['path']))
... raise zc.buildout.UserError('Invalid Path')
- ...
...
+ ...
... def install(self):
... path = self.options['path']
... logging.getLogger(self.name).info(
@@ -185,7 +185,7 @@
section name to a mapping of options for that section. The buildout
directory is available as the directory option of the buildout
section. We normalize the path and save it back into the options
-directory.
+directory.
The install method is responsible for creating the part. In this
case, we need the path of the directory to create. We'll use a path
@@ -214,7 +214,7 @@
>>> write(sample_buildout, 'recipes', 'setup.py',
... """
... from setuptools import setup
- ...
+ ...
... setup(
... name = "recipes",
... entry_points = {'zc.buildout': ['mkdir = mkdir:Mkdir']},
@@ -270,7 +270,7 @@
[data-dir]
recipe = recipes:mkdir
- path = mystuff
+ path = mystuff
When we name a part, we also create a section of the same
@@ -317,7 +317,7 @@
Note that the directory we installed is included in .installed.cfg.
In addition, the path option includes the actual destination
-directory.
+directory.
If we change the name of the directory in the configuration file,
we'll see that the directory gets removed and recreated:
@@ -401,7 +401,7 @@
clean up any system side effects, such as files created. Let's update
the mkdir recipe to support multiple paths:
- >>> write(sample_buildout, 'recipes', 'mkdir.py',
+ >>> write(sample_buildout, 'recipes', 'mkdir.py',
... """
... import logging, os, zc.buildout
...
@@ -413,7 +413,7 @@
... # Normalize paths and check that their parent
... # directories exist:
... paths = []
- ... for path in options['path'].split():
+ ... for path in options['path'].split():
... path = os.path.join(buildout['buildout']['directory'], path)
... if not os.path.isdir(os.path.dirname(path)):
... logging.getLogger(self.name).error(
@@ -425,7 +425,7 @@
...
... def install(self):
... paths = self.options['path'].split()
- ... for path in paths:
+ ... for path in paths:
... logging.getLogger(self.name).info(
... 'Creating directory %s', os.path.basename(path))
... os.mkdir(path)
@@ -502,7 +502,7 @@
Let's fix the recipe:
- >>> write(sample_buildout, 'recipes', 'mkdir.py',
+ >>> write(sample_buildout, 'recipes', 'mkdir.py',
... """
... import logging, os, zc.buildout
...
@@ -514,7 +514,7 @@
... # Normalize paths and check that their parent
... # directories exist:
... paths = []
- ... for path in options['path'].split():
+ ... for path in options['path'].split():
... path = os.path.join(buildout['buildout']['directory'], path)
... if not os.path.isdir(os.path.dirname(path)):
... logging.getLogger(self.name).error(
@@ -528,7 +528,7 @@
... paths = self.options['path'].split()
... created = []
... try:
- ... for path in paths:
+ ... for path in paths:
... logging.getLogger(self.name).info(
... 'Creating directory %s', os.path.basename(path))
... os.mkdir(path)
@@ -573,6 +573,10 @@
...
OSError: [Errno 17] File exists: '/sample-buildout/bin'
+.. Wait for the file to really disappear. My linux is weird.
+
+ >>> wait_until("foo goes away", lambda : not os.path.exists('foo'))
+
we get the same error, but we don't get the directory left behind:
>>> os.path.exists('foo')
@@ -588,7 +592,7 @@
used to return the files created. Let's use this API to simplify the
recipe:
- >>> write(sample_buildout, 'recipes', 'mkdir.py',
+ >>> write(sample_buildout, 'recipes', 'mkdir.py',
... """
... import logging, os, zc.buildout
...
@@ -600,7 +604,7 @@
... # Normalize paths and check that their parent
... # directories exist:
... paths = []
- ... for path in options['path'].split():
+ ... for path in options['path'].split():
... path = os.path.join(buildout['buildout']['directory'], path)
... if not os.path.isdir(os.path.dirname(path)):
... logging.getLogger(self.name).error(
@@ -612,7 +616,7 @@
...
... def install(self):
... paths = self.options['path'].split()
- ... for path in paths:
+ ... for path in paths:
... logging.getLogger(self.name).info(
... 'Creating directory %s', os.path.basename(path))
... os.mkdir(path)
@@ -710,7 +714,7 @@
To illustrate this, we'll create an debug recipe to
allow us to see interactions with the buildout:
- >>> write(sample_buildout, 'recipes', 'debug.py',
+ >>> write(sample_buildout, 'recipes', 'debug.py',
... """
... class Debug:
...
@@ -777,7 +781,7 @@
and option name joined by a colon.
Now, if we run the buildout, we'll see the options with the values
-substituted.
+substituted.
>>> print system(buildout),
Develop: '/sample-buildout/recipes'
@@ -1069,9 +1073,9 @@
The example is pretty trivial, but the pattern it illustrates is
pretty common. In a more practical example, the base buildout might
represent a product and the extending buildout might be a
-customization.
+customization.
-Here is a more elaborate example.
+Here is a more elaborate example.
>>> other = tmpdir('other')
@@ -1171,7 +1175,7 @@
... """
... [buildout]
... extends = r1.cfg
- ...
+ ...
... [debug]
... op2 = r2 2
... op3 = r2 3
@@ -1179,7 +1183,7 @@
>>> server_url = start_server(server_data)
- >>> write('client.cfg',
+ >>> write('client.cfg',
... """
... [buildout]
... develop = recipes
@@ -1208,10 +1212,10 @@
base URL when they appear in configuration files loaded via URL.
We can also specify a URL as the configuration file to be used by a
-buildout.
+buildout.
>>> os.remove('client.cfg')
- >>> write(server_data, 'remote.cfg',
+ >>> write(server_data, 'remote.cfg',
... """
... [buildout]
... develop = recipes
@@ -1344,7 +1348,7 @@
Here's a recipe that simulates installation of a system service, along
with an uninstall recipe that simulates removing the service.
- >>> write(sample_buildout, 'recipes', 'service.py',
+ >>> write(sample_buildout, 'recipes', 'service.py',
... """
... class Service:
...
@@ -1354,7 +1358,7 @@
... self.options = options
...
... def install(self):
- ... print "chkconfig --add %s" % self.options['script']
+ ... print "chkconfig --add %s" % self.options['script']
... return ()
...
... def update(self):
@@ -1446,7 +1450,7 @@
... [buildout]
... develop = recipes
... parts = debug
- ...
+ ...
... [debug]
... recipe = recipes:debug
... """)
@@ -1469,14 +1473,14 @@
For example, here's an uninstallation recipe that simulates backing up
a directory before it is deleted. It is designed to work with the
mkdir recipe introduced earlier.
-
- >>> write(sample_buildout, 'recipes', 'backup.py',
+
+ >>> write(sample_buildout, 'recipes', 'backup.py',
... """
... import os
... def backup_directory(name, options):
... path = options['path']
... size = len(os.listdir(path))
- ... print "backing up directory %s of size %s" % (path, size)
+ ... print "backing up directory %s of size %s" % (path, size)
... """)
It must be registered with the zc.buildout.uninstall entry
@@ -1507,7 +1511,7 @@
... [buildout]
... develop = recipes
... parts = dir debug
- ...
+ ...
... [dir]
... recipe = recipes:mkdir
... path = my_directory
@@ -1534,7 +1538,7 @@
... [buildout]
... develop = recipes
... parts = debug
- ...
+ ...
... [debug]
... recipe = recipes:debug
... """)
@@ -1583,7 +1587,7 @@
-c filename
The -c option can be used to specify a configuration file, rather than
- buildout.cfg in the current directory.
+ buildout.cfg in the current directory.
-t socket_timeout
@@ -1602,11 +1606,11 @@
Don't read user-default configuration.
-o
- Run in off-line mode. This is equivalent to the assignment
+ Run in off-line mode. This is equivalent to the assignment
buildout:offline=true.
-O
- Run in non-off-line mode. This is equivalent to the assignment
+ Run in non-off-line mode. This is equivalent to the assignment
buildout:offline=false. This is the default buildout mode. The
-O option would normally be used to override a true offline
setting in a configuration file.
@@ -1618,10 +1622,10 @@
available that satisfy its requirements.
-N
- Run in non-newest mode. This is equivalent to the assignment
+ Run in non-newest mode. This is equivalent to the assignment
buildout:newest=false. With this setting, buildout will not seek
new distributions if installed distributions satisfy it's
- requirements.
+ requirements.
Assignments are of the form::
@@ -1646,7 +1650,7 @@
Note that we used the installed buildout option to specify an
alternate file to store information about installed parts.
-
+
>>> print system(buildout+' -c other.cfg debug:op1=foo -v'),
Develop: '/sample-buildout/recipes'
Installing debug.
@@ -1654,12 +1658,12 @@
op1 foo
recipe recipes:debug
-Here we used the -c option to specify an alternate configuration file,
+Here we used the -c option to specify an alternate configuration file,
and the -v option to increase the level of logging from the default,
WARNING.
Options can also be combined in the usual Unix way, as in:
-
+
>>> print system(buildout+' -vcother.cfg debug:op1=foo'),
Develop: '/sample-buildout/recipes'
Updating debug.
@@ -1712,7 +1716,7 @@
d2: Creating directory d2
Installing d3.
d3: Creating directory d3
-
+
>>> ls(sample_buildout)
- .installed.cfg
- b1.cfg
@@ -1791,7 +1795,7 @@
d3: Creating directory data3
Installing d4.
d4: Creating directory data2-extra
-
+
>>> ls(sample_buildout)
- .installed.cfg
- b1.cfg
@@ -1808,7 +1812,7 @@
d eggs
d parts
d recipes
-
+
Only the d3 and d4 recipes ran. d3 was removed and data3 and data2-extra
were created.
@@ -1899,7 +1903,7 @@
... """
... [buildout]
... develop = recipes
- ... parts =
+ ... parts =
... develop-eggs-directory = %(developbasket)s
... eggs-directory = %(basket)s
... bin-directory = %(scripts)s
@@ -1928,7 +1932,7 @@
d scripts
d work
- >>> ls(alt, 'developbasket')
+ >>> ls(alt, 'developbasket')
- recipes.egg-link
You can also specify an alternate buildout directory:
@@ -1941,12 +1945,12 @@
... [buildout]
... directory = %(alt)s
... develop = %(recipes)s
- ... parts =
+ ... parts =
... """ % dict(
... alt=alt,
... recipes=os.path.join(sample_buildout, 'recipes'),
... ))
-
+
>>> print system(buildout),
Creating directory '/sample-alt/bin'.
Creating directory '/sample-alt/parts'.
@@ -1961,7 +1965,7 @@
d eggs
d parts
- >>> ls(alt, 'develop-eggs')
+ >>> ls(alt, 'develop-eggs')
- recipes.egg-link
Logging control
@@ -1969,10 +1973,10 @@
Three buildout options are used to control logging:
-log-level
+log-level
specifies the log level
-verbosity
+verbosity
adjusts the log level
log-format
@@ -1990,7 +1994,7 @@
... verbosity = 5
... log-format = %(levelname)s %(message)s
... """)
-
+
Here, we've changed the format to include the log-level name, rather
than the logger name.
@@ -2010,7 +2014,7 @@
these, we'll run a minimal buildout configuration with a debug logging
level. One of the features of debug logging is that the configuration
database is shown.
-
+
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
@@ -2040,7 +2044,7 @@
python = buildout
verbosity = 20
<BLANKLINE>
-
+
All of these options can be overridden by configuration files or by
command-line assignments. We've discussed most of these options
already, but let's review them and touch on some we haven't discussed:
@@ -2108,7 +2112,7 @@
If zc.buildout is installed, you can use it to create a new buildout
with it's own local copies of zc.buildout and setuptools and with
-local buildout scripts.
+local buildout scripts.
>>> sample_bootstrapped = tmpdir('sample-bootstrapped')
@@ -2237,7 +2241,7 @@
You can also specify more locations to search for distributions using
the `find-links` option. All locations specified will be searched for
-distributions along with the package index as described before.
+distributions along with the package index as described before.
Locations can be urls::
@@ -2257,11 +2261,11 @@
...
find-links = /some/path/someegg-1.0.0-py2.3.egg
-Any number of locations can be specified in the `find-links` option::
+Any number of locations can be specified in the `find-links` option::
[buildout]
...
- find-links =
+ find-links =
http://download.zope.org/distribution/
/some/otherpath
/some/path/someegg-1.0.0-py2.3.egg
@@ -2288,7 +2292,7 @@
".installed.cfg", but it can be overridden in the configuration file
or on the command line:
- >>> write('buildout.cfg',
+ >>> write('buildout.cfg',
... """
... [buildout]
... develop = recipes
@@ -2341,7 +2345,7 @@
Note that there will be no installation database if there are no
parts:
- >>> write('buildout.cfg',
+ >>> write('buildout.cfg',
... """
... [buildout]
... parts =
@@ -2379,7 +2383,7 @@
>>> mkdir(sample_bootstrapped, 'demo')
- >>> write(sample_bootstrapped, 'demo', 'demo.py',
+ >>> write(sample_bootstrapped, 'demo', 'demo.py',
... """
... def ext(buildout):
... print 'ext', list(buildout)
@@ -2388,7 +2392,7 @@
>>> write(sample_bootstrapped, 'demo', 'setup.py',
... """
... from setuptools import setup
- ...
+ ...
... setup(
... name = "demo",
... entry_points = {'zc.buildout.extension': ['ext = demo:ext']},
@@ -2426,7 +2430,7 @@
... extensions = demo
... parts =
... """)
-
+
We see that our extension is loaded and executed:
>>> print system(os.path.join(sample_bootstrapped, 'bin', 'buildout')),
@@ -2437,22 +2441,22 @@
-----------
On some environments the links visited by `zc.buildout` can be forbidden
-by paranoiac firewalls. These URL might be on the chain of links
+by paranoiac firewalls. These URL might be on the chain of links
visited by `zc.buildout` wheter they are defined in the `find-links` option,
-wheter they are defined by various eggs in their `url`, `download_url`,
+wheter they are defined by various eggs in their `url`, `download_url`,
`dependency_links` metadata.
-It is even harder to track that package_index works like a spider and
+It is even harder to track that package_index works like a spider and
might visit links and go to other location.
-The `allow-hosts` option provides a way to prevent this, and
+The `allow-hosts` option provides a way to prevent this, and
works exactly like the one provided in `easy_install`.
You can provide a list of allowed host, together with wildcards::
[buildout]
...
-
+
allow-hosts =
*.python.org
example.com
Modified: zc.buildout/trunk/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/trunk/src/zc/buildout/testing.py 2009-03-17 13:09:25 UTC (rev 98177)
+++ zc.buildout/trunk/src/zc/buildout/testing.py 2009-03-17 13:09:27 UTC (rev 98178)
@@ -176,13 +176,26 @@
o.close()
if os.path.exists(e):
return e
-
+
raise ValueError(
"Couldn't figure out the executable 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."
% {'version': version})
+def wait_until(label, func, *args, **kw):
+ if 'timeout' in kw:
+ kw = dict(kw)
+ timeout = kw.pop('timeout')
+ else:
+ timeout = 30
+ deadline = time.time()+timeout
+ while time.time() < deadline:
+ if func(*args, **kw):
+ return
+ time.sleep('.01')
+ raise ValueError('Timed out waiting for: '+label)
+
def buildoutSetUp(test):
test.globs['__tear_downs'] = __tear_downs = []
@@ -214,7 +227,7 @@
tmp = tempfile.mkdtemp('buildouttests')
register_teardown(lambda: rmtree(tmp))
-
+
zc.buildout.easy_install.default_index_url = 'file://'+tmp
os.environ['buildout-testing-index-url'] = (
zc.buildout.easy_install.default_index_url)
@@ -243,8 +256,8 @@
]
).bootstrap([])
-
-
+
+
# Create the develop-eggs dir, which didn't get created the usual
# way due to thr trick above:
os.mkdir('develop-eggs')
@@ -272,14 +285,15 @@
bdist_egg = bdist_egg,
start_server = start_server,
buildout = os.path.join(sample, 'bin', 'buildout'),
+ wait_until = wait_until,
))
-
+
zc.buildout.easy_install.prefer_final(prefer_final)
def buildoutTearDown(test):
for f in test.globs['__tear_downs']:
f()
-
+
class Server(BaseHTTPServer.HTTPServer):
def __init__(self, tree, *args):
@@ -307,12 +321,12 @@
def do_GET(self):
if '__stop__' in self.path:
raise SystemExit
-
+
if self.path == '/enable_server_logging':
self.__server.__log = True
self.send_response(200)
return
-
+
if self.path == '/disable_server_logging':
self.__server.__log = False
self.send_response(200)
@@ -361,7 +375,7 @@
self.end_headers()
self.wfile.write(out)
-
+
def log_request(self, code):
if self.__server.__log:
print '%s %s %s' % (self.command, code, self.path)
@@ -462,7 +476,7 @@
if path.startswith('\\'):
path = path[1:]
return '/' + path.replace(os.path.sep, '/')
-
+
normalize_path = (
re.compile(
r'''[^'" \t\n\r]+\%(sep)s_[Tt][Ee][Ss][Tt]_\%(sep)s([^"' \t\n\r]+)'''
More information about the Checkins
mailing list