##// END OF EJS Templates
restore accidentally removed EngineError...
MinRK -
Show More
@@ -1,247 +1,250 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Classes and functions for kernel related errors and exceptions.
3 """Classes and functions for kernel related errors and exceptions.
4
4
5 Inheritance diagram:
5 Inheritance diagram:
6
6
7 .. inheritance-diagram:: IPython.parallel.error
7 .. inheritance-diagram:: IPython.parallel.error
8 :parts: 3
8 :parts: 3
9
9
10 Authors:
10 Authors:
11
11
12 * Brian Granger
12 * Brian Granger
13 * Min RK
13 * Min RK
14 """
14 """
15 from __future__ import print_function
15 from __future__ import print_function
16
16
17 import sys
17 import sys
18 import traceback
18 import traceback
19
19
20 __docformat__ = "restructuredtext en"
20 __docformat__ = "restructuredtext en"
21
21
22 # Tell nose to skip this module
22 # Tell nose to skip this module
23 __test__ = {}
23 __test__ = {}
24
24
25 #-------------------------------------------------------------------------------
25 #-------------------------------------------------------------------------------
26 # Copyright (C) 2008-2011 The IPython Development Team
26 # Copyright (C) 2008-2011 The IPython Development Team
27 #
27 #
28 # Distributed under the terms of the BSD License. The full license is in
28 # Distributed under the terms of the BSD License. The full license is in
29 # the file COPYING, distributed as part of this software.
29 # the file COPYING, distributed as part of this software.
30 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
31
31
32 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
33 # Error classes
33 # Error classes
34 #-------------------------------------------------------------------------------
34 #-------------------------------------------------------------------------------
35 class IPythonError(Exception):
35 class IPythonError(Exception):
36 """Base exception that all of our exceptions inherit from.
36 """Base exception that all of our exceptions inherit from.
37
37
38 This can be raised by code that doesn't have any more specific
38 This can be raised by code that doesn't have any more specific
39 information."""
39 information."""
40
40
41 pass
41 pass
42
42
43 class KernelError(IPythonError):
43 class KernelError(IPythonError):
44 pass
44 pass
45
45
46 class EngineError(KernelError):
47 pass
48
46 class NoEnginesRegistered(KernelError):
49 class NoEnginesRegistered(KernelError):
47 pass
50 pass
48
51
49 class TaskAborted(KernelError):
52 class TaskAborted(KernelError):
50 pass
53 pass
51
54
52 class TaskTimeout(KernelError):
55 class TaskTimeout(KernelError):
53 pass
56 pass
54
57
55 class TimeoutError(KernelError):
58 class TimeoutError(KernelError):
56 pass
59 pass
57
60
58 class UnmetDependency(KernelError):
61 class UnmetDependency(KernelError):
59 pass
62 pass
60
63
61 class ImpossibleDependency(UnmetDependency):
64 class ImpossibleDependency(UnmetDependency):
62 pass
65 pass
63
66
64 class DependencyTimeout(ImpossibleDependency):
67 class DependencyTimeout(ImpossibleDependency):
65 pass
68 pass
66
69
67 class InvalidDependency(ImpossibleDependency):
70 class InvalidDependency(ImpossibleDependency):
68 pass
71 pass
69
72
70 class RemoteError(KernelError):
73 class RemoteError(KernelError):
71 """Error raised elsewhere"""
74 """Error raised elsewhere"""
72 ename=None
75 ename=None
73 evalue=None
76 evalue=None
74 traceback=None
77 traceback=None
75 engine_info=None
78 engine_info=None
76
79
77 def __init__(self, ename, evalue, traceback, engine_info=None):
80 def __init__(self, ename, evalue, traceback, engine_info=None):
78 self.ename=ename
81 self.ename=ename
79 self.evalue=evalue
82 self.evalue=evalue
80 self.traceback=traceback
83 self.traceback=traceback
81 self.engine_info=engine_info or {}
84 self.engine_info=engine_info or {}
82 self.args=(ename, evalue)
85 self.args=(ename, evalue)
83
86
84 def __repr__(self):
87 def __repr__(self):
85 engineid = self.engine_info.get('engine_id', ' ')
88 engineid = self.engine_info.get('engine_id', ' ')
86 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
89 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
87
90
88 def __str__(self):
91 def __str__(self):
89 return "%s(%s)" % (self.ename, self.evalue)
92 return "%s(%s)" % (self.ename, self.evalue)
90
93
91 def render_traceback(self):
94 def render_traceback(self):
92 """render traceback to a list of lines"""
95 """render traceback to a list of lines"""
93 return (self.traceback or "No traceback available").splitlines()
96 return (self.traceback or "No traceback available").splitlines()
94
97
95 def _render_traceback_(self):
98 def _render_traceback_(self):
96 """Special method for custom tracebacks within IPython.
99 """Special method for custom tracebacks within IPython.
97
100
98 This will be called by IPython instead of displaying the local traceback.
101 This will be called by IPython instead of displaying the local traceback.
99
102
100 It should return a traceback rendered as a list of lines.
103 It should return a traceback rendered as a list of lines.
101 """
104 """
102 return self.render_traceback()
105 return self.render_traceback()
103
106
104 def print_traceback(self, excid=None):
107 def print_traceback(self, excid=None):
105 """print my traceback"""
108 """print my traceback"""
106 print('\n'.join(self.render_traceback()))
109 print('\n'.join(self.render_traceback()))
107
110
108
111
109
112
110
113
111 class TaskRejectError(KernelError):
114 class TaskRejectError(KernelError):
112 """Exception to raise when a task should be rejected by an engine.
115 """Exception to raise when a task should be rejected by an engine.
113
116
114 This exception can be used to allow a task running on an engine to test
117 This exception can be used to allow a task running on an engine to test
115 if the engine (or the user's namespace on the engine) has the needed
118 if the engine (or the user's namespace on the engine) has the needed
116 task dependencies. If not, the task should raise this exception. For
119 task dependencies. If not, the task should raise this exception. For
117 the task to be retried on another engine, the task should be created
120 the task to be retried on another engine, the task should be created
118 with the `retries` argument > 1.
121 with the `retries` argument > 1.
119
122
120 The advantage of this approach over our older properties system is that
123 The advantage of this approach over our older properties system is that
121 tasks have full access to the user's namespace on the engines and the
124 tasks have full access to the user's namespace on the engines and the
122 properties don't have to be managed or tested by the controller.
125 properties don't have to be managed or tested by the controller.
123 """
126 """
124
127
125
128
126 class CompositeError(RemoteError):
129 class CompositeError(RemoteError):
127 """Error for representing possibly multiple errors on engines"""
130 """Error for representing possibly multiple errors on engines"""
128 tb_limit = 4 # limit on how many tracebacks to draw
131 tb_limit = 4 # limit on how many tracebacks to draw
129
132
130 def __init__(self, message, elist):
133 def __init__(self, message, elist):
131 Exception.__init__(self, *(message, elist))
134 Exception.__init__(self, *(message, elist))
132 # Don't use pack_exception because it will conflict with the .message
135 # Don't use pack_exception because it will conflict with the .message
133 # attribute that is being deprecated in 2.6 and beyond.
136 # attribute that is being deprecated in 2.6 and beyond.
134 self.msg = message
137 self.msg = message
135 self.elist = elist
138 self.elist = elist
136 self.args = [ e[0] for e in elist ]
139 self.args = [ e[0] for e in elist ]
137
140
138 def _get_engine_str(self, ei):
141 def _get_engine_str(self, ei):
139 if not ei:
142 if not ei:
140 return '[Engine Exception]'
143 return '[Engine Exception]'
141 else:
144 else:
142 return '[%s:%s]: ' % (ei['engine_id'], ei['method'])
145 return '[%s:%s]: ' % (ei['engine_id'], ei['method'])
143
146
144 def _get_traceback(self, ev):
147 def _get_traceback(self, ev):
145 try:
148 try:
146 tb = ev._ipython_traceback_text
149 tb = ev._ipython_traceback_text
147 except AttributeError:
150 except AttributeError:
148 return 'No traceback available'
151 return 'No traceback available'
149 else:
152 else:
150 return tb
153 return tb
151
154
152 def __str__(self):
155 def __str__(self):
153 s = str(self.msg)
156 s = str(self.msg)
154 for en, ev, etb, ei in self.elist[:self.tb_limit]:
157 for en, ev, etb, ei in self.elist[:self.tb_limit]:
155 engine_str = self._get_engine_str(ei)
158 engine_str = self._get_engine_str(ei)
156 s = s + '\n' + engine_str + en + ': ' + str(ev)
159 s = s + '\n' + engine_str + en + ': ' + str(ev)
157 if len(self.elist) > self.tb_limit:
160 if len(self.elist) > self.tb_limit:
158 s = s + '\n.... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
161 s = s + '\n.... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
159 return s
162 return s
160
163
161 def __repr__(self):
164 def __repr__(self):
162 return "CompositeError(%i)" % len(self.elist)
165 return "CompositeError(%i)" % len(self.elist)
163
166
164 def render_traceback(self, excid=None):
167 def render_traceback(self, excid=None):
165 """render one or all of my tracebacks to a list of lines"""
168 """render one or all of my tracebacks to a list of lines"""
166 lines = []
169 lines = []
167 if excid is None:
170 if excid is None:
168 for (en,ev,etb,ei) in self.elist[:self.tb_limit]:
171 for (en,ev,etb,ei) in self.elist[:self.tb_limit]:
169 lines.append(self._get_engine_str(ei))
172 lines.append(self._get_engine_str(ei))
170 lines.extend((etb or 'No traceback available').splitlines())
173 lines.extend((etb or 'No traceback available').splitlines())
171 lines.append('')
174 lines.append('')
172 if len(self.elist) > self.tb_limit:
175 if len(self.elist) > self.tb_limit:
173 lines.append(
176 lines.append(
174 '... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
177 '... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
175 )
178 )
176 else:
179 else:
177 try:
180 try:
178 en,ev,etb,ei = self.elist[excid]
181 en,ev,etb,ei = self.elist[excid]
179 except:
182 except:
180 raise IndexError("an exception with index %i does not exist"%excid)
183 raise IndexError("an exception with index %i does not exist"%excid)
181 else:
184 else:
182 lines.append(self._get_engine_str(ei))
185 lines.append(self._get_engine_str(ei))
183 lines.extend((etb or 'No traceback available').splitlines())
186 lines.extend((etb or 'No traceback available').splitlines())
184
187
185 return lines
188 return lines
186
189
187 def print_traceback(self, excid=None):
190 def print_traceback(self, excid=None):
188 print('\n'.join(self.render_traceback(excid)))
191 print('\n'.join(self.render_traceback(excid)))
189
192
190 def raise_exception(self, excid=0):
193 def raise_exception(self, excid=0):
191 try:
194 try:
192 en,ev,etb,ei = self.elist[excid]
195 en,ev,etb,ei = self.elist[excid]
193 except:
196 except:
194 raise IndexError("an exception with index %i does not exist"%excid)
197 raise IndexError("an exception with index %i does not exist"%excid)
195 else:
198 else:
196 raise RemoteError(en, ev, etb, ei)
199 raise RemoteError(en, ev, etb, ei)
197
200
198
201
199 def collect_exceptions(rdict_or_list, method='unspecified'):
202 def collect_exceptions(rdict_or_list, method='unspecified'):
200 """check a result dict for errors, and raise CompositeError if any exist.
203 """check a result dict for errors, and raise CompositeError if any exist.
201 Passthrough otherwise."""
204 Passthrough otherwise."""
202 elist = []
205 elist = []
203 if isinstance(rdict_or_list, dict):
206 if isinstance(rdict_or_list, dict):
204 rlist = rdict_or_list.values()
207 rlist = rdict_or_list.values()
205 else:
208 else:
206 rlist = rdict_or_list
209 rlist = rdict_or_list
207 for r in rlist:
210 for r in rlist:
208 if isinstance(r, RemoteError):
211 if isinstance(r, RemoteError):
209 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
212 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
210 # Sometimes we could have CompositeError in our list. Just take
213 # Sometimes we could have CompositeError in our list. Just take
211 # the errors out of them and put them in our new list. This
214 # the errors out of them and put them in our new list. This
212 # has the effect of flattening lists of CompositeErrors into one
215 # has the effect of flattening lists of CompositeErrors into one
213 # CompositeError
216 # CompositeError
214 if en=='CompositeError':
217 if en=='CompositeError':
215 for e in ev.elist:
218 for e in ev.elist:
216 elist.append(e)
219 elist.append(e)
217 else:
220 else:
218 elist.append((en, ev, etb, ei))
221 elist.append((en, ev, etb, ei))
219 if len(elist)==0:
222 if len(elist)==0:
220 return rdict_or_list
223 return rdict_or_list
221 else:
224 else:
222 msg = "one or more exceptions from call to method: %s" % (method)
225 msg = "one or more exceptions from call to method: %s" % (method)
223 # This silliness is needed so the debugger has access to the exception
226 # This silliness is needed so the debugger has access to the exception
224 # instance (e in this case)
227 # instance (e in this case)
225 try:
228 try:
226 raise CompositeError(msg, elist)
229 raise CompositeError(msg, elist)
227 except CompositeError as e:
230 except CompositeError as e:
228 raise e
231 raise e
229
232
230 def wrap_exception(engine_info={}):
233 def wrap_exception(engine_info={}):
231 etype, evalue, tb = sys.exc_info()
234 etype, evalue, tb = sys.exc_info()
232 stb = traceback.format_exception(etype, evalue, tb)
235 stb = traceback.format_exception(etype, evalue, tb)
233 exc_content = {
236 exc_content = {
234 'status' : 'error',
237 'status' : 'error',
235 'traceback' : stb,
238 'traceback' : stb,
236 'ename' : unicode(etype.__name__),
239 'ename' : unicode(etype.__name__),
237 'evalue' : unicode(evalue),
240 'evalue' : unicode(evalue),
238 'engine_info' : engine_info
241 'engine_info' : engine_info
239 }
242 }
240 return exc_content
243 return exc_content
241
244
242 def unwrap_exception(content):
245 def unwrap_exception(content):
243 err = RemoteError(content['ename'], content['evalue'],
246 err = RemoteError(content['ename'], content['evalue'],
244 ''.join(content['traceback']),
247 ''.join(content['traceback']),
245 content.get('engine_info', {}))
248 content.get('engine_info', {}))
246 return err
249 return err
247
250
General Comments 0
You need to be logged in to leave comments. Login now