242
243
raise NotImplementedError
245
class ThreadWithException(threading.Thread):
246
"""A catching exception thread.
248
If an exception occurs during the thread execution, it's caught and
249
re-raised when the thread is joined().
252
def __init__(self, *args, **kwargs):
253
# There are cases where the calling thread must wait, yet, if an
254
# exception occurs, the event should be set so the caller is not
255
# blocked. The main example is a calling thread that want to wait for
256
# the called thread to be in a given state before continuing.
258
event = kwargs.pop('event')
260
# If the caller didn't pass a specific event, create our own
261
event = threading.Event()
262
super(ThreadWithException, self).__init__(*args, **kwargs)
263
self.set_ready_event(event)
264
self.exception = None
265
self.ignored_exceptions = None # see set_ignored_exceptions
267
# compatibility thunk for python-2.4 and python-2.5...
268
if sys.version_info < (2, 6):
269
name = property(threading.Thread.getName, threading.Thread.setName)
271
def set_ready_event(self, event):
272
"""Set the ``ready`` event used to synchronize exception catching.
274
When the thread uses an event to synchronize itself with another thread
275
(setting it when the other thread can wake up from a ``wait`` call),
276
the event must be set after catching an exception or the other thread
279
Some threads require multiple events and should set the relevant one
284
def set_ignored_exceptions(self, ignored):
285
"""Declare which exceptions will be ignored.
287
:param ignored: Can be either:
288
- None: all exceptions will be raised,
289
- an exception class: the instances of this class will be ignored,
290
- a tuple of exception classes: the instances of any class of the
291
list will be ignored,
292
- a callable: that will be passed the exception object
293
and should return True if the exception should be ignored
296
self.ignored_exceptions = None
297
elif isinstance(ignored, (Exception, tuple)):
298
self.ignored_exceptions = lambda e: isinstance(e, ignored)
300
self.ignored_exceptions = ignored
303
"""Overrides Thread.run to capture any exception."""
307
super(ThreadWithException, self).run()
309
self.exception = sys.exc_info()
311
# Make sure the calling thread is released
246
class TestThread(cethread.CatchingExceptionThread):
315
248
def join(self, timeout=5):
316
"""Overrides Thread.join to raise any exception caught.
319
Calling join(timeout=0) will raise the caught exception or return None
320
if the thread is still alive.
249
"""Overrides to use a default timeout.
322
251
The default timeout is set to 5 and should expire only when a thread
323
252
serving a client connection is hung.
325
super(ThreadWithException, self).join(timeout)
326
if self.exception is not None:
327
exc_class, exc_value, exc_tb = self.exception
328
self.exception = None # The exception should be raised only once
329
if (self.ignored_exceptions is None
330
or not self.ignored_exceptions(exc_value)):
331
# Raise non ignored exceptions
332
raise exc_class, exc_value, exc_tb
254
super(TestThread, self).join(timeout)
333
255
if timeout and self.isAlive():
334
256
# The timeout expired without joining the thread, the thread is
335
257
# therefore stucked and that's a failure as far as the test is