|
|
# encoding: utf-8
|
|
|
|
|
|
"""Classes and functions for kernel related errors and exceptions."""
|
|
|
|
|
|
__docformat__ = "restructuredtext en"
|
|
|
|
|
|
# Tell nose to skip this module
|
|
|
__test__ = {}
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
# Copyright (C) 2008 The IPython Development Team
|
|
|
#
|
|
|
# Distributed under the terms of the BSD License. The full license is in
|
|
|
# the file COPYING, distributed as part of this software.
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
# Imports
|
|
|
#-------------------------------------------------------------------------------
|
|
|
from twisted.python import failure
|
|
|
|
|
|
from IPython.kernel.core import error
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
# Error classes
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
class KernelError(error.IPythonError):
|
|
|
pass
|
|
|
|
|
|
class NotDefined(KernelError):
|
|
|
def __init__(self, name):
|
|
|
self.name = name
|
|
|
self.args = (name,)
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<NotDefined: %s>' % self.name
|
|
|
|
|
|
__str__ = __repr__
|
|
|
|
|
|
class QueueCleared(KernelError):
|
|
|
pass
|
|
|
|
|
|
class IdInUse(KernelError):
|
|
|
pass
|
|
|
|
|
|
class ProtocolError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class ConnectionError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class InvalidEngineID(KernelError):
|
|
|
pass
|
|
|
|
|
|
class NoEnginesRegistered(KernelError):
|
|
|
pass
|
|
|
|
|
|
class InvalidClientID(KernelError):
|
|
|
pass
|
|
|
|
|
|
class InvalidDeferredID(KernelError):
|
|
|
pass
|
|
|
|
|
|
class SerializationError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class MessageSizeError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class PBMessageSizeError(MessageSizeError):
|
|
|
pass
|
|
|
|
|
|
class ResultNotCompleted(KernelError):
|
|
|
pass
|
|
|
|
|
|
class ResultAlreadyRetrieved(KernelError):
|
|
|
pass
|
|
|
|
|
|
class ClientError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class TaskAborted(KernelError):
|
|
|
pass
|
|
|
|
|
|
class TaskTimeout(KernelError):
|
|
|
pass
|
|
|
|
|
|
class NotAPendingResult(KernelError):
|
|
|
pass
|
|
|
|
|
|
class UnpickleableException(KernelError):
|
|
|
pass
|
|
|
|
|
|
class AbortedPendingDeferredError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class InvalidProperty(KernelError):
|
|
|
pass
|
|
|
|
|
|
class MissingBlockArgument(KernelError):
|
|
|
pass
|
|
|
|
|
|
class StopLocalExecution(KernelError):
|
|
|
pass
|
|
|
|
|
|
class SecurityError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class FileTimeoutError(KernelError):
|
|
|
pass
|
|
|
|
|
|
class TaskRejectError(KernelError):
|
|
|
"""Exception to raise when a task should be rejected by an engine.
|
|
|
|
|
|
This exception can be used to allow a task running on an engine to test
|
|
|
if the engine (or the user's namespace on the engine) has the needed
|
|
|
task dependencies. If not, the task should raise this exception. For
|
|
|
the task to be retried on another engine, the task should be created
|
|
|
with the `retries` argument > 1.
|
|
|
|
|
|
The advantage of this approach over our older properties system is that
|
|
|
tasks have full access to the user's namespace on the engines and the
|
|
|
properties don't have to be managed or tested by the controller.
|
|
|
"""
|
|
|
|
|
|
class CompositeError(KernelError):
|
|
|
def __init__(self, message, elist):
|
|
|
Exception.__init__(self, *(message, elist))
|
|
|
# Don't use pack_exception because it will conflict with the .message
|
|
|
# attribute that is being deprecated in 2.6 and beyond.
|
|
|
self.msg = message
|
|
|
self.elist = elist
|
|
|
|
|
|
def _get_engine_str(self, ev):
|
|
|
try:
|
|
|
ei = ev._ipython_engine_info
|
|
|
except AttributeError:
|
|
|
return '[Engine Exception]'
|
|
|
else:
|
|
|
return '[%i:%s]: ' % (ei['engineid'], ei['method'])
|
|
|
|
|
|
def _get_traceback(self, ev):
|
|
|
try:
|
|
|
tb = ev._ipython_traceback_text
|
|
|
except AttributeError:
|
|
|
return 'No traceback available'
|
|
|
else:
|
|
|
return tb
|
|
|
|
|
|
def __str__(self):
|
|
|
s = str(self.msg)
|
|
|
for et, ev, etb in self.elist:
|
|
|
engine_str = self._get_engine_str(ev)
|
|
|
s = s + '\n' + engine_str + str(et.__name__) + ': ' + str(ev)
|
|
|
return s
|
|
|
|
|
|
def print_tracebacks(self, excid=None):
|
|
|
if excid is None:
|
|
|
for (et,ev,etb) in self.elist:
|
|
|
print self._get_engine_str(ev)
|
|
|
print self._get_traceback(ev)
|
|
|
print
|
|
|
else:
|
|
|
try:
|
|
|
et,ev,etb = self.elist[excid]
|
|
|
except:
|
|
|
raise IndexError("an exception with index %i does not exist"%excid)
|
|
|
else:
|
|
|
print self._get_engine_str(ev)
|
|
|
print self._get_traceback(ev)
|
|
|
|
|
|
def raise_exception(self, excid=0):
|
|
|
try:
|
|
|
et,ev,etb = self.elist[excid]
|
|
|
except:
|
|
|
raise IndexError("an exception with index %i does not exist"%excid)
|
|
|
else:
|
|
|
raise et, ev, etb
|
|
|
|
|
|
def collect_exceptions(rlist, method):
|
|
|
elist = []
|
|
|
for r in rlist:
|
|
|
if isinstance(r, failure.Failure):
|
|
|
r.cleanFailure()
|
|
|
et, ev, etb = r.type, r.value, r.tb
|
|
|
# Sometimes we could have CompositeError in our list. Just take
|
|
|
# the errors out of them and put them in our new list. This
|
|
|
# has the effect of flattening lists of CompositeErrors into one
|
|
|
# CompositeError
|
|
|
if et==CompositeError:
|
|
|
for e in ev.elist:
|
|
|
elist.append(e)
|
|
|
else:
|
|
|
elist.append((et, ev, etb))
|
|
|
if len(elist)==0:
|
|
|
return rlist
|
|
|
else:
|
|
|
msg = "one or more exceptions from call to method: %s" % (method)
|
|
|
# This silliness is needed so the debugger has access to the exception
|
|
|
# instance (e in this case)
|
|
|
try:
|
|
|
raise CompositeError(msg, elist)
|
|
|
except CompositeError, e:
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|