changeset: 95053:a1b5ac563d8d parent: 95051:b29342f53174 parent: 95052:f3977f34cf6c user: Antoine Pitrou date: Thu Mar 19 00:01:37 2015 +0100 files: Lib/unittest/loader.py Lib/unittest/test/test_discovery.py Misc/NEWS description: Issue #22903: The fake test case created by unittest.loader when it fails importing a test module is now picklable. diff -r b29342f53174 -r a1b5ac563d8d Lib/unittest/loader.py --- a/Lib/unittest/loader.py Wed Mar 18 22:23:40 2015 +0100 +++ b/Lib/unittest/loader.py Thu Mar 19 00:01:37 2015 +0100 @@ -20,23 +20,34 @@ VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE) +class _FailedTest(case.TestCase): + _testMethodName = None + + def __init__(self, method_name, exception): + self._exception = exception + super(_FailedTest, self).__init__(method_name) + + def __getattr__(self, name): + if name != self._testMethodName: + return super(_FailedTest, self).__getattr__(name) + def testFailure(): + raise self._exception + return testFailure + + def _make_failed_import_test(name, suiteClass): message = 'Failed to import test module: %s\n%s' % ( name, traceback.format_exc()) - return _make_failed_test('ModuleImportFailure', name, ImportError(message), - suiteClass, message) + return _make_failed_test(name, ImportError(message), suiteClass, message) def _make_failed_load_tests(name, exception, suiteClass): message = 'Failed to call load_tests:\n%s' % (traceback.format_exc(),) return _make_failed_test( - 'LoadTestsFailure', name, exception, suiteClass, message) + name, exception, suiteClass, message) -def _make_failed_test(classname, methodname, exception, suiteClass, message): - def testFailure(self): - raise exception - attrs = {methodname: testFailure} - TestClass = type(classname, (case.TestCase,), attrs) - return suiteClass((TestClass(methodname),)), message +def _make_failed_test(methodname, exception, suiteClass, message): + test = _FailedTest(methodname, exception) + return suiteClass((test,)), message def _make_skipped_test(methodname, exception, suiteClass): @case.skip(str(exception)) @@ -169,7 +180,7 @@ else: # Otherwise, we signal that an AttributeError has occurred. error_case, error_message = _make_failed_test( - 'AttributeError', part, e, self.suiteClass, + part, e, self.suiteClass, 'Failed to access attribute:\n%s' % ( traceback.format_exc(),)) self.errors.append(error_message) diff -r b29342f53174 -r a1b5ac563d8d Lib/unittest/test/test_discovery.py --- a/Lib/unittest/test/test_discovery.py Wed Mar 18 22:23:40 2015 +0100 +++ b/Lib/unittest/test/test_discovery.py Thu Mar 19 00:01:37 2015 +0100 @@ -3,6 +3,7 @@ import re import sys import types +import pickle import builtins from test import support @@ -482,6 +483,10 @@ test.my_package() self.assertEqual(import_calls, ['my_package']) + # Check picklability + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pickle.loads(pickle.dumps(test, proto)) + def test_discover_with_module_that_raises_SkipTest_on_import(self): loader = unittest.TestLoader() @@ -498,6 +503,10 @@ suite.run(result) self.assertEqual(len(result.skipped), 1) + # Check picklability + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pickle.loads(pickle.dumps(suite, proto)) + def test_discover_with_init_module_that_raises_SkipTest_on_import(self): vfs = {abspath('/foo'): ['my_package'], abspath('/foo/my_package'): ['__init__.py', 'test_module.py']} @@ -518,6 +527,10 @@ self.assertEqual(result.testsRun, 1) self.assertEqual(import_calls, ['my_package']) + # Check picklability + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + pickle.loads(pickle.dumps(suite, proto)) + def test_command_line_handling_parseArgs(self): program = TestableTestProgram() diff -r b29342f53174 -r a1b5ac563d8d Misc/NEWS --- a/Misc/NEWS Wed Mar 18 22:23:40 2015 +0100 +++ b/Misc/NEWS Thu Mar 19 00:01:37 2015 +0100 @@ -18,6 +18,9 @@ Library ------- +- Issue #22903: The fake test case created by unittest.loader when it fails + importing a test module is now picklable. + - Issue #22181: On Linux, os.urandom() now uses the new getrandom() syscall if available, syscall introduced in the Linux kernel 3.17. It is more reliable and more secure, because it avoids the need of a file descriptor and waits