Commit 517357c1 authored by Jim Fulton's avatar Jim Fulton

- Improved error handling. No longer show tracebacks for user errors.

- Now require a recipe option (and therefore a section) for every part.
parent 181089e4
...@@ -189,6 +189,13 @@ You can send questions to jim@zope.com. ...@@ -189,6 +189,13 @@ You can send questions to jim@zope.com.
Change History Change History
============== ==============
1.0.0b3
-------
- Improved error handling. No longer show tracebacks for user errors.
- Now require a recipe option (and therefore a section) for every part.
1.0.0b2 1.0.0b2
------- -------
......
...@@ -29,10 +29,21 @@ import zc.buildout.easy_install ...@@ -29,10 +29,21 @@ import zc.buildout.easy_install
import pkg_resources import pkg_resources
import zc.buildout.easy_install import zc.buildout.easy_install
class MissingOption(KeyError): class UserError(Exception):
"""Errors made by a user
"""
def __str__(self):
return " ".join(map(str, self))
class MissingOption(UserError, KeyError):
"""A required option was missing """A required option was missing
""" """
class MissingSection(UserError, KeyError):
"""A required section is missinh
"""
class Options(dict): class Options(dict):
def __init__(self, buildout, section, data): def __init__(self, buildout, section, data):
...@@ -44,7 +55,8 @@ class Options(dict): ...@@ -44,7 +55,8 @@ class Options(dict):
try: try:
return super(Options, self).__getitem__(option) return super(Options, self).__getitem__(option)
except KeyError: except KeyError:
raise MissingOption("Missing option", self.section, option) raise MissingOption("Missing option: %s:%s"
% (self.section, option))
# XXX need test # XXX need test
def __setitem__(self, option, value): def __setitem__(self, option, value):
...@@ -133,7 +145,12 @@ class Buildout(dict): ...@@ -133,7 +145,12 @@ class Buildout(dict):
if r is not None: if r is not None:
return r return r
if key in seen: if key in seen:
raise ValueError('Circular references', seen, key) raise UserError("Circular reference in substitutions.\n"
"We're evaluating %s\nand are referencing: %s.\n"
% (", ".join([":".join(k) for k in seen]),
":".join(key)
)
)
seen.append(key) seen.append(key)
value = '$$'.join([self._dosubs_esc(s, data, converted, seen) value = '$$'.join([self._dosubs_esc(s, data, converted, seen)
for s in value.split('$$') for s in value.split('$$')
...@@ -151,10 +168,12 @@ class Buildout(dict): ...@@ -151,10 +168,12 @@ class Buildout(dict):
if v is None: if v is None:
options = data.get(s[0]) options = data.get(s[0])
if options is None: if options is None:
raise KeyError("Referenced section does not exist", s[0]) raise MissingSection(
"Referenced section does not exist", s[0])
v = options.get(s[1]) v = options.get(s[1])
if v is None: if v is None:
raise KeyError("Referenced option does not exist", *s) raise MissingOption("Referenced option does not exist:",
*s)
if '$' in v: if '$' in v:
v = self._dosubs(s[0], s[1], v, data, converted, seen) v = self._dosubs(s[0], s[1], v, data, converted, seen)
options[s[1]] = v options[s[1]] = v
...@@ -335,7 +354,8 @@ class Buildout(dict): ...@@ -335,7 +354,8 @@ class Buildout(dict):
for part in parts: for part in parts:
options = self.get(part) options = self.get(part)
if options is None: if options is None:
options = self[part] = {} raise MissingSection("No section was specified for part", part)
recipe, entry = self._recipe(part, options) recipe, entry = self._recipe(part, options)
recipes_requirements.append(recipe) recipes_requirements.append(recipe)
...@@ -382,7 +402,7 @@ class Buildout(dict): ...@@ -382,7 +402,7 @@ class Buildout(dict):
options['__buildout_signature__'] = ' '.join(sig) options['__buildout_signature__'] = ' '.join(sig)
def _recipe(self, part, options): def _recipe(self, part, options):
recipe = options.get('recipe', part) recipe = options['recipe']
if ':' in recipe: if ':' in recipe:
recipe, entry = recipe.split(':') recipe, entry = recipe.split(':')
else: else:
...@@ -528,7 +548,7 @@ def _open(base, filename, seen): ...@@ -528,7 +548,7 @@ def _open(base, filename, seen):
filename = os.path.join(base, filename) filename = os.path.join(base, filename)
if filename in seen: if filename in seen:
raise ValueError("Recursive file include", seen, filename) raise UserError("Recursive file include", seen, filename)
base = os.path.dirname(filename) base = os.path.dirname(filename)
seen.append(filename) seen.append(filename)
...@@ -592,7 +612,7 @@ def _update(d1, d2): ...@@ -592,7 +612,7 @@ def _update(d1, d2):
return d1 return d1
def _error(*message): def _error(*message):
sys.stderr.write(' '.join(message) +'\n') sys.stderr.write('Error: ' + ' '.join(message) +'\n')
sys.exit(1) sys.exit(1)
def main(args=None): def main(args=None):
...@@ -612,9 +632,6 @@ def main(args=None): ...@@ -612,9 +632,6 @@ def main(args=None):
else: else:
verbosity -= 10 verbosity -= 10
op = op[1:] op = op[1:]
if op == 'd':
op = op[1:]
import pdb; pdb.set_trace()
if op[:1] == 'c': if op[:1] == 'c':
op = op[1:] op = op[1:]
...@@ -641,8 +658,11 @@ def main(args=None): ...@@ -641,8 +658,11 @@ def main(args=None):
if verbosity: if verbosity:
options.append(('buildout', 'verbosity', str(verbosity))) options.append(('buildout', 'verbosity', str(verbosity)))
try:
buildout = Buildout(config_file, options) buildout = Buildout(config_file, options)
buildout._setup_logging() buildout._setup_logging()
except UserError, v:
_error(str(v))
if args: if args:
command = args.pop(0) command = args.pop(0)
...@@ -651,8 +671,12 @@ def main(args=None): ...@@ -651,8 +671,12 @@ def main(args=None):
else: else:
command = 'install' command = 'install'
try:
try: try:
getattr(buildout, command)(args) getattr(buildout, command)(args)
except UserError, v:
_error(str(v))
finally: finally:
logging.shutdown() logging.shutdown()
......
...@@ -255,8 +255,9 @@ buildout and recipes are doing. ...@@ -255,8 +255,9 @@ buildout and recipes are doing.
recipe = recipes:mkdir recipe = recipes:mkdir
path = mystuff path = mystuff
Generally, when we name a part, we also create a section of the same
name that contains part data. In this section, we'll usually define When we name a part, we also create a section of the same
name that contains part data. In this section, we'll define
the recipe to be used to install the part. In this case, we also the recipe to be used to install the part. In this case, we also
specify the path to be created. specify the path to be created.
......
...@@ -39,7 +39,7 @@ Asking for an option that doesn't exist, a MissingOption error is raised: ...@@ -39,7 +39,7 @@ Asking for an option that doesn't exist, a MissingOption error is raised:
>>> buildout['buildout']['eek'] >>> buildout['buildout']['eek']
Traceback (most recent call last): Traceback (most recent call last):
... ...
MissingOption: ('Missing option', 'buildout', 'eek') MissingOption: Missing option: buildout:eek
It is an error to create a variable-reference cycle: It is an error to create a variable-reference cycle:
...@@ -55,11 +55,36 @@ It is an error to create a variable-reference cycle: ...@@ -55,11 +55,36 @@ It is an error to create a variable-reference cycle:
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')), >>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last): Error: Circular reference in substitutions.
We're evaluating buildout:y, buildout:z, buildout:x
and are referencing: buildout:y.
Al parts have to have a section:
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... parts = x
... ''')
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
Error: No section was specified for part x
and all parts have to have a specified recipe:
>>> write(sample_buildout, 'buildout.cfg',
... '''
... [buildout]
... parts = x
... ...
ValueError: ('Circular references', ... [x]
[('buildout', 'y'), ('buildout', 'z'), ('buildout', 'x')], ... foo = 1
('buildout', 'y')) ... ''')
>>> print system(os.path.join(sample_buildout, 'bin', 'buildout')),
Error: Missing option: x:recipe
""" """
def test_comparing_saved_options_with_funny_characters(): def test_comparing_saved_options_with_funny_characters():
......
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