##// END OF EJS Templates
Merge pull request #3527 from minrk/parallelerror...
Matthias Bussonnier -
r11224:e051dca1 merge
parent child Browse files
Show More
@@ -1,53 +1,53 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Global exception classes for IPython.core.
3 Global exception classes for IPython.core.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Fernando Perez
8 * Fernando Perez
9 * Min Ragan-Kelley
9 * Min Ragan-Kelley
10
10
11 Notes
11 Notes
12 -----
12 -----
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2011 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Exception classes
27 # Exception classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 class IPythonCoreError(Exception):
30 class IPythonCoreError(Exception):
31 pass
31 pass
32
32
33
33
34 class TryNext(IPythonCoreError):
34 class TryNext(IPythonCoreError):
35 """Try next hook exception.
35 """Try next hook exception.
36
36
37 Raise this in your hook function to indicate that the next hook handler
37 Raise this in your hook function to indicate that the next hook handler
38 should be used to handle the operation.
38 should be used to handle the operation.
39 """
39 """
40
40
41 class UsageError(IPythonCoreError):
41 class UsageError(IPythonCoreError):
42 """Error in magic function arguments, etc.
42 """Error in magic function arguments, etc.
43
43
44 Something that probably won't warrant a full traceback, but should
44 Something that probably won't warrant a full traceback, but should
45 nevertheless interrupt a macro / batch file.
45 nevertheless interrupt a macro / batch file.
46 """
46 """
47
47
48 class StdinNotImplementedError(IPythonCoreError, NotImplementedError):
48 class StdinNotImplementedError(IPythonCoreError, NotImplementedError):
49 """raw_input was requested in a context where it is not supported
49 """raw_input was requested in a context where it is not supported
50
50
51 For use in IPython kernels, where only some frontends may support
51 For use in IPython kernels, where only some frontends may support
52 stdin requests.
52 stdin requests.
53 """
53 """
@@ -1,354 +1,248 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 # Exceptions associated with the controller objects
44 class ControllerError(IPythonError): pass
45
46 class ControllerCreationError(ControllerError): pass
47
48
49 # Exceptions associated with the Engines
50 class EngineError(IPythonError): pass
51
52 class EngineCreationError(EngineError): pass
53
54 class KernelError(IPythonError):
43 class KernelError(IPythonError):
55 pass
44 pass
56
45
57 class NotDefined(KernelError):
58 def __init__(self, name):
59 self.name = name
60 self.args = (name,)
61
62 def __repr__(self):
63 return '<NotDefined: %s>' % self.name
64
65 __str__ = __repr__
66
67
68 class QueueCleared(KernelError):
69 pass
70
71
72 class IdInUse(KernelError):
73 pass
74
75
76 class ProtocolError(KernelError):
77 pass
78
79
80 class ConnectionError(KernelError):
81 pass
82
83
84 class InvalidEngineID(KernelError):
85 pass
86
46
87
47
88 class NoEnginesRegistered(KernelError):
48 class NoEnginesRegistered(KernelError):
89 pass
49 pass
90
50
91
51
92 class InvalidClientID(KernelError):
93 pass
94
95
96 class InvalidDeferredID(KernelError):
97 pass
98
99
100 class SerializationError(KernelError):
101 pass
102
103
104 class MessageSizeError(KernelError):
105 pass
106
107
108 class PBMessageSizeError(MessageSizeError):
109 pass
110
111
112 class ResultNotCompleted(KernelError):
113 pass
114
115
116 class ResultAlreadyRetrieved(KernelError):
117 pass
118
119 class ClientError(KernelError):
120 pass
121
122
123 class TaskAborted(KernelError):
52 class TaskAborted(KernelError):
124 pass
53 pass
125
54
126
55
127 class TaskTimeout(KernelError):
128 pass
129
130
131 class NotAPendingResult(KernelError):
132 pass
133
134
135 class UnpickleableException(KernelError):
136 pass
137
138
139 class AbortedPendingDeferredError(KernelError):
140 pass
141
142
143 class InvalidProperty(KernelError):
144 pass
145
146
147 class MissingBlockArgument(KernelError):
148 pass
149
150
151 class StopLocalExecution(KernelError):
152 pass
153
154
155 class SecurityError(KernelError):
156 pass
157
158
159 class FileTimeoutError(KernelError):
160 pass
161
162 class TimeoutError(KernelError):
56 class TimeoutError(KernelError):
163 pass
57 pass
164
58
165 class UnmetDependency(KernelError):
59 class UnmetDependency(KernelError):
166 pass
60 pass
167
61
168 class ImpossibleDependency(UnmetDependency):
62 class ImpossibleDependency(UnmetDependency):
169 pass
63 pass
170
64
171 class DependencyTimeout(ImpossibleDependency):
65 class DependencyTimeout(ImpossibleDependency):
172 pass
66 pass
173
67
174 class InvalidDependency(ImpossibleDependency):
68 class InvalidDependency(ImpossibleDependency):
175 pass
69 pass
176
70
177 class RemoteError(KernelError):
71 class RemoteError(KernelError):
178 """Error raised elsewhere"""
72 """Error raised elsewhere"""
179 ename=None
73 ename=None
180 evalue=None
74 evalue=None
181 traceback=None
75 traceback=None
182 engine_info=None
76 engine_info=None
183
77
184 def __init__(self, ename, evalue, traceback, engine_info=None):
78 def __init__(self, ename, evalue, traceback, engine_info=None):
185 self.ename=ename
79 self.ename=ename
186 self.evalue=evalue
80 self.evalue=evalue
187 self.traceback=traceback
81 self.traceback=traceback
188 self.engine_info=engine_info or {}
82 self.engine_info=engine_info or {}
189 self.args=(ename, evalue)
83 self.args=(ename, evalue)
190
84
191 def __repr__(self):
85 def __repr__(self):
192 engineid = self.engine_info.get('engine_id', ' ')
86 engineid = self.engine_info.get('engine_id', ' ')
193 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
87 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
194
88
195 def __str__(self):
89 def __str__(self):
196 return "%s(%s)" % (self.ename, self.evalue)
90 return "%s(%s)" % (self.ename, self.evalue)
197
91
198 def render_traceback(self):
92 def render_traceback(self):
199 """render traceback to a list of lines"""
93 """render traceback to a list of lines"""
200 return (self.traceback or "No traceback available").splitlines()
94 return (self.traceback or "No traceback available").splitlines()
201
95
202 def _render_traceback_(self):
96 def _render_traceback_(self):
203 """Special method for custom tracebacks within IPython.
97 """Special method for custom tracebacks within IPython.
204
98
205 This will be called by IPython instead of displaying the local traceback.
99 This will be called by IPython instead of displaying the local traceback.
206
100
207 It should return a traceback rendered as a list of lines.
101 It should return a traceback rendered as a list of lines.
208 """
102 """
209 return self.render_traceback()
103 return self.render_traceback()
210
104
211 def print_traceback(self, excid=None):
105 def print_traceback(self, excid=None):
212 """print my traceback"""
106 """print my traceback"""
213 print('\n'.join(self.render_traceback()))
107 print('\n'.join(self.render_traceback()))
214
108
215
109
216
110
217
111
218 class TaskRejectError(KernelError):
112 class TaskRejectError(KernelError):
219 """Exception to raise when a task should be rejected by an engine.
113 """Exception to raise when a task should be rejected by an engine.
220
114
221 This exception can be used to allow a task running on an engine to test
115 This exception can be used to allow a task running on an engine to test
222 if the engine (or the user's namespace on the engine) has the needed
116 if the engine (or the user's namespace on the engine) has the needed
223 task dependencies. If not, the task should raise this exception. For
117 task dependencies. If not, the task should raise this exception. For
224 the task to be retried on another engine, the task should be created
118 the task to be retried on another engine, the task should be created
225 with the `retries` argument > 1.
119 with the `retries` argument > 1.
226
120
227 The advantage of this approach over our older properties system is that
121 The advantage of this approach over our older properties system is that
228 tasks have full access to the user's namespace on the engines and the
122 tasks have full access to the user's namespace on the engines and the
229 properties don't have to be managed or tested by the controller.
123 properties don't have to be managed or tested by the controller.
230 """
124 """
231
125
232
126
233 class CompositeError(RemoteError):
127 class CompositeError(RemoteError):
234 """Error for representing possibly multiple errors on engines"""
128 """Error for representing possibly multiple errors on engines"""
235 tb_limit = 4 # limit on how many tracebacks to draw
129 tb_limit = 4 # limit on how many tracebacks to draw
236
130
237 def __init__(self, message, elist):
131 def __init__(self, message, elist):
238 Exception.__init__(self, *(message, elist))
132 Exception.__init__(self, *(message, elist))
239 # Don't use pack_exception because it will conflict with the .message
133 # Don't use pack_exception because it will conflict with the .message
240 # attribute that is being deprecated in 2.6 and beyond.
134 # attribute that is being deprecated in 2.6 and beyond.
241 self.msg = message
135 self.msg = message
242 self.elist = elist
136 self.elist = elist
243 self.args = [ e[0] for e in elist ]
137 self.args = [ e[0] for e in elist ]
244
138
245 def _get_engine_str(self, ei):
139 def _get_engine_str(self, ei):
246 if not ei:
140 if not ei:
247 return '[Engine Exception]'
141 return '[Engine Exception]'
248 else:
142 else:
249 return '[%s:%s]: ' % (ei['engine_id'], ei['method'])
143 return '[%s:%s]: ' % (ei['engine_id'], ei['method'])
250
144
251 def _get_traceback(self, ev):
145 def _get_traceback(self, ev):
252 try:
146 try:
253 tb = ev._ipython_traceback_text
147 tb = ev._ipython_traceback_text
254 except AttributeError:
148 except AttributeError:
255 return 'No traceback available'
149 return 'No traceback available'
256 else:
150 else:
257 return tb
151 return tb
258
152
259 def __str__(self):
153 def __str__(self):
260 s = str(self.msg)
154 s = str(self.msg)
261 for en, ev, etb, ei in self.elist[:self.tb_limit]:
155 for en, ev, etb, ei in self.elist[:self.tb_limit]:
262 engine_str = self._get_engine_str(ei)
156 engine_str = self._get_engine_str(ei)
263 s = s + '\n' + engine_str + en + ': ' + str(ev)
157 s = s + '\n' + engine_str + en + ': ' + str(ev)
264 if len(self.elist) > self.tb_limit:
158 if len(self.elist) > self.tb_limit:
265 s = s + '\n.... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
159 s = s + '\n.... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
266 return s
160 return s
267
161
268 def __repr__(self):
162 def __repr__(self):
269 return "CompositeError(%i)" % len(self.elist)
163 return "CompositeError(%i)" % len(self.elist)
270
164
271 def render_traceback(self, excid=None):
165 def render_traceback(self, excid=None):
272 """render one or all of my tracebacks to a list of lines"""
166 """render one or all of my tracebacks to a list of lines"""
273 lines = []
167 lines = []
274 if excid is None:
168 if excid is None:
275 for (en,ev,etb,ei) in self.elist[:self.tb_limit]:
169 for (en,ev,etb,ei) in self.elist[:self.tb_limit]:
276 lines.append(self._get_engine_str(ei))
170 lines.append(self._get_engine_str(ei))
277 lines.extend((etb or 'No traceback available').splitlines())
171 lines.extend((etb or 'No traceback available').splitlines())
278 lines.append('')
172 lines.append('')
279 if len(self.elist) > self.tb_limit:
173 if len(self.elist) > self.tb_limit:
280 lines.append(
174 lines.append(
281 '... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
175 '... %i more exceptions ...' % (len(self.elist) - self.tb_limit)
282 )
176 )
283 else:
177 else:
284 try:
178 try:
285 en,ev,etb,ei = self.elist[excid]
179 en,ev,etb,ei = self.elist[excid]
286 except:
180 except:
287 raise IndexError("an exception with index %i does not exist"%excid)
181 raise IndexError("an exception with index %i does not exist"%excid)
288 else:
182 else:
289 lines.append(self._get_engine_str(ei))
183 lines.append(self._get_engine_str(ei))
290 lines.extend((etb or 'No traceback available').splitlines())
184 lines.extend((etb or 'No traceback available').splitlines())
291
185
292 return lines
186 return lines
293
187
294 def print_traceback(self, excid=None):
188 def print_traceback(self, excid=None):
295 print('\n'.join(self.render_traceback(excid)))
189 print('\n'.join(self.render_traceback(excid)))
296
190
297 def raise_exception(self, excid=0):
191 def raise_exception(self, excid=0):
298 try:
192 try:
299 en,ev,etb,ei = self.elist[excid]
193 en,ev,etb,ei = self.elist[excid]
300 except:
194 except:
301 raise IndexError("an exception with index %i does not exist"%excid)
195 raise IndexError("an exception with index %i does not exist"%excid)
302 else:
196 else:
303 raise RemoteError(en, ev, etb, ei)
197 raise RemoteError(en, ev, etb, ei)
304
198
305
199
306 def collect_exceptions(rdict_or_list, method='unspecified'):
200 def collect_exceptions(rdict_or_list, method='unspecified'):
307 """check a result dict for errors, and raise CompositeError if any exist.
201 """check a result dict for errors, and raise CompositeError if any exist.
308 Passthrough otherwise."""
202 Passthrough otherwise."""
309 elist = []
203 elist = []
310 if isinstance(rdict_or_list, dict):
204 if isinstance(rdict_or_list, dict):
311 rlist = rdict_or_list.values()
205 rlist = rdict_or_list.values()
312 else:
206 else:
313 rlist = rdict_or_list
207 rlist = rdict_or_list
314 for r in rlist:
208 for r in rlist:
315 if isinstance(r, RemoteError):
209 if isinstance(r, RemoteError):
316 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
210 en, ev, etb, ei = r.ename, r.evalue, r.traceback, r.engine_info
317 # Sometimes we could have CompositeError in our list. Just take
211 # Sometimes we could have CompositeError in our list. Just take
318 # the errors out of them and put them in our new list. This
212 # the errors out of them and put them in our new list. This
319 # has the effect of flattening lists of CompositeErrors into one
213 # has the effect of flattening lists of CompositeErrors into one
320 # CompositeError
214 # CompositeError
321 if en=='CompositeError':
215 if en=='CompositeError':
322 for e in ev.elist:
216 for e in ev.elist:
323 elist.append(e)
217 elist.append(e)
324 else:
218 else:
325 elist.append((en, ev, etb, ei))
219 elist.append((en, ev, etb, ei))
326 if len(elist)==0:
220 if len(elist)==0:
327 return rdict_or_list
221 return rdict_or_list
328 else:
222 else:
329 msg = "one or more exceptions from call to method: %s" % (method)
223 msg = "one or more exceptions from call to method: %s" % (method)
330 # This silliness is needed so the debugger has access to the exception
224 # This silliness is needed so the debugger has access to the exception
331 # instance (e in this case)
225 # instance (e in this case)
332 try:
226 try:
333 raise CompositeError(msg, elist)
227 raise CompositeError(msg, elist)
334 except CompositeError as e:
228 except CompositeError as e:
335 raise e
229 raise e
336
230
337 def wrap_exception(engine_info={}):
231 def wrap_exception(engine_info={}):
338 etype, evalue, tb = sys.exc_info()
232 etype, evalue, tb = sys.exc_info()
339 stb = traceback.format_exception(etype, evalue, tb)
233 stb = traceback.format_exception(etype, evalue, tb)
340 exc_content = {
234 exc_content = {
341 'status' : 'error',
235 'status' : 'error',
342 'traceback' : stb,
236 'traceback' : stb,
343 'ename' : unicode(etype.__name__),
237 'ename' : unicode(etype.__name__),
344 'evalue' : unicode(evalue),
238 'evalue' : unicode(evalue),
345 'engine_info' : engine_info
239 'engine_info' : engine_info
346 }
240 }
347 return exc_content
241 return exc_content
348
242
349 def unwrap_exception(content):
243 def unwrap_exception(content):
350 err = RemoteError(content['ename'], content['evalue'],
244 err = RemoteError(content['ename'], content['evalue'],
351 ''.join(content['traceback']),
245 ''.join(content['traceback']),
352 content.get('engine_info', {}))
246 content.get('engine_info', {}))
353 return err
247 return err
354
248
General Comments 0
You need to be logged in to leave comments. Login now