Preserve tracebacks from run_in_thread
Now the traceback goes all the way down to where the exception came from, not just down to run_in_thread. Better for debugging. Change-Id: Iac6acb843a6ecf51ea2672a563d80fa43d731f23
This commit is contained in:
parent
3c82cfc7a9
commit
16204c706d
@ -2233,8 +2233,8 @@ class ThreadPool(object):
|
|||||||
try:
|
try:
|
||||||
result = func(*args, **kwargs)
|
result = func(*args, **kwargs)
|
||||||
result_queue.put((ev, True, result))
|
result_queue.put((ev, True, result))
|
||||||
except BaseException as err:
|
except BaseException:
|
||||||
result_queue.put((ev, False, err))
|
result_queue.put((ev, False, sys.exc_info()))
|
||||||
finally:
|
finally:
|
||||||
work_queue.task_done()
|
work_queue.task_done()
|
||||||
os.write(self.wpipe, self.BYTE)
|
os.write(self.wpipe, self.BYTE)
|
||||||
@ -2264,7 +2264,7 @@ class ThreadPool(object):
|
|||||||
if success:
|
if success:
|
||||||
ev.send(result)
|
ev.send(result)
|
||||||
else:
|
else:
|
||||||
ev.send_exception(result)
|
ev.send_exception(*result)
|
||||||
finally:
|
finally:
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ from textwrap import dedent
|
|||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
import unittest
|
import unittest
|
||||||
import fcntl
|
import fcntl
|
||||||
import shutil
|
import shutil
|
||||||
@ -2578,13 +2579,8 @@ class TestThreadpool(unittest.TestCase):
|
|||||||
result = tp.force_run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
result = tp.force_run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
||||||
self.assertEquals(result, {'args': (1, 2),
|
self.assertEquals(result, {'args': (1, 2),
|
||||||
'kwargs': {'bert': 'ernie'}})
|
'kwargs': {'bert': 'ernie'}})
|
||||||
|
self.assertRaises(ValueError, tp.force_run_in_thread,
|
||||||
caught = False
|
self._raise_valueerror)
|
||||||
try:
|
|
||||||
tp.force_run_in_thread(self._raise_valueerror)
|
|
||||||
except ValueError:
|
|
||||||
caught = True
|
|
||||||
self.assertTrue(caught)
|
|
||||||
|
|
||||||
def test_run_in_thread_without_threads(self):
|
def test_run_in_thread_without_threads(self):
|
||||||
# with zero threads, run_in_thread doesn't actually do so
|
# with zero threads, run_in_thread doesn't actually do so
|
||||||
@ -2597,13 +2593,8 @@ class TestThreadpool(unittest.TestCase):
|
|||||||
result = tp.run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
result = tp.run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
||||||
self.assertEquals(result, {'args': (1, 2),
|
self.assertEquals(result, {'args': (1, 2),
|
||||||
'kwargs': {'bert': 'ernie'}})
|
'kwargs': {'bert': 'ernie'}})
|
||||||
|
self.assertRaises(ValueError, tp.run_in_thread,
|
||||||
caught = False
|
self._raise_valueerror)
|
||||||
try:
|
|
||||||
tp.run_in_thread(self._raise_valueerror)
|
|
||||||
except ValueError:
|
|
||||||
caught = True
|
|
||||||
self.assertTrue(caught)
|
|
||||||
|
|
||||||
def test_force_run_in_thread_without_threads(self):
|
def test_force_run_in_thread_without_threads(self):
|
||||||
# with zero threads, force_run_in_thread uses eventlet.tpool
|
# with zero threads, force_run_in_thread uses eventlet.tpool
|
||||||
@ -2616,12 +2607,36 @@ class TestThreadpool(unittest.TestCase):
|
|||||||
result = tp.force_run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
result = tp.force_run_in_thread(self._capture_args, 1, 2, bert='ernie')
|
||||||
self.assertEquals(result, {'args': (1, 2),
|
self.assertEquals(result, {'args': (1, 2),
|
||||||
'kwargs': {'bert': 'ernie'}})
|
'kwargs': {'bert': 'ernie'}})
|
||||||
caught = False
|
self.assertRaises(ValueError, tp.force_run_in_thread,
|
||||||
|
self._raise_valueerror)
|
||||||
|
|
||||||
|
def test_preserving_stack_trace_from_thread(self):
|
||||||
|
def gamma():
|
||||||
|
return 1 / 0 # ZeroDivisionError
|
||||||
|
|
||||||
|
def beta():
|
||||||
|
return gamma()
|
||||||
|
|
||||||
|
def alpha():
|
||||||
|
return beta()
|
||||||
|
|
||||||
|
tp = utils.ThreadPool(1)
|
||||||
try:
|
try:
|
||||||
tp.force_run_in_thread(self._raise_valueerror)
|
tp.run_in_thread(alpha)
|
||||||
except ValueError:
|
except ZeroDivisionError:
|
||||||
caught = True
|
# NB: format is (filename, line number, function name, text)
|
||||||
self.assertTrue(caught)
|
tb_func = [elem[2] for elem
|
||||||
|
in traceback.extract_tb(sys.exc_traceback)]
|
||||||
|
else:
|
||||||
|
self.fail("Expected ZeroDivisionError")
|
||||||
|
|
||||||
|
self.assertEqual(tb_func[-1], "gamma")
|
||||||
|
self.assertEqual(tb_func[-2], "beta")
|
||||||
|
self.assertEqual(tb_func[-3], "alpha")
|
||||||
|
# omit the middle; what's important is that the start and end are
|
||||||
|
# included, not the exact names of helper methods
|
||||||
|
self.assertEqual(tb_func[1], "run_in_thread")
|
||||||
|
self.assertEqual(tb_func[0], "test_preserving_stack_trace_from_thread")
|
||||||
|
|
||||||
|
|
||||||
class TestAuditLocationGenerator(unittest.TestCase):
|
class TestAuditLocationGenerator(unittest.TestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user