[Checkins] SVN: zc.buildout/branches/use_distribute/ merge changes from r102547 to HEAD of trunk.
Chris Withers
chris at simplistix.co.uk
Tue Sep 8 06:52:14 EDT 2009
Log message for revision 103642:
merge changes from r102547 to HEAD of trunk.
Changed:
U zc.buildout/branches/use_distribute/CHANGES.txt
A zc.buildout/branches/use_distribute/DEVELOPERS.txt
U zc.buildout/branches/use_distribute/README.txt
U zc.buildout/branches/use_distribute/setup.py
U zc.buildout/branches/use_distribute/src/zc/buildout/buildout.py
U zc.buildout/branches/use_distribute/src/zc/buildout/buildout.txt
U zc.buildout/branches/use_distribute/src/zc/buildout/download.py
U zc.buildout/branches/use_distribute/src/zc/buildout/download.txt
U zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.py
U zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.txt
U zc.buildout/branches/use_distribute/src/zc/buildout/extends-cache.txt
U zc.buildout/branches/use_distribute/src/zc/buildout/testing.py
A zc.buildout/branches/use_distribute/src/zc/buildout/testrecipes.py
U zc.buildout/branches/use_distribute/src/zc/buildout/tests.py
U zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/README.txt
U zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt
-=-
Modified: zc.buildout/branches/use_distribute/CHANGES.txt
===================================================================
--- zc.buildout/branches/use_distribute/CHANGES.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/CHANGES.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -1,9 +1,49 @@
Change History
**************
-1.4.0 (unreleased)
+1.?.? (2008-0?-??)
==================
+Bugs fixed:
+
+- Incrementing didn't work properly when extending multiple files.
+ https://bugs.launchpad.net/zc.buildout/+bug/421022
+
+1.4.1 (2009-08-27)
+==================
+
+New Feature:
+
+- Added a debug built-in recipe to make writing some tests easier.
+
+Bugs fixed:
+
+- (introduced in 1.4.0) option incrementing (-=) and decrementing (-=)
+ didn't work in the buildout section.
+ https://bugs.launchpad.net/zc.buildout/+bug/420463
+
+- Option incrementing and decrementing didn't work for options
+ specified on the command line.
+
+- Scripts generated with relative-paths enabled couldn't be
+ symbolically linked to other locations and still work.
+
+- Scripts run using generated interpreters didn't have __file__ set correctly.
+
+- The standard Python -m option didn't work for custom interpreters.
+
+1.4.0 (2009-08-26)
+==================
+
+- When doing variable substitutions, you can omit the section name to
+ refer to a variable in the same section (e.g. ${:foo}).
+
+- When doing variable substitution, you can use the special option,
+ ``_buildout_section_name_`` to get the section name. This is most handy
+ for getting the current section name (e.g. ${:_buildout_section_name_}).
+
+- A new special option, ``<`` allows sections to be used as macros.
+
- Added annotate command for annotated sections. Displays sections
key-value pairs along with the value origin.
@@ -13,6 +53,11 @@
- Used the download API to allow caching of base configurations (specified by
the buildout section's 'extends' option).
+1.3.1 (2009-08-12)
+==================
+
+- Bug fixed: extras were ignored in some cases when versions were specified.
+
1.3.0 (2009-06-22)
==================
Copied: zc.buildout/branches/use_distribute/DEVELOPERS.txt (from rev 103641, zc.buildout/trunk/DEVELOPERS.txt)
===================================================================
--- zc.buildout/branches/use_distribute/DEVELOPERS.txt (rev 0)
+++ zc.buildout/branches/use_distribute/DEVELOPERS.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -0,0 +1,11 @@
+Developing buildout itself
+**************************
+
+When you're developing buildout itself, you need to know two things:
+
+- Use a clean python *without* setuptools installed. Otherwise many tests
+ will find your already-installed setuptools, leading to test differences
+ when setuptools' presence is explicitly tested.
+
+- Don't bootstrap with ``python bootstrap/bootstrap.py`` but with ``python
+ dev.py``.
Modified: zc.buildout/branches/use_distribute/README.txt
===================================================================
--- zc.buildout/branches/use_distribute/README.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/README.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -123,7 +123,7 @@
This is probably the most common type of buildout.
If I need to run a previous version of zc.buildout, I use the
-`--version` option of the buildout.py script::
+`--version` option of the bootstrap.py script::
$ python bootstrap.py --version 1.1.3
Modified: zc.buildout/branches/use_distribute/setup.py
===================================================================
--- zc.buildout/branches/use_distribute/setup.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/setup.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -11,11 +11,11 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-
+name = "zc.buildout"
version = "1.4.0-distribute-dev"
import os
-from setuptools import setup, find_packages
+from setuptools import setup
def read(*rnames):
return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
@@ -54,9 +54,15 @@
'**********************\n'
)
-open('doc.txt', 'w').write(long_description)
+entry_points = """
+[console_scripts]
+buildout = %(name)s.buildout:main
-name = "zc.buildout"
+[zc.buildout]
+debug = %(name)s.testrecipes:Debug
+
+""" % dict(name=name)
+
setup(
name = name,
version = version,
@@ -66,7 +72,7 @@
long_description=long_description,
license = "ZPL 2.1",
keywords = "development build",
- url='http://www.python.org/pypi/zc.buildout',
+ url='http://pypi.python.org/pypi/zc.buildout',
data_files = [('.', ['README.txt'])],
packages = ['zc', 'zc.buildout'],
@@ -74,8 +80,7 @@
namespace_packages = ['zc'],
install_requires = 'distribute',
include_package_data = True,
- entry_points = {'console_scripts':
- ['buildout = %s.buildout:main' % name]},
+ entry_points = entry_points,
zip_safe=False,
classifiers = [
'Intended Audience :: Developers',
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/buildout.py
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/buildout.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/buildout.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -12,38 +12,33 @@
#
##############################################################################
"""Buildout main script
-
-$Id$
"""
+from rmtree import rmtree
+try:
+ from hashlib import md5
+except ImportError:
+ # Python 2.4 and older
+ from md5 import md5
+
+import ConfigParser
+import copy
import distutils.errors
+import glob
+import itertools
import logging
import os
-import pprint
+import pkg_resources
import re
import shutil
-import cStringIO
import sys
import tempfile
-import ConfigParser
import UserDict
-import glob
-import copy
-
-
-import pkg_resources
import zc.buildout
import zc.buildout.download
import zc.buildout.easy_install
-from rmtree import rmtree
-try:
- from hashlib import md5
-except ImportError:
- # Python 2.4 and older
- from md5 import md5
-
realpath = zc.buildout.easy_install.realpath
pkg_resources_loc = pkg_resources.working_set.find(
@@ -164,10 +159,15 @@
else:
base = None
- override = dict((option, (value, 'COMMAND_LINE_VALUE'))
- for section, option, value in cloptions
- if section == 'buildout')
+ cloptions = dict(
+ (section, dict((option, (value, 'COMMAND_LINE_VALUE'))
+ for (_, option, value) in v))
+ for (section, v) in itertools.groupby(sorted(cloptions),
+ lambda v: v[0])
+ )
+ override = cloptions.get('buildout', {}).copy()
+
# load user defaults, which override defaults
if user_defaults:
user_config = os.path.join(os.path.expanduser('~'),
@@ -182,11 +182,7 @@
data['buildout'].copy(), override))
# apply command-line options
- for (section, option, value) in cloptions:
- options = data.get(section)
- if options is None:
- options = data[section] = {}
- options[option] = value, "COMMAND_LINE_VALUE"
+ _update(data, cloptions)
self._annotated = copy.deepcopy(data)
self._raw = _unannotate(data)
@@ -929,10 +925,13 @@
arg_list = list()
for a in args:
- add_args.append(zc.buildout.easy_install._safe_arg(a))
+ arg_list.append(zc.buildout.easy_install._safe_arg(a))
- subprocess.Popen([zc.buildout.easy_install._safe_arg(sys.executable)] + list(tsetup) +
- arg_list).wait()
+ subprocess.Popen(
+ [zc.buildout.easy_install._safe_arg(sys.executable)]
+ + list(tsetup)
+ + arg_list
+ ).wait()
else:
os.spawnl(os.P_WAIT, sys.executable, zc.buildout.easy_install._safe_arg (sys.executable), tsetup,
@@ -1028,6 +1027,9 @@
name = self.name
__doing__ = 'Initializing section %s.', name
+ if '<' in self._raw:
+ self._raw = self._do_extend_raw(name, self._raw, [])
+
# force substitutions
for k, v in self._raw.items():
if '${' in v:
@@ -1048,6 +1050,33 @@
self.recipe = recipe_class(buildout, name, self)
buildout._parts.append(name)
+ def _do_extend_raw(self, name, data, doing):
+ if name == 'buildout':
+ return data
+ if name in doing:
+ raise zc.buildout.UserError("Infinite extending loop %r" % name)
+ doing.append(name)
+ try:
+ to_do = data.pop('<', None)
+ if to_do is None:
+ return data
+ __doing__ = 'Loading input sections for %r', name
+
+ result = {}
+ for iname in to_do.split('\n'):
+ iname = iname.strip()
+ if not iname:
+ continue
+ raw = self.buildout._raw.get(iname)
+ if raw is None:
+ raise zc.buildout.UserError("No section named %r" % iname)
+ result.update(self._do_extend_raw(iname, raw, doing))
+
+ result.update(data)
+ return result
+ finally:
+ assert doing.pop() == name
+
def _dosub(self, option, v):
__doing__ = 'Getting option %s:%s.', self.name, option
seen = [(self.name, option)]
@@ -1086,7 +1115,7 @@
_template_split = re.compile('([$]{[^}]*})').split
_simple = re.compile('[-a-zA-Z0-9 ._]+$').match
- _valid = re.compile('\${[-a-zA-Z0-9 ._]+:[-a-zA-Z0-9 ._]+}$').match
+ _valid = re.compile('\${[-a-zA-Z0-9 ._]*:[-a-zA-Z0-9 ._]+}$').match
def _sub(self, template, seen):
value = self._template_split(template)
subs = []
@@ -1112,9 +1141,16 @@
"has invalid characters."
% ref)
- v = self.buildout[s[0]].get(s[1], None, seen)
+ section, option = s
+ if not section:
+ section = self.name
+ v = self.buildout[section].get(option, None, seen)
if v is None:
- raise MissingOption("Referenced option does not exist:", *s)
+ if option == '_buildout_section_name_':
+ v = self.name
+ else:
+ raise MissingOption("Referenced option does not exist:",
+ section, option)
subs.append(v)
subs.append('')
@@ -1172,7 +1208,7 @@
elif os.path.isfile(p):
os.remove(p)
else:
- self._buildout._logger.warn("Couldn't clean up %r.", p)
+ self.buildout._logger.warn("Couldn't clean up %r.", p)
raise
finally:
self._created = None
@@ -1238,11 +1274,13 @@
"""
_update_section(dl_options, override)
_dl_options = _unannotate_section(dl_options.copy())
+ is_temp = False
download = zc.buildout.download.Download(
_dl_options, cache=_dl_options.get('extends-cache'), fallback=True,
hash_name=True)
if _isurl(filename):
- fp = open(download(filename))
+ path, is_temp = download(filename)
+ fp = open(path)
base = filename[:filename.rfind('/')]
elif _isurl(base):
if os.path.isabs(filename):
@@ -1250,7 +1288,8 @@
base = os.path.dirname(filename)
else:
filename = base + '/' + filename
- fp = open(download(filename))
+ path, is_temp = download(filename)
+ fp = open(path)
base = filename[:filename.rfind('/')]
else:
filename = os.path.join(base, filename)
@@ -1258,6 +1297,9 @@
base = os.path.dirname(filename)
if filename in seen:
+ if is_temp:
+ fp.close()
+ os.remove(path)
raise zc.buildout.UserError("Recursive file include", seen, filename)
root_config_file = not seen
@@ -1268,6 +1310,10 @@
parser = ConfigParser.RawConfigParser()
parser.optionxform = lambda s: s
parser.readfp(fp)
+ if is_temp:
+ fp.close()
+ os.remove(path)
+
extends = extended_by = None
for section in parser.sections():
options = dict(parser.items(section))
@@ -1283,10 +1329,10 @@
if extends:
extends = extends.split()
- extends.reverse()
+ eresult = _open(base, extends.pop(0), seen, dl_options, override)
for fname in extends:
- result = _update(_open(base, fname, seen, dl_options, override),
- result)
+ _update(eresult, _open(base, fname, seen, dl_options, override))
+ result = _update(eresult, result)
if extended_by:
self._logger.warn(
@@ -1326,6 +1372,7 @@
return result
def _update_section(s1, s2):
+ s2 = s2.copy() # avoid mutating the second argument, which is unexpected
for k, v in s2.items():
v2, note2 = v
if k.endswith('+'):
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/buildout.txt
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/buildout.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/buildout.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -866,7 +866,38 @@
contain alphanumeric characters, hyphens, periods and spaces. This
restriction might be relaxed in future releases.
+We can ommit the section name in a variable substitution to refer to
+the current section. We can also use the special option,
+_buildout_section_name_ to get the current section name.
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = recipes
+ ... parts = data-dir debug
+ ... log-level = INFO
+ ...
+ ... [debug]
+ ... recipe = recipes:debug
+ ... File 1 = ${data-dir:path}/file
+ ... File 2 = ${:File 1}/log
+ ... my_name = ${:_buildout_section_name_}
+ ...
+ ... [data-dir]
+ ... recipe = recipes:mkdir
+ ... path = mydata
+ ... """)
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling debug.
+ Updating data-dir.
+ Installing debug.
+ File 1 /sample-buildout/mydata/file
+ File 2 /sample-buildout/mydata/file/log
+ my_name debug
+ recipe recipes:debug
+
Automatic part selection and ordering
-------------------------------------
@@ -897,8 +928,9 @@
>>> print system(buildout),
Develop: '/sample-buildout/recipes'
+ Uninstalling debug.
Updating data-dir.
- Updating debug.
+ Installing debug.
File 1 /sample-buildout/mydata/file
File 2 /sample-buildout/mydata/file/log
recipe recipes:debug
@@ -947,7 +979,56 @@
parts = data-dir debug
...
+Extending sections (macros)
+---------------------------
+A section (other than the buildout section) can extend one or more
+other sections using the ``<`` option. Options from the referenced
+sections are copied to the refering section *before* variable
+substitution. This, together with the ability to refer to variables
+of the current section allows sections to be used as macros.
+
+ >>> write(sample_buildout, 'buildout.cfg',
+ ... """
+ ... [buildout]
+ ... develop = recipes
+ ... parts = myfiles
+ ... log-level = INFO
+ ...
+ ... [debug]
+ ... recipe = recipes:debug
+ ...
+ ... [with_file1]
+ ... <= debug
+ ... file1 = ${:path}/file1
+ ... color = red
+ ...
+ ... [with_file2]
+ ... <= debug
+ ... file2 = ${:path}/file2
+ ... color = blue
+ ...
+ ... [myfiles]
+ ... <= with_file1
+ ... with_file2
+ ... path = mydata
+ ... """)
+
+ >>> print system(buildout),
+ Develop: '/sample-buildout/recipes'
+ Uninstalling debug.
+ Uninstalling data-dir.
+ Installing myfiles.
+ color blue
+ file1 mydata/file1
+ file2 mydata/file2
+ path mydata
+ recipe recipes:debug
+
+In this example, the debug, with_file1 and with_file2 sections act as
+macros. In particular, the variable substitutions are performed
+relative to the myfiles section.
+
Adding and removing options
---------------------------
@@ -1051,11 +1132,10 @@
>>> os.chdir(sample_buildout)
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
Develop: '/sample-buildout/demo'
- Uninstalling debug.
+ Uninstalling myfiles.
Getting distribution for 'recipes'.
zip_safe flag not set; analyzing archive contents...
Got recipes 0.0.0.
- Uninstalling data-dir.
warning: install_lib: 'build/lib' does not exist -- no Python modules to install
Verify option values.
@@ -1075,7 +1155,8 @@
Annotated sections output shows which files are responsible for which
operations.
- >>> print system(os.path.join('bin', 'buildout') + ' annotate'), # doctest: +ELLIPSIS
+ >>> print system(os.path.join('bin', 'buildout') + ' annotate'),
+ ... # doctest: +ELLIPSIS
<BLANKLINE>
Annotated sections
==================
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/download.py
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/download.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/download.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -21,6 +21,7 @@
import logging
import os
import os.path
+import re
import shutil
import tempfile
import urllib
@@ -56,6 +57,7 @@
def __init__(self, options={}, cache=-1, namespace=None,
offline=-1, fallback=False, hash_name=False, logger=None):
+ self.directory = options.get('directory', '')
self.cache = cache
if cache == -1:
self.cache = options.get('download-cache')
@@ -69,10 +71,15 @@
self.logger = logger or logging.getLogger('zc.buildout')
@property
- def cache_dir(self):
+ def download_cache(self):
if self.cache is not None:
- return os.path.join(realpath(self.cache), self.namespace or '')
+ return realpath(os.path.join(self.directory, self.cache))
+ @property
+ def cache_dir(self):
+ if self.download_cache is not None:
+ return os.path.join(self.download_cache, self.namespace or '')
+
def __call__(self, url, md5sum=None, path=None):
"""Download a file according to the utility's configuration.
@@ -84,11 +91,11 @@
"""
if self.cache:
- local_path = self.download_cached(url, md5sum)
+ local_path, is_temp = self.download_cached(url, md5sum)
else:
- local_path = self.download(url, md5sum, path)
+ local_path, is_temp = self.download(url, md5sum, path)
- return locate_at(local_path, path)
+ return locate_at(local_path, path), is_temp
def download_cached(self, url, md5sum=None):
"""Download a file from a URL using the cache.
@@ -98,17 +105,24 @@
but will not remove the copy in that case.
"""
+ if not os.path.exists(self.download_cache):
+ raise zc.buildout.UserError(
+ 'The directory:\n'
+ '%r\n'
+ "to be used as a download cache doesn't exist.\n"
+ % self.download_cache)
cache_dir = self.cache_dir
if not os.path.exists(cache_dir):
- os.makedirs(cache_dir)
+ os.mkdir(cache_dir)
cache_key = self.filename(url)
cached_path = os.path.join(cache_dir, cache_key)
self.logger.debug('Searching cache at %s' % cache_dir)
if os.path.isfile(cached_path):
+ is_temp = False
if self.fallback:
try:
- self.download(url, md5sum, cached_path)
+ _, is_temp = self.download(url, md5sum, cached_path)
except ChecksumError:
raise
except Exception:
@@ -122,9 +136,9 @@
else:
self.logger.debug('Cache miss; will cache %s as %s' %
(url, cached_path))
- self.download(url, md5sum, cached_path)
+ _, is_temp = self.download(url, md5sum, cached_path)
- return cached_path
+ return cached_path, is_temp
def download(self, url, md5sum=None, path=None):
"""Download a file from a URL to a given or temporary path.
@@ -135,6 +149,8 @@
returned and the client code is responsible for cleaning it up.
"""
+ if re.match(r"^[A-Za-z]:\\", url):
+ url = 'file:' + url
parsed_url = urlparse.urlparse(url, 'file')
url_scheme, _, url_path = parsed_url[:3]
if url_scheme == 'file':
@@ -143,7 +159,7 @@
raise ChecksumError(
'MD5 checksum mismatch for local resource at %r.' %
url_path)
- return locate_at(url_path, path)
+ return locate_at(url_path, path), False
if self.offline:
raise zc.buildout.UserError(
@@ -152,18 +168,23 @@
self.logger.info('Downloading %s' % url)
urllib._urlopener = url_opener
handle, tmp_path = tempfile.mkstemp(prefix='buildout-')
- tmp_path, headers = urllib.urlretrieve(url, tmp_path)
- os.close(handle)
- if not check_md5sum(tmp_path, md5sum):
+ try:
+ try:
+ tmp_path, headers = urllib.urlretrieve(url, tmp_path)
+ if not check_md5sum(tmp_path, md5sum):
+ raise ChecksumError(
+ 'MD5 checksum mismatch downloading %r' % url)
+ finally:
+ os.close(handle)
+ except:
os.remove(tmp_path)
- raise ChecksumError(
- 'MD5 checksum mismatch downloading %r' % url)
+ raise
if path:
shutil.move(tmp_path, path)
- return path
+ return path, False
else:
- return tmp_path
+ return tmp_path, True
def filename(self, url):
"""Determine a file name from a URL according to the configuration.
@@ -172,16 +193,27 @@
if self.hash_name:
return md5(url).hexdigest()
else:
- parsed = urlparse.urlparse(url)
+ if re.match(r"^[A-Za-z]:\\", url):
+ url = 'file:' + url
+ parsed = urlparse.urlparse(url, 'file')
url_path = parsed[2]
- for name in reversed(url_path.split('/')):
- if name:
- return name
+
+ if parsed[0] == 'file':
+ while True:
+ url_path, name = os.path.split(url_path)
+ if name:
+ return name
+ if not url_path:
+ break
else:
- url_host, url_port = parsed[-2:]
- return '%s:%s' % (url_host, url_port)
+ for name in reversed(url_path.split('/')):
+ if name:
+ return name
+ url_host, url_port = parsed[-2:]
+ return '%s:%s' % (url_host, url_port)
+
def check_md5sum(path, md5sum):
"""Tell whether the MD5 checksum of the file at path matches.
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/download.txt
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/download.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/download.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -12,7 +12,14 @@
>>> write(server_data, 'foo.txt', 'This is a foo text.')
>>> server_url = start_server(server_data)
+We also use a fresh directory for temporary files in order to make sure that
+all temporary files have been cleaned up in the end:
+>>> import tempfile
+>>> old_tempdir = tempfile.tempdir
+>>> tempfile.tempdir = tmpdir('tmp')
+
+
Downloading without using the cache
-----------------------------------
@@ -25,9 +32,11 @@
None
Downloading a file is achieved by calling the utility with the URL as an
-argument:
+argument. A tuple is returned that consists of the path to the downloaded copy
+of the file and a boolean value indicating whether this is a temporary file
+meant to be cleaned up during the same buildout run:
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/.../buildout-...
>>> cat(path)
@@ -36,50 +45,69 @@
As we aren't using the download cache and haven't specified a target path
either, the download has ended up in a temporary file:
+>>> is_temp
+True
+
>>> import tempfile
>>> path.startswith(tempfile.gettempdir())
True
+We are responsible for cleaning up temporary files behind us:
+
+>>> remove(path)
+
When trying to access a file that doesn't exist, we'll get an exception:
->>> download(server_url+'not-there')
-Traceback (most recent call last):
-IOError: ('http error', 404, 'Not Found',
- <httplib.HTTPMessage instance at 0xa0ffd2c>)
+>>> try: download(server_url+'not-there') # doctest: +ELLIPSIS
+... except: print 'download error'
+... else: print 'woops'
+download error
+Downloading a local file doesn't produce a temporary file but simply returns
+the local file itself:
+
+>>> download(join(server_data, 'foo.txt'))
+('/sample_files/foo.txt', False)
+
We can also have the downloaded file's MD5 sum checked:
>>> try: from hashlib import md5
... except ImportError: from md5 import new as md5
->>> path = download(server_url+'foo.txt',
-... md5('This is a foo text.').hexdigest())
+>>> path, is_temp = download(server_url+'foo.txt',
+... md5('This is a foo text.').hexdigest())
+>>> is_temp
+True
+>>> remove(path)
->>> path = download(server_url+'foo.txt',
-... md5('The wrong text.').hexdigest())
+>>> download(server_url+'foo.txt',
+... md5('The wrong text.').hexdigest())
Traceback (most recent call last):
ChecksumError: MD5 checksum mismatch downloading 'http://localhost/foo.txt'
The error message in the event of an MD5 checksum mismatch for a local file
reads somewhat differently:
->>> path = download(join(server_data, 'foo.txt'),
-... md5('This is a foo text.').hexdigest())
+>>> download(join(server_data, 'foo.txt'),
+... md5('This is a foo text.').hexdigest())
+('/sample_files/foo.txt', False)
->>> path = download(join(server_data, 'foo.txt'),
-... md5('The wrong text.').hexdigest())
+>>> download(join(server_data, 'foo.txt'),
+... md5('The wrong text.').hexdigest())
Traceback (most recent call last):
ChecksumError: MD5 checksum mismatch for local resource at '/sample_files/foo.txt'.
Finally, we can download the file to a specified place in the file system:
>>> target_dir = tmpdir('download-target')
->>> path = download(server_url+'foo.txt',
-... path=join(target_dir, 'downloaded.txt'))
+>>> path, is_temp = download(server_url+'foo.txt',
+... path=join(target_dir, 'downloaded.txt'))
>>> print path
/download-target/downloaded.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
Trying to download a file in offline mode will result in an error:
@@ -91,13 +119,14 @@
As an exception to this rule, file system paths and URLs in the ``file``
scheme will still work:
->>> cat(download(join(server_data, 'foo.txt')))
+>>> cat(download(join(server_data, 'foo.txt'))[0])
This is a foo text.
->>> cat(download('file://%s/foo.txt' % server_data))
+>>> cat(download('file:' + join(server_data, 'foo.txt'))[0])
This is a foo text.
>>> remove(path)
+
Downloading using the download cache
------------------------------------
@@ -118,17 +147,19 @@
to the cached copy:
>>> ls(cache)
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/download-cache/foo.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
Whenever the file is downloaded again, the cached copy is used. Let's change
the file on the server to see this:
>>> write(server_data, 'foo.txt', 'The wrong text.')
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/download-cache/foo.txt
>>> cat(path)
@@ -137,7 +168,7 @@
If we specify an MD5 checksum for a file that is already in the cache, the
cached copy's checksum will be verified:
->>> path = download(server_url+'foo.txt', md5('The wrong text.').hexdigest())
+>>> download(server_url+'foo.txt', md5('The wrong text.').hexdigest())
Traceback (most recent call last):
ChecksumError: MD5 checksum mismatch for cached download
from 'http://localhost/foo.txt' at '/download-cache/foo.txt'
@@ -147,7 +178,7 @@
>>> mkdir(server_data, 'other')
>>> write(server_data, 'other', 'foo.txt', 'The wrong text.')
->>> path = download(server_url+'other/foo.txt')
+>>> path, is_temp = download(server_url+'other/foo.txt')
>>> print path
/download-cache/foo.txt
>>> cat(path)
@@ -161,30 +192,34 @@
>>> ls(cache)
>>> write(server_data, 'foo.txt', 'This is a foo text.')
->>> path = download(server_url+'foo.txt',
-... path=join(target_dir, 'downloaded.txt'))
+>>> path, is_temp = download(server_url+'foo.txt',
+... path=join(target_dir, 'downloaded.txt'))
>>> print path
/download-target/downloaded.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
>>> ls(cache)
- foo.txt
>>> remove(path)
>>> write(server_data, 'foo.txt', 'The wrong text.')
->>> path = download(server_url+'foo.txt',
-... path=join(target_dir, 'downloaded.txt'))
+>>> path, is_temp = download(server_url+'foo.txt',
+... path=join(target_dir, 'downloaded.txt'))
>>> print path
/download-target/downloaded.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
In offline mode, downloads from any URL will be successful if the file is
found in the cache:
>>> download = Download(cache=cache, offline=True)
->>> cat(download(server_url+'foo.txt'))
+>>> cat(download(server_url+'foo.txt')[0])
This is a foo text.
Local resources will be cached just like any others since download caches are
@@ -196,14 +231,14 @@
>>> write(server_data, 'foo.txt', 'This is a foo text.')
>>> download = Download(cache=cache)
->>> cat(download('file://' + join(server_data, 'foo.txt'), path=path))
+>>> cat(download('file:' + join(server_data, 'foo.txt'), path=path)[0])
This is a foo text.
>>> ls(cache)
- foo.txt
>>> remove(cache, 'foo.txt')
->>> cat(download(join(server_data, 'foo.txt'), path=path))
+>>> cat(download(join(server_data, 'foo.txt'), path=path)[0])
This is a foo text.
>>> ls(cache)
- foo.txt
@@ -219,6 +254,15 @@
>>> remove(path)
+Finally, let's see what happens if the download cache to be used doesn't exist
+as a directory in the file system yet:
+
+>>> Download(cache=join(cache, 'non-existent'))(server_url+'foo.txt')
+Traceback (most recent call last):
+UserError: The directory:
+'/download-cache/non-existent'
+to be used as a download cache doesn't exist.
+
Using namespace sub-directories of the download cache
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -240,7 +284,7 @@
Downloading a file now creates the namespace sub-directory and places a copy
of the file inside it:
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/download-cache/test/foo.txt
>>> ls(cache)
@@ -249,6 +293,8 @@
- foo.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
The next time we want to download that file, the copy from inside the cache
namespace is used. To see this clearly, we put a file with the same name but
@@ -257,7 +303,7 @@
>>> write(server_data, 'foo.txt', 'The wrong text.')
>>> write(cache, 'foo.txt', 'The wrong text.')
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/download-cache/test/foo.txt
>>> cat(path)
@@ -278,7 +324,7 @@
be used as the filename in the cache:
>>> download = Download(cache=cache, hash_name=True)
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> print path
/download-cache/09f5793fcdc1716727f72d49519c688d
>>> cat(path)
@@ -291,13 +337,13 @@
the test is run, so we don't actually know the full URL of the file. Let's
check that the checksum actually belongs to the particular URL used:
->>> path == join(cache, md5(server_url+'foo.txt').hexdigest())
+>>> path.lower() == join(cache, md5(server_url+'foo.txt').hexdigest()).lower()
True
The cached copy is used when downloading the file again:
>>> write(server_data, 'foo.txt', 'The wrong text.')
->>> path == download(server_url+'foo.txt')
+>>> (path, is_temp) == download(server_url+'foo.txt')
True
>>> cat(path)
This is a foo text.
@@ -308,12 +354,12 @@
file the same, the file will be downloaded again this time and put in the
cache under a different name:
->>> path2 = download(server_url+'other/foo.txt')
+>>> path2, is_temp = download(server_url+'other/foo.txt')
>>> print path2
/download-cache/537b6d73267f8f4447586989af8c470e
>>> path == path2
False
->>> path2 == join(cache, md5(server_url+'other/foo.txt').hexdigest())
+>>> path2.lower() == join(cache, md5(server_url+'other/foo.txt').hexdigest()).lower()
True
>>> cat(path)
This is a foo text.
@@ -343,40 +389,48 @@
A downloaded file will be cached:
>>> ls(cache)
->>> path = download(server_url+'foo.txt')
+>>> path, is_temp = download(server_url+'foo.txt')
>>> ls(cache)
- foo.txt
>>> cat(cache, 'foo.txt')
This is a foo text.
+>>> is_temp
+False
If the file cannot be served, the cached copy will be used:
>>> remove(server_data, 'foo.txt')
->>> Download()(server_url+'foo.txt')
-Traceback (most recent call last):
-IOError: ('http error', 404, 'Not Found',
- <httplib.HTTPMessage instance at 0xa35d36c>)
->>> path = download(server_url+'foo.txt')
+>>> try: Download()(server_url+'foo.txt') # doctest: +ELLIPSIS
+... except: print 'download error'
+... else: print 'woops'
+download error
+>>> path, is_temp = download(server_url+'foo.txt')
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
Similarly, if the file is served but we're in offline mode, we'll fall back to
using the cache:
>>> write(server_data, 'foo.txt', 'The wrong text.')
->>> cat(Download()(server_url+'foo.txt'))
-The wrong text.
+>>> get(server_url+'foo.txt')
+'The wrong text.'
+
>>> offline_download = Download(cache=cache, offline=True, fallback=True)
->>> path = offline_download(server_url+'foo.txt')
+>>> path, is_temp = offline_download(server_url+'foo.txt')
+>>> print path
+/download-cache/foo.txt
>>> cat(path)
This is a foo text.
+>>> is_temp
+False
However, when downloading the file normally with the cache being used in
fall-back mode, the file will be downloaded from the net and the cached copy
will be replaced with the new content:
->>> path = download(server_url+'foo.txt')
->>> cat(path)
+>>> cat(download(server_url+'foo.txt')[0])
The wrong text.
>>> cat(cache, 'foo.txt')
The wrong text.
@@ -408,6 +462,19 @@
>>> print download.cache_dir
/download-cache/cmmi
+If the ``download-cache`` option specifies a relative path, it is understood
+relative to the current working directory, or to the buildout directory if
+that is given:
+
+>>> download = Download({'download-cache': 'relative-cache'})
+>>> print download.cache_dir
+/sample-buildout/relative-cache/
+
+>>> download = Download({'directory': join(sample_buildout, 'root'),
+... 'download-cache': 'relative-cache'})
+>>> print download.cache_dir
+/sample-buildout/root/relative-cache/
+
Keyword parameters take precedence over the corresponding options:
>>> download = Download({'download-cache': cache}, cache=None)
@@ -454,3 +521,15 @@
>>> download = Download({'install-from-cache': 'false'}, offline=True)
>>> download.offline
True
+
+
+Clean up
+--------
+
+We should have cleaned up all temporary files created by downloading things:
+
+>>> ls(tempfile.tempdir)
+
+Reset the global temporary directory:
+
+>>> tempfile.tempdir = old_tempdir
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -16,8 +16,6 @@
This module provides a high-level Python API for installing packages.
It doesn't install scripts. It uses distribute and requires it to be
installed.
-
-$Id$
"""
import distutils.errors
@@ -34,7 +32,6 @@
import subprocess
import sys
import tempfile
-import urlparse
import zc.buildout
import zipimport
@@ -55,7 +52,6 @@
is_jython = sys.platform.startswith('java')
if is_jython:
- import subprocess
import java.lang.System
jython_os_name = (java.lang.System.getProperties()['os.name']).lower()
@@ -621,7 +617,9 @@
raise IncompatibleVersionError("Bad version", version)
requirement = pkg_resources.Requirement.parse(
- "%s ==%s" % (requirement.project_name, version))
+ "%s[%s] ==%s" % (requirement.project_name,
+ ','.join(requirement.extras),
+ version))
return requirement
@@ -1023,7 +1021,7 @@
import os
join = os.path.join
-base = os.path.dirname(os.path.abspath(__file__))
+base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
"""
def _script(module_name, attrs, path, dest, executable, arguments,
@@ -1129,22 +1127,28 @@
_interactive = True
if len(sys.argv) > 1:
- import getopt
- _options, _args = getopt.getopt(sys.argv[1:], 'ic:')
+ _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
+ elif _opt == '-m':
+ sys.argv[1:] = _args
+ _args = []
+ __import__("runpy").run_module(
+ _val, {}, "__main__", alter_sys=True)
if _args:
sys.argv[:] = _args
- execfile(sys.argv[0])
+ __file__ = _args[0]
+ del _options, _args
+ execfile(__file__)
if _interactive:
- import code
- code.interact(banner="", local=globals())
+ del _interactive
+ __import__("code").interact(banner="", local=globals())
'''
runsetup_template = """
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.txt
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/easy_install.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -663,34 +663,59 @@
>>> cat(bin, 'py') # doctest: +NORMALIZE_WHITESPACE
#!/usr/local/bin/python2.4
+ <BLANKLINE>
import sys
<BLANKLINE>
sys.path[0:0] = [
- '/sample-install/demo-0.3-py2.4.egg',
- '/sample-install/demoneeded-1.1-py2.4.egg',
+ '/sample-install/demo-0.3-pyN.N.egg',
+ '/sample-install/demoneeded-1.1-pyN.N.egg',
]
<BLANKLINE>
_interactive = True
if len(sys.argv) > 1:
- import getopt
- _options, _args = getopt.getopt(sys.argv[1:], 'ic:')
+ _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
+ elif _opt == '-m':
+ sys.argv[1:] = _args
+ _args = []
+ __import__("runpy").run_module(
+ _val, {}, "__main__", alter_sys=True)
<BLANKLINE>
if _args:
sys.argv[:] = _args
- execfile(sys.argv[0])
+ __file__ = _args[0]
+ del _options, _args
+ execfile(__file__)
<BLANKLINE>
if _interactive:
- import code
- code.interact(banner="", local=globals())
+ del _interactive
+ __import__("code").interact(banner="", local=globals())
If invoked with a script name and arguments, it will run that script, instead.
+ >>> write('ascript', '''
+ ... "demo doc"
+ ... print sys.argv
+ ... print (__name__, __file__, __doc__)
+ ... ''')
+ >>> print system(join(bin, 'py')+' ascript a b c'),
+ ['ascript', 'a', 'b', 'c']
+ ('__main__', 'ascript', 'demo doc')
+
+For Python 2.5 and higher, you can also use the -m option to run a
+module:
+
+ >>> print system(join(bin, 'py')+' -m pdb'),
+ usage: pdb.py scriptfile [arg] ...
+
+ >>> print system(join(bin, 'py')+' -m pdb what'),
+ Error: what does not exist
+
An additional argument can be passed to define which scripts to install
and to provide script names. The argument is a dictionary mapping
original script names to new script names.
@@ -817,7 +842,7 @@
import os
<BLANKLINE>
join = os.path.join
- base = os.path.dirname(os.path.abspath(__file__))
+ base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
<BLANKLINE>
import sys
@@ -849,7 +874,7 @@
import os
<BLANKLINE>
join = os.path.join
- base = os.path.dirname(os.path.abspath(__file__))
+ base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
<BLANKLINE>
import sys
@@ -863,22 +888,28 @@
<BLANKLINE>
_interactive = True
if len(sys.argv) > 1:
- import getopt
- _options, _args = getopt.getopt(sys.argv[1:], 'ic:')
+ _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
+ elif _opt == '-m':
+ sys.argv[1:] = _args
+ _args = []
+ __import__("runpy").run_module(
+ _val, {}, "__main__", alter_sys=True)
<BLANKLINE>
if _args:
sys.argv[:] = _args
- execfile(sys.argv[0])
+ __file__ = _args[0]
+ del _options, _args
+ execfile(__file__)
<BLANKLINE>
if _interactive:
- import code
- code.interact(banner="", local=globals())
+ del _interactive
+ __import__("code").interact(banner="", local=globals())
Handling custom build options for extensions provided in source distributions
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/extends-cache.txt
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/extends-cache.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/extends-cache.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -13,7 +13,14 @@
>>> server_url = start_server(server_data)
>>> cd(sample_buildout)
+We also use a fresh directory for temporary files in order to make sure that
+all temporary files have been cleaned up in the end:
+>>> import tempfile
+>>> old_tempdir = tempfile.tempdir
+>>> tempfile.tempdir = tmpdir('tmp')
+
+
Basic use of the extends cache
------------------------------
@@ -59,6 +66,7 @@
running buildout and the base.cfg file will be put in it (with the file name
being a hash of the complete URL):
+>>> mkdir('cache')
>>> write('buildout.cfg', """\
... [buildout]
... extends = %sbase.cfg
@@ -159,6 +167,8 @@
>>> mkdir('home')
>>> mkdir('home', '.buildout')
+>>> mkdir('cache')
+>>> mkdir('user-cache')
>>> os.environ['HOME'] = join(sample_buildout, 'home')
>>> write('home', '.buildout', 'default.cfg', """\
... [buildout]
@@ -375,3 +385,15 @@
Checking for upgrades.
An internal error occured ...
ValueError: install_from_cache set to true with no download cache
+
+
+Clean up
+--------
+
+We should have cleaned up all temporary files created by downloading things:
+
+>>> ls(tempfile.tempdir)
+
+Reset the global temporary directory:
+
+>>> tempfile.tempdir = old_tempdir
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/testing.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/testing.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -121,7 +121,9 @@
here = os.getcwd()
try:
os.chdir(d)
- os.spawnle(os.P_WAIT, executable, zc.buildout.easy_install._safe_arg (executable), setup, *args)
+ os.spawnle(os.P_WAIT, executable,
+ zc.buildout.easy_install._safe_arg(executable),
+ setup, *args)
if os.path.exists('build'):
rmtree('build')
finally:
Copied: zc.buildout/branches/use_distribute/src/zc/buildout/testrecipes.py (from rev 103641, zc.buildout/trunk/src/zc/buildout/testrecipes.py)
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/testrecipes.py (rev 0)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/testrecipes.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -0,0 +1,16 @@
+
+class Debug:
+
+ def __init__(self, buildout, name, options):
+ self.buildout = buildout
+ self.name = name
+ self.options = options
+
+ def install(self):
+ items = self.options.items()
+ items.sort()
+ for option, value in items:
+ print " %s=%r" % (option, value)
+ return ()
+
+ update = install
Modified: zc.buildout/branches/use_distribute/src/zc/buildout/tests.py
===================================================================
--- zc.buildout/branches/use_distribute/src/zc/buildout/tests.py 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/src/zc/buildout/tests.py 2009-09-08 10:52:14 UTC (rev 103642)
@@ -11,17 +11,19 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""XXX short summary goes here.
-
-$Id$
-"""
-
-import os, re, shutil, sys, tempfile, unittest, zipfile
-from zope.testing import doctest, renormalizing
+from zope.testing import doctest
+from zope.testing import renormalizing
+import os
import pkg_resources
-import zc.buildout.testing, zc.buildout.easy_install
-
+import re
+import shutil
+import sys
+import tempfile
+import unittest
+import zc.buildout.easy_install
+import zc.buildout.testing
import zc.buildout.testselectingpython
+import zipfile
os_path_sep = os.path.sep
if os_path_sep == '\\':
@@ -2527,7 +2529,130 @@
"""
+def make_sure_versions_dont_cancel_extras():
+ """
+ There was a bug that caused extras in requirements to be lost.
+ >>> open('setup.py', 'w').write('''
+ ... from setuptools import setup
+ ... setup(name='extraversiondemo', version='1.0',
+ ... url='x', author='x', author_email='x',
+ ... extras_require=dict(foo=['demo']), py_modules=['t'])
+ ... ''')
+ >>> open('README', 'w').close()
+ >>> open('t.py', 'w').close()
+
+ >>> sdist('.', sample_eggs)
+ >>> mkdir('dest')
+ >>> ws = zc.buildout.easy_install.install(
+ ... ['extraversiondemo[foo]'], 'dest', links=[sample_eggs],
+ ... versions = dict(extraversiondemo='1.0')
+ ... )
+ >>> sorted(dist.key for dist in ws)
+ ['demo', 'demoneeded', 'extraversiondemo']
+ """
+
+def increment_buildout_options():
+ r"""
+ >>> write('b1.cfg', '''
+ ... [buildout]
+ ... parts = p1
+ ... x = 1
+ ... y = a
+ ... b
+ ...
+ ... [p1]
+ ... recipe = zc.buildout:debug
+ ... foo = ${buildout:x} ${buildout:y}
+ ... ''')
+
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... extends = b1.cfg
+ ... parts += p2
+ ... x += 2
+ ... y -= a
+ ...
+ ... [p2]
+ ... <= p1
+ ... ''')
+
+ >>> print system(buildout),
+ Installing p1.
+ foo='1\n2 b'
+ recipe='zc.buildout:debug'
+ Installing p2.
+ foo='1\n2 b'
+ recipe='zc.buildout:debug'
+ """
+
+def increment_buildout_with_multiple_extended_files_421022():
+ r"""
+ >>> write('foo.cfg', '''
+ ... [buildout]
+ ... foo-option = foo
+ ... [other]
+ ... foo-option = foo
+ ... ''')
+ >>> write('bar.cfg', '''
+ ... [buildout]
+ ... bar-option = bar
+ ... [other]
+ ... bar-option = bar
+ ... ''')
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... parts = p other
+ ... extends = bar.cfg foo.cfg
+ ... bar-option += baz
+ ... foo-option += ham
+ ...
+ ... [other]
+ ... recipe = zc.buildout:debug
+ ... bar-option += baz
+ ... foo-option += ham
+ ...
+ ... [p]
+ ... recipe = zc.buildout:debug
+ ... x = ${buildout:bar-option} ${buildout:foo-option}
+ ... ''')
+
+ >>> print system(buildout),
+ Installing p.
+ recipe='zc.buildout:debug'
+ x='bar\nbaz foo\nham'
+ Installing other.
+ bar-option='bar\nbaz'
+ foo-option='foo\nham'
+ recipe='zc.buildout:debug'
+ """
+
+def increment_on_command_line():
+ r"""
+ >>> write('buildout.cfg', '''
+ ... [buildout]
+ ... parts = p1
+ ... x = 1
+ ... y = a
+ ... b
+ ...
+ ... [p1]
+ ... recipe = zc.buildout:debug
+ ... foo = ${buildout:x} ${buildout:y}
+ ...
+ ... [p2]
+ ... <= p1
+ ... ''')
+
+ >>> print system(buildout+' buildout:parts+=p2 p1:foo+=bar'),
+ Installing p1.
+ foo='1 a\nb\nbar'
+ recipe='zc.buildout:debug'
+ Installing p2.
+ foo='1 a\nb\nbar'
+ recipe='zc.buildout:debug'
+ """
+
######################################################################
def create_sample_eggs(test, executable=sys.executable):
@@ -2809,7 +2934,11 @@
(re.compile('extdemo[.]pyd'), 'extdemo.so'),
(re.compile('[-d] distribute-\S+[.]egg'), 'distribute.egg'),
(re.compile(r'\\[\\]?'), '/'),
- ]),
+ ]+(sys.version_info < (2, 5) and [
+ (re.compile('.*No module named runpy.*', re.S), ''),
+ (re.compile('.*usage: pdb.py scriptfile .*', re.S), ''),
+ (re.compile('.*Error: what does not exist.*', re.S), ''),
+ ] or [])),
),
doctest.DocFileSuite(
@@ -2818,7 +2947,7 @@
tearDown=zc.buildout.testing.buildoutTearDown,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
checker=renormalizing.RENormalizing([
- (re.compile('0x[0-9a-f]+'), '<MEM ADDRESS>'),
+ (re.compile(' at -?0x[^>]+'), '<MEM ADDRESS>'),
(re.compile('http://localhost:[0-9]{4,5}/'),
'http://localhost/'),
(re.compile('[0-9a-f]{32}'), '<MD5 CHECKSUM>'),
Modified: zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/README.txt
===================================================================
--- zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/README.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/README.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -425,7 +425,7 @@
import os
<BLANKLINE>
join = os.path.join
- base = os.path.dirname(os.path.abspath(__file__))
+ base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
<BLANKLINE>
import sys
@@ -472,7 +472,7 @@
import os
<BLANKLINE>
join = os.path.join
- base = os.path.dirname(os.path.abspath(__file__))
+ base = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
base = os.path.dirname(base)
<BLANKLINE>
import sys
Modified: zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt
===================================================================
--- zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt 2009-09-08 10:46:18 UTC (rev 103641)
+++ zc.buildout/branches/use_distribute/zc.recipe.egg_/src/zc/recipe/egg/selecting-python.txt 2009-09-08 10:52:14 UTC (rev 103642)
@@ -78,7 +78,10 @@
... else:
... script_name = 'demo'
>>> f = open(os.path.join(sample_buildout, 'bin', script_name))
- >>> f.readline().strip() == '#!' + other_executable
+ >>> shebang = f.readline().strip()
+ >>> if shebang[:3] == '#!"' and shebang[-1] == '"':
+ ... shebang = '#!'+shebang[3:-1]
+ >>> shebang == '#!' + other_executable
True
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
<BLANKLINE>
@@ -97,9 +100,14 @@
... 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() == '#!' + other_executable
+
+ >>> shebang = f.readline().strip()
+ >>> if shebang[:3] == '#!"' and shebang[-1] == '"':
+ ... shebang = '#!'+shebang[3:-1]
+ >>> shebang == '#!' + other_executable
True
>>> print f.read(), # doctest: +NORMALIZE_WHITESPACE
+ <BLANKLINE>
import sys
<BLANKLINE>
sys.path[0:0] = [
@@ -109,21 +117,27 @@
<BLANKLINE>
_interactive = True
if len(sys.argv) > 1:
- import getopt
- _options, _args = getopt.getopt(sys.argv[1:], 'ic:')
+ _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:')
_interactive = False
for (_opt, _val) in _options:
if _opt == '-i':
_interactive = True
elif _opt == '-c':
exec _val
+ elif _opt == '-m':
+ sys.argv[1:] = _args
+ _args = []
+ __import__("runpy").run_module(
+ _val, {}, "__main__", alter_sys=True)
<BLANKLINE>
if _args:
sys.argv[:] = _args
- execfile(sys.argv[0])
+ __file__ = _args[0]
+ del _options, _args
+ execfile(__file__)
<BLANKLINE>
if _interactive:
- import code
- code.interact(banner="", local=globals())
+ del _interactive
+ __import__("code").interact(banner="", local=globals())
>>> f.close()
More information about the checkins
mailing list