make interpolation error messages more informative
diff --git a/reclass/core.py b/reclass/core.py
index 6dc3254..4f1774c 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -17,7 +17,7 @@
from reclass.settings import Settings
from reclass.output.yaml_outputter import ExplicitDumper
from reclass.datatypes import Entity, Classes, Parameters, Exports
-from reclass.errors import MappingFormatError, ClassNotFound, ResolveError, InvQueryError, InterpolationError
+from reclass.errors import MappingFormatError, ClassNotFound, InvQueryClassNotFound, InvQueryError, InterpolationError
class Core(object):
@@ -99,8 +99,9 @@
if klass not in seen:
try:
class_entity = self._storage.get_class(klass, environment, self._settings)
- except ClassNotFound, e:
- e.set_nodename(nodename)
+ except ClassNotFound as e:
+ e.nodename = nodename
+ e.uri = entity.uri
raise
descent = self._recurse_entity(class_entity, seen=seen,
@@ -137,29 +138,28 @@
raise
if all_envs or node_base.environment == environment:
- node = self._node_entity(nodename)
+ try:
+ node = self._node_entity(nodename)
+ except ClassNotFound as e:
+ raise InvQueryClassNotFound(e)
if queries is None:
try:
node.interpolate_exports()
- except ResolveError as e:
+ except InterpolationError as e:
e.nodename = nodename
else:
node.initialise_interpolation()
for p, q in queries:
try:
node.interpolate_single_export(q)
- except ResolveError as e:
+ except InterpolationError as e:
e.nodename = nodename
raise InvQueryError(q.contents(), e, context=p, uri=q.uri())
inventory[nodename] = node.exports.as_dict()
return inventory
def _node_entity(self, nodename):
- try:
- node_entity = self._storage.get_node(nodename, self._settings)
- except InterpolationError as e:
- e.nodename = nodename
- raise
+ node_entity = self._storage.get_node(nodename, self._settings)
if node_entity.environment == None:
node_entity.environment = self._settings.default_environment
base_entity = Entity(self._settings, name='base')
@@ -167,26 +167,22 @@
base_entity.merge(self._get_input_data_entity())
base_entity.merge_parameters(self._get_automatic_parameters(nodename, node_entity.environment))
seen = {}
- merge_base = self._recurse_entity(base_entity, seen=seen, nodename=base_entity.name,
+ merge_base = self._recurse_entity(base_entity, seen=seen, nodename=nodename,
environment=node_entity.environment)
- return self._recurse_entity(node_entity, merge_base, seen=seen, nodename=node_entity.name,
+ return self._recurse_entity(node_entity, merge_base, seen=seen, nodename=nodename,
environment=node_entity.environment)
def _nodeinfo(self, nodename, inventory):
- ret = self._node_entity(nodename)
- ret.initialise_interpolation()
- if ret.parameters.has_inv_query() and inventory is None:
- try:
- inventory = self._get_inventory(ret.parameters.needs_all_envs(), ret.environment, ret.parameters.get_inv_queries())
- except InvQueryError as e:
- e.nodename = nodename
- raise e
try:
+ ret = self._node_entity(nodename)
+ ret.initialise_interpolation()
+ if ret.parameters.has_inv_query() and inventory is None:
+ inventory = self._get_inventory(ret.parameters.needs_all_envs(), ret.environment, ret.parameters.get_inv_queries())
ret.interpolate(nodename, inventory)
- except ResolveError as e:
+ return ret
+ except (InterpolationError, InvQueryClassNotFound) as e:
e.nodename = nodename
raise
- return ret
def _nodeinfo_as_dict(self, nodename, entity):
ret = {'__reclass__' : {'node': entity.name, 'name': nodename,
diff --git a/reclass/errors.py b/reclass/errors.py
index a27919b..629ae4d 100644
--- a/reclass/errors.py
+++ b/reclass/errors.py
@@ -14,11 +14,14 @@
class ReclassException(Exception):
- def __init__(self, rc=posix.EX_SOFTWARE, msg=None):
+ def __init__(self, rc=posix.EX_SOFTWARE, msg=None, tbFlag=True):
super(ReclassException, self).__init__()
self._rc = rc
self._msg = msg
- self._previous_traceback = traceback.format_exc()
+ if tbFlag:
+ self._traceback = traceback.format_exc()
+ else:
+ self._traceback = None
self._full_traceback = False
message = property(lambda self: self._get_message())
@@ -40,8 +43,8 @@
for l in traceback.format_tb(tb):
print >>out, l,
print >>out
- if self._previous_traceback:
- print >>out, self._previous_traceback
+ if self._traceback:
+ print >>out, self._traceback
print >>out, self.message
print >>out
sys.exit(self.rc)
@@ -100,34 +103,47 @@
def __init__(self, storage, nodename, uri):
super(NodeNotFound, self).__init__(msg=None)
- self._storage = storage
- self._name = nodename
- self._uri = uri
+ self.storage = storage
+ self.name = nodename
+ self.uri = uri
def _get_message(self):
msg = "Node '{0}' not found under {1}://{2}"
- return msg.format(self._name, self._storage, self._uri)
+ return msg.format(self.name, self.storage, self.uri)
class ClassNotFound(NotFoundError):
- def __init__(self, storage, classname, uri, nodename=None):
+ def __init__(self, storage, classname, path, nodename='', uri=''):
super(ClassNotFound, self).__init__(msg=None)
- self._storage = storage
- self._name = classname
- self._uri = uri
- self._nodename = nodename
+ self.storage = storage
+ self.name = classname
+ self.path = path
+ self.nodename = nodename
+ self.uri = uri
def _get_message(self):
- if self._nodename:
- msg = "Class '{0}' (in ancestry of node '{1}') not found " \
- "under {2}://{3}"
- else:
- msg = "Class '{0}' not found under {2}://{3}"
- return msg.format(self._name, self._nodename, self._storage, self._uri)
+ msg = '=> {0}\n'.format(self.nodename)
+ msg += ' In {0}\n'.format(self.uri)
+ msg += ' Class {0} not found under {1}://{2}'.format(self.name, self.storage, self.path)
+ return msg
- def set_nodename(self, nodename):
- self._nodename = nodename
+
+class InvQueryClassNotFound(NotFoundError):
+
+ def __init__(self, classNotFoundError, nodename=''):
+ super(InvQueryClassNotFound, self).__init__(msg=None)
+ self.nodename = nodename
+ self.classNotFoundError = classNotFoundError
+ self._traceback = self.classNotFoundError._traceback
+
+ def _get_message(self):
+ msg = '-> {0}\n'.format(self.nodename)
+ msg += ' For InvQueries:\n'
+ msg += ' -> {0}\n'.format(self.classNotFoundError.nodename)
+ msg += ' In {0}\n'.format(self.classNotFoundError.uri)
+ msg += ' Class {0} not found under {1}://{2}'.format(self.classNotFoundError.name, self.classNotFoundError.storage, self.classNotFoundError.path)
+ return msg
class InterpolationError(ReclassException):
@@ -139,7 +155,7 @@
self.context = None
def _get_message(self):
- msg = '=> {0}\n'.format(self.nodename)
+ msg = '-> {0}\n'.format(self.nodename)
msg += self._render_error_message(self._get_error_message(), 1)
msg = msg[:-1]
return msg
@@ -183,9 +199,9 @@
def _get_error_message(self):
msg1 = 'Failed inv query {0}'.format(self.query.join(EXPORT_SENTINELS)) + self._add_context_and_uri()
- msg2 = [ '--> {0}'.format(self.resolveError.nodename) ]
- msg2.extend(self.resolveError._get_error_message())
- return [ msg1, msg2 ]
+ msg2 = '-> {0}'.format(self.resolveError.nodename)
+ msg3 = self.resolveError._get_error_message()
+ return [ msg1, msg2, msg3 ]
class ParseError(InterpolationError):
@@ -228,8 +244,8 @@
class ExpressionError(InterpolationError):
- def __init__(self, msg, rc=posix.EX_DATAERR):
- super(ExpressionError, self).__init__(rc=rc, msg=None)
+ def __init__(self, msg, rc=posix.EX_DATAERR, tbFlag=True):
+ super(ExpressionError, self).__init__(rc=rc, msg=None, tbFlag=tbFlag)
self._error_msg = msg
def _get_error_message(self):
diff --git a/reclass/storage/yaml_fs/__init__.py b/reclass/storage/yaml_fs/__init__.py
index 20388ac..c05afdb 100644
--- a/reclass/storage/yaml_fs/__init__.py
+++ b/reclass/storage/yaml_fs/__init__.py
@@ -98,7 +98,7 @@
entity = YamlData.from_file(path).get_entity(name, settings)
return entity
- def get_class(self, name, environment, setings):
+ def get_class(self, name, environment, settings):
vvv('GET CLASS {0}'.format(name))
try:
path = os.path.join(self.classes_uri, self._classes[name])
diff --git a/reclass/values/invitem.py b/reclass/values/invitem.py
index cb55fc3..e3201a0 100644
--- a/reclass/values/invitem.py
+++ b/reclass/values/invitem.py
@@ -64,7 +64,7 @@
self._parameter_value = self._resolve(self._parameter_path, context)
if self._parameter_value is None or self._test is None:
- raise ExpressionError('Failed to render %s' % str(self))
+ raise ExpressionError('Failed to render %s' % str(self), tbFlag=False)
if self._export_path.exists_in(items):
result = False
@@ -76,7 +76,7 @@
if export_value != self._parameter_value:
result = True
else:
- raise ExpressionError('Unknown test {0}'.format(self._test))
+ raise ExpressionError('Unknown test {0}'.format(self._test), tbFlag=False)
return result
else:
return False
@@ -144,7 +144,7 @@
elif self._operators[i] == _OR:
result = result or next_result
else:
- raise ExpressionError('Unknown operator {0} {1}'.format(self._operators[i], self.elements))
+ raise ExpressionError('Unknown operator {0} {1}'.format(self._operators[i], self.elements), tbFlag=False)
return result
@@ -248,7 +248,7 @@
self._expr_type = tokens[1][0]
self._expr = list(tokens[1][1])
else:
- raise ExpressionError('Failed to parse %s' % str(tokens))
+ raise ExpressionError('Failed to parse %s' % str(tokens), tbFlag=False)
if self._expr_type == _VALUE:
self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first()
@@ -267,7 +267,7 @@
self._refs = self._question.refs()
self._inv_refs = self._question.inv_refs()
else:
- raise ExpressionError('Unknown expression type: %s' % self._expr_type)
+ raise ExpressionError('Unknown expression type: %s' % self._expr_type, tbFlag=False)
def assembleRefs(self, context):
return
@@ -308,7 +308,7 @@
def _test_expression(self, context, inventory):
if self._value_path is None:
- ExpressionError('Failed to render %s' % str(self))
+ ExpressionError('Failed to render %s' % str(self), tbFlag=False)
results = {}
for node, items in inventory.iteritems():
@@ -330,7 +330,7 @@
return self._test_expression(context, inventory)
elif self._expr_type == _LIST_TEST:
return self._list_test_expression(context, inventory)
- raise ExpressionError('Failed to render %s' % str(self))
+ raise ExpressionError('Failed to render %s' % str(self), tbFlag=False)
def __str__(self):
return ' '.join(str(j) for i,j in self._expr)