Source code for radical.saga.exceptions


__author__    = "Andre Merzky, Ole Weidner"
__copyright__ = "Copyright 2012-2013, The SAGA Project"
__license__   = "MIT"


""" Exception classes
"""

import sys
import weakref
import operator
import traceback


# We have the choice of doing signature checks in exceptions, or to raise saga
# exceptions on signature checks -- we cannot do both.  At this point, we use
# the saga.exceptions in signatures, thus can *not* have signature checks
# here...

# ------------------------------------------------------------------------------
#
[docs]class SagaException (Exception) : """ The Exception class encapsulates information about error conditions encountered in SAGA. Additionally to the error message (e.message), the exception also provides a trace to the code location where the error condition got raised (e.traceback). B{Example}:: try : file = saga.filesystem.File ("sftp://comet.xsede.org/tmp/data1.dat") except saga.Timeout as to : # maybe the network is down? print("connection timed out") except saga.Exception as e : # something else went wrong print("Exception occurred: %s %s" % (e, e.traceback)) There are cases where multiple backends can report errors at the same time. In that case, the radical.saga implementation will collect the exceptions, sort them by their 'rank', and return the highest ranked one. All other catched exceptions are available via :func:`get_all_exceptions`, or via the `exceptions` property. The rank of an exception defines its explicity: in general terms: the higher the rank, the better defined / known is the cause of the problem. """ # -------------------------------------------------------------------------- # def __init__ (self, msg, parent=None, api_object=None, from_log=False) : """ Create a new exception object. :param msg: The exception message. :param parent: Original exception :param api_object: The object that has caused the exception, default is None. :param from_log: Exception c'tor originates from the static log_ member method (ignore in exception stack!) """ Exception.__init__(self, msg) self._plain_message = msg self._exceptions = [self] self._top_exception = self self._ptype = type(parent).__name__ # parent exception type self._stype = type(self ).__name__ # own exception type ignore_stack = 2 if from_log : ignore_stack += 1 if api_object : self._object = weakref.ref (api_object) else : self._object = None # did we get a parent exception? if parent : # if so, then this exception is likely created in some 'except' # clause, as a reaction on a previously catched exception (the # parent). Thus we append the message of the parent to our own # message, but keep the parent's traceback (after all, the original # exception location is what we are interested in). # if isinstance (parent, SagaException) : # that all works nicely when parent is our own exception type... self._traceback = parent.traceback frame = traceback.extract_stack ()[- ignore_stack] line = "%s +%s (%s) : %s" % (frame.filename, frame.lineno, frame.name, frame.line) self._message = " %-20s: %s (%s)\n%s" \ % (self._stype, msg, line, parent.msg) else: if self._stype != "NoneType" : # ... but if parent is a native (or any other) exception # type, we don't have a traceback really -- so we dig it # out of sys.exc_info. trace = sys.exc_info ()[2] stack = traceback.extract_tb (trace) traceback_list = traceback.format_list (stack) self._traceback = "".join (traceback_list) # the message composition is very similar -- we just inject # the parent exception type inconspicuously somewhere (above # that was part of 'parent.message' already). frame = traceback.extract_stack ()[- ignore_stack] line = "%s +%s (%s) : %s" % (frame.filename, frame.lineno, frame.name, frame.line) self._message = " %-20s: %s (%s)\n %-20s: %s" \ % (self._stype, msg, line, self._ptype, parent) else : # if we don't have a parent, we are a 1st principle exception, # i.e. a reaction to some genuine code error. Thus we extract the # traceback from exactly where we are in the code (the last stack # frame will be the call to this exception constructor), and we # create the original exception message from 'stype' and 'message'. stack = traceback.extract_stack () traceback_list = traceback.format_list (stack) self._traceback = "".join (traceback_list[:-1]) frame = traceback.extract_stack ()[- ignore_stack -1] line = "%s +%s (%s) : %s" % (frame.filename, frame.lineno, frame.name, frame.line) self._message = "%s (%s)" % (msg, line) # we can't do that earlier as _msg was not set up before self._messages = [self._message] # -------------------------------------------------------------------------- # def __str__ (self) : return self.get_message () # -------------------------------------------------------------------------- # def __repr__ (self) : return "%s\n%s" % (self._message, self._traceback) # -------------------------------------------------------------------------- # def _clone (self) : """ method is used internally -- see :func:`_get_exception_stack`.""" clone = self.__class__ ("") clone._parent = self._parent clone._object = self._object clone._message = self._message clone._messages = self._messages clone._exception = self._exceptions clone._traceback = self._traceback clone._stype = self._stype clone._ptype = self._ptype return clone # -------------------------------------------------------------------------- # @classmethod def _log (cls, logger, msg, parent=None, api_object=None, level='error'): """ This class method allows to log the exception message while constructing a SAGA exception, like:: # raise an exception, no logging raise saga.IncorrectState("File closed") # raise an exception, log as error event (error level is default) raise saga.IncorrectState._log(logger, "File closed") # raise an exception, log as warning event raise saga.IncorrectState._log(logger, "File closed", level=warning) raise saga.IncorrectState._log(logger, "File closed", warning) # same This way, the 'raise' remains clearly in the code, as that is the dominating semantics of the call. """ log_method = logger.error try : log_method = getattr (logger, level.lower()) except : sys.stderr.write ("unknown log level '%s'" % level) log_method ("%s: %s" % (cls.__name__, msg)) return cls (msg, parent=parent, api_object=api_object, from_log=True) # -------------------------------------------------------------------------- #
[docs] def get_message (self) : """ Return the exception message as a string. That message is also available via the 'message' property.""" return self._message
# -------------------------------------------------------------------------- # def _get_plain_message (self) : """ Return the plain error message as a string. """ return self._message # -------------------------------------------------------------------------- #
[docs] def get_type (self): """ Return the type of the exception as string. """ return self._stype
# -------------------------------------------------------------------------- #
[docs] def get_object (self) : """ Return the object that raised this exception. An object may not always be available -- for example, exceptions raised during object creation may not have the option to keep an incomplete object instance around. In those cases, this method will return 'None'. Either way, the object is also accessible via the 'object' property. """ # object is a weak_ref, and may have been garbage collected - we simply # return 'None' then return self._object ()
# -------------------------------------------------------------------------- # def _add_exception (self, e) : """ Some sub-operation raised a SAGA exception, but other exceptions may be catched later on. In that case the later exceptions can be added to the original one with :func:`_add_exception`\(e). Once all exceptions are collected, a call to :func:`_get_exception_stack`\() will return a new exception which is selected from the stack by rank and order. All other exceptions can be accessed from the returned exception by :func:`get_all_exceptions`\() -- those exceptions are then also ordered by rank. """ self._exceptions.append (e) self._messages.append (e.message) if e._rank > self._top_exception._rank : self._top_exception = e # -------------------------------------------------------------------------- # def _get_exception_stack (self) : """ This method is internally used by the radical.saga engine, and is only relevant for operations which (potentially) bind to more than one adaptor. """ if self._top_exception == self : return self # damned, we can't simply recast ourself to the top exception type -- so # we have to create a new exception with that type, and copy all state # over... # create a new exception with same type as top_exception clone = self._top_exception._clone () clone._exceptions = [] clone._messages = [] # copy all state over for e in sorted(self._exceptions, key=operator.attrgetter ('_rank'), reverse=True): clone._exceptions.append (e) clone._messages.append (e._message) return clone # -------------------------------------------------------------------------- #
[docs] def get_all_exceptions (self) : return self._exceptions
# -------------------------------------------------------------------------- #
[docs] def get_all_messages (self) : return self._messages
# -------------------------------------------------------------------------- #
[docs] def get_traceback (self) : return self._traceback
# -------------------------------------------------------------------------- # message = property (get_message) # string object = property (get_object) # object type type = property (get_type) # exception type exceptions = property (get_all_exceptions) # list [Exception] messages = property (get_all_messages) # list [string] traceback = property (get_traceback) # string
# ------------------------------------------------------------------------------ #
[docs]class NotImplemented(SagaException, NotImplementedError): """ radical.saga does not implement this method or class. (rank: 11)""" _rank = 11 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class IncorrectURL(SagaException, ValueError): """ The given URL could not be interpreted, for example due to an incorrect / unknown schema. (rank: 10)""" _rank = 10 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class BadParameter(SagaException, ValueError): """ A given parameter is out of bound or ill formatted. (rank: 9)""" _rank = 9 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class AlreadyExists(SagaException, ValueError): """ The entity to be created already exists. (rank: 8)""" _rank = 8 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class DoesNotExist(SagaException, KeyError): """ An operation tried to access a non-existing entity. (rank: 7)""" _rank = 7 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class IncorrectState(SagaException, RuntimeError): """ The operation is not allowed on the entity in its current state. (rank: 6)""" _rank = 6 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class PermissionDenied(SagaException, RuntimeError): """ The used identity is not permitted to perform the requested operation. (rank: 5)""" _rank = 5 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class AuthorizationFailed(SagaException, RuntimeError): """ The backend could not establish a valid identity. (rank: 4)""" _rank = 4 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class AuthenticationFailed(SagaException, RuntimeError): """ The backend could not establish a valid identity. (rank: 3)""" _rank = 3 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class Timeout(SagaException, RuntimeError): """ The interaction with the backend times out. (rank: 2)""" _rank = 2 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------ #
[docs]class NoSuccess(SagaException, RuntimeError): """ Some other error occurred. (rank: 1)""" _rank = 1 def __init__ (self, msg, parent=None, api_object=None, from_log=False) : SagaException.__init__ (self, msg, parent, api_object, from_log)
# ------------------------------------------------------------------------------