Rework exceptions, improve message handling
Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/reclass/errors.py b/reclass/errors.py
index 4ab85d6..2440178 100644
--- a/reclass/errors.py
+++ b/reclass/errors.py
@@ -14,125 +14,162 @@
class ReclassException(Exception):
- def __init__(self, msg, rc=posix.EX_SOFTWARE, *args):
- super(ReclassException, self).__init__(msg, *args)
+ def __init__(self, rc=posix.EX_SOFTWARE, msg=None):
+ super(ReclassException, self).__init__()
self._rc = rc
+ self._msg = msg
self._traceback = traceback.format_exc()
- def __str__(self):
- return self.message
-
+ message = property(lambda self: self._get_message())
rc = property(lambda self: self._rc)
+ def _get_message(self):
+ if self._msg:
+ return self._msg
+ else:
+ return 'No error message provided.'
+
def exit_with_message(self, out=sys.stderr):
print >>out, self.message
- print >>out, self._traceback
+ if self._traceback:
+ print >>out, self._traceback
sys.exit(self.rc)
class PermissionError(ReclassException):
def __init__(self, msg, rc=posix.EX_NOPERM):
- super(PermissionError, self).__init__(msg, rc)
+ super(PermissionError, self).__init__(rc=rc, msg=msg)
class InvocationError(ReclassException):
def __init__(self, msg, rc=posix.EX_USAGE):
- super(InvocationError, self).__init__(msg, rc)
+ super(InvocationError, self).__init__(rc=rc, msg=msg)
class ConfigError(ReclassException):
def __init__(self, msg, rc=posix.EX_CONFIG):
- super(ConfigError, self).__init__(msg, rc)
+ super(ConfigError, self).__init__(rc=rc, msg=msg)
class DuplicateUriError(ConfigError):
def __init__(self, nodes_uri, classes_uri):
- msg = "The inventory URIs must not be the same for nodes and classes: "
- msg += nodes_uri
- super(DuplicateUriError, self).__init__(msg)
+ super(DuplicateUriError, self).__init__(msg=None)
+ self._nodes_uri = nodes_uri
+ self._classes_uri = classes_uri
+
+ def _get_message(self):
+ return "The inventory URIs must not be the same " \
+ "for nodes and classes: {0}".format(self._nodes_uri)
class UriOverlapError(ConfigError):
def __init__(self, nodes_uri, classes_uri):
+ super(UriOverlapError, self).__init__(msg=None)
+ self._nodes_uri = nodes_uri
+ self._classes_uri = classes_uri
+
+ def _get_message(self):
msg = "The URIs for the nodes and classes inventories must not " \
"overlap, but {0} and {1} do."
- msg = msg.format(nodes_uri, classes_uri)
- super(UriOverlapError, self).__init__(msg)
+ return msg.format(self._nodes_uri, self._classes_uri)
class NotFoundError(ReclassException):
def __init__(self, msg, rc=posix.EX_IOERR):
- super(NotFoundError, self).__init__(msg, rc)
+ super(NotFoundError, self).__init__(rc=rc, msg=msg)
class NodeNotFound(NotFoundError):
def __init__(self, storage, nodename, uri):
+ super(NodeNotFound, self).__init__(msg=None)
self._storage = storage
self._name = nodename
self._uri = uri
- msg = "Node '{0}' not found under {1}://{2}".format(self._name,
- self._storage,
- self._uri)
- super(NodeNotFound, self).__init__(msg)
+
+ def _get_message(self):
+ msg = "Node '{0}' not found under {1}://{2}"
+ return msg.format(self._name, self._storage, self._uri)
class ClassNotFound(NotFoundError):
- def __init__(self, storage, classname, uri, nodename):
+ def __init__(self, storage, classname, uri, nodename=None):
+ super(ClassNotFound, self).__init__(msg=None)
self._storage = storage
self._name = classname
self._uri = uri
self._nodename = nodename
- msg = "Class '{0}' (in ancestry of node '{1}') not found under {2}://{3}" \
- .format(self._name, self._nodename, self._storage, self._uri)
- super(ClassNotFound, self).__init__(msg)
+
+ 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)
+
+ def set_nodename(self, nodename):
+ self._nodename = nodename
class InterpolationError(ReclassException):
def __init__(self, msg, rc=posix.EX_DATAERR):
- super(InterpolationError, self).__init__(msg, rc)
+ super(InterpolationError, self).__init__(rc=rc, msg=msg)
class UndefinedVariableError(InterpolationError):
def __init__(self, var, context=None):
+ super(UndefinedVariableError, self).__init__(msg=None)
self._var = var
- msg = "Cannot resolve " + var.join(PARAMETER_INTERPOLATION_SENTINELS)
- if context:
- msg += ' in the context of %s' % context
- super(UndefinedVariableError, self).__init__(msg)
+ self._context = context
- var = property(lambda x: x._var)
+ def _get_message(self):
+ msg = "Cannot resolve " + var.join(PARAMETER_INTERPOLATION_SENTINELS)
+ if self._context:
+ msg += ' in the context of %s' % self._context
+ return msg
+
+ def set_context(self, context):
+ self._context = context
class IncompleteInterpolationError(InterpolationError):
def __init__(self, string, end_sentinel):
- msg = "Missing '%s' to end reference: %s" % \
- (end_sentinel, string.join(PARAMETER_INTERPOLATION_SENTINELS))
- super(IncompleteInterpolationError, self).__init__(msg)
+ super(IncompleteInterpolationError, self).__init__(msg=None)
+ self._ref = string.join(PARAMETER_INTERPOLATION_SENTINELS)
+ self._end_sentinel = end_sentinel
+
+ def _get_message(self):
+ msg = "Missing '{0}' to end reference: {1}"
+ return msg.format(self._end_sentinel, self._ref)
class InfiniteRecursionError(InterpolationError):
def __init__(self, path, ref):
- msg = "Infinite recursion while resolving %s at %s" \
- % (ref.join(PARAMETER_INTERPOLATION_SENTINELS), path)
- super(InfiniteRecursionError, self).__init__(msg)
+ super(InfiniteRecursionError, self).__init__(msg=None)
+ self._path = path
+ self._ref = ref.join(PARAMETER_INTERPOLATION_SENTINELS)
+
+ def _get_message(self):
+ msg = "Infinite recursion while resolving {0} at {1}"
+ return msg.format(self._ref, self._path)
class MappingError(ReclassException):
def __init__(self, msg, rc=posix.EX_DATAERR):
- super(MappingError, self).__init__(msg, rc)
+ super(MappingError, self).__init__(rc=rc, msg=msg)
class MappingFormatError(MappingError):
@@ -144,23 +181,31 @@
class NameError(ReclassException):
def __init__(self, msg, rc=posix.EX_DATAERR):
- super(NameError, self).__init__(msg, rc)
+ super(NameError, self).__init__(rc=rc, msg=msg)
class InvalidClassnameError(NameError):
def __init__(self, invalid_character, classname):
- self._invalid_character = invalid_character
+ super(InvalidClassnameError, self).__init__(msg=None)
+ self._char = invalid_character
self._classname = classname
+
+ def _get_message(self):
msg = "Invalid character '{0}' in class name '{1}'."
- msg = msg.format(invalid_character, classname)
- super(InvalidClassnameError, self).__init__(msg)
+ return msg.format(self._char, classname)
class DuplicateNodeNameError(NameError):
def __init__(self, storage, name, uri1, uri2):
+ super(DuplicateNodeNameError, self).__init__(msg=None)
+ self._storage = storage
+ self._name = name
+ self._uris = (uri1, uri2)
+
+ def _get_message(self):
msg = "{0}: Definition of node '{1}' in '{2}' collides with " \
- "definition in '{3}'. Nodes can only be defined once per inventory."
- msg = msg.format(storage, name, uri2, uri1)
- super(DuplicateNodeNameError, self).__init__(msg)
+ "definition in '{3}'. Nodes can only be defined once " \
+ "per inventory."
+ return msg.format(storage, name, uris[1], uris[0])