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