|
57 | 57 | import weakref |
58 | 58 | from functools import partial |
59 | 59 | import itertools |
| 60 | +import traceback |
60 | 61 |
|
61 | 62 | # Workers are created as daemon threads and processes. This is done to allow the |
62 | 63 | # interpreter to exit when there are still idle processes in a |
@@ -90,6 +91,27 @@ def _python_exit(): |
90 | 91 | # (Futures in the call queue cannot be cancelled). |
91 | 92 | EXTRA_QUEUED_CALLS = 1 |
92 | 93 |
|
| 94 | +# Hack to embed stringification of remote traceback in local traceback |
| 95 | + |
| 96 | +class _RemoteTraceback(Exception): |
| 97 | + def __init__(self, tb): |
| 98 | + self.tb = tb |
| 99 | + def __str__(self): |
| 100 | + return self.tb |
| 101 | + |
| 102 | +class _ExceptionWithTraceback: |
| 103 | + def __init__(self, exc, tb): |
| 104 | + tb = traceback.format_exception(type(exc), exc, tb) |
| 105 | + tb = ''.join(tb) |
| 106 | + self.exc = exc |
| 107 | + self.tb = '\n"""\n%s"""' % tb |
| 108 | + def __reduce__(self): |
| 109 | + return _rebuild_exc, (self.exc, self.tb) |
| 110 | + |
| 111 | +def _rebuild_exc(exc, tb): |
| 112 | + exc.__cause__ = _RemoteTraceback(tb) |
| 113 | + return exc |
| 114 | + |
93 | 115 | class _WorkItem(object): |
94 | 116 | def __init__(self, future, fn, args, kwargs): |
95 | 117 | self.future = future |
@@ -152,8 +174,8 @@ def _process_worker(call_queue, result_queue): |
152 | 174 | try: |
153 | 175 | r = call_item.fn(*call_item.args, **call_item.kwargs) |
154 | 176 | except BaseException as e: |
155 | | - result_queue.put(_ResultItem(call_item.work_id, |
156 | | - exception=e)) |
| 177 | + exc = _ExceptionWithTraceback(e, e.__traceback__) |
| 178 | + result_queue.put(_ResultItem(call_item.work_id, exception=exc)) |
157 | 179 | else: |
158 | 180 | result_queue.put(_ResultItem(call_item.work_id, |
159 | 181 | result=r)) |
|
0 commit comments