Fixed #27301 -- Prevented exceptions that fail unpickling from crashing the parallel test runner.
This commit is contained in:
parent
9cfd060b1c
commit
52188a5ca6
|
@ -89,6 +89,14 @@ class RemoteTestResult(object):
|
|||
def test_index(self):
|
||||
return self.testsRun - 1
|
||||
|
||||
def _confirm_picklable(self, obj):
|
||||
"""
|
||||
Confirm that obj can be pickled and unpickled as multiprocessing will
|
||||
need to pickle the exception in the child process and unpickle it in
|
||||
the parent process. Let the exception rise, if not.
|
||||
"""
|
||||
pickle.loads(pickle.dumps(obj))
|
||||
|
||||
def _print_unpicklable_subtest(self, test, subtest, pickle_exc):
|
||||
print("""
|
||||
Subtest failed:
|
||||
|
@ -113,7 +121,7 @@ with a cleaner failure message.
|
|||
# with the multiprocessing module. Since we're in a forked process,
|
||||
# our best chance to communicate with them is to print to stdout.
|
||||
try:
|
||||
pickle.dumps(err)
|
||||
self._confirm_picklable(err)
|
||||
except Exception as exc:
|
||||
original_exc_txt = repr(err[1])
|
||||
original_exc_txt = textwrap.fill(original_exc_txt, 75, initial_indent=' ', subsequent_indent=' ')
|
||||
|
@ -154,7 +162,7 @@ failure and get a correct traceback.
|
|||
|
||||
def check_subtest_picklable(self, test, subtest):
|
||||
try:
|
||||
pickle.dumps(subtest)
|
||||
self._confirm_picklable(subtest)
|
||||
except Exception as exc:
|
||||
self._print_unpicklable_subtest(test, subtest, exc)
|
||||
raise
|
||||
|
|
|
@ -10,6 +10,15 @@ except ImportError:
|
|||
tblib = None
|
||||
|
||||
|
||||
class ExceptionThatFailsUnpickling(Exception):
|
||||
"""
|
||||
After pickling, this class fails unpickling with an error about incorrect
|
||||
arguments passed to __init__().
|
||||
"""
|
||||
def __init__(self, arg):
|
||||
super(ExceptionThatFailsUnpickling, self).__init__()
|
||||
|
||||
|
||||
class ParallelTestRunnerTest(SimpleTestCase):
|
||||
"""
|
||||
End-to-end tests of the parallel test runner.
|
||||
|
@ -44,6 +53,19 @@ class SampleFailingSubtest(SimpleTestCase):
|
|||
|
||||
class RemoteTestResultTest(SimpleTestCase):
|
||||
|
||||
def test_pickle_errors_detection(self):
|
||||
picklable_error = RuntimeError('This is fine')
|
||||
not_unpicklable_error = ExceptionThatFailsUnpickling('arg')
|
||||
|
||||
result = RemoteTestResult()
|
||||
result._confirm_picklable(picklable_error)
|
||||
|
||||
msg = '__init__() missing 1 required positional argument'
|
||||
if six.PY2:
|
||||
msg = '__init__() takes exactly 2 arguments (1 given)'
|
||||
with self.assertRaisesMessage(TypeError, msg):
|
||||
result._confirm_picklable(not_unpicklable_error)
|
||||
|
||||
@unittest.skipUnless(six.PY3 and tblib is not None, 'requires tblib to be installed')
|
||||
def test_add_failing_subtests(self):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue