From f1f99b5175ce5606217eae4b10428842060d675a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Wed, 7 Oct 2009 20:23:55 +0200 Subject: [PATCH] use os.fork() when running doctests to prevent tested modules from staying in sys.modules --- runtests.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/runtests.py b/runtests.py index ce1cc7bd..f5e53405 100644 --- a/runtests.py +++ b/runtests.py @@ -8,6 +8,13 @@ import shutil import unittest import doctest import operator +from StringIO import StringIO + +try: + import cPickle as pickle +except ImportError: + import pickle + WITH_CYTHON = True @@ -363,7 +370,7 @@ class CythonRunTestCase(CythonCompileTestCase): self.setUp() self.runCompileTest() if not self.cython_only: - doctest.DocTestSuite(self.module).run(result) + self.run_doctests(self.module, result) except Exception: result.addError(self, sys.exc_info()) result.stopTest(self) @@ -372,6 +379,66 @@ class CythonRunTestCase(CythonCompileTestCase): except Exception: pass + def run_doctests(self, module_name, result): + if not hasattr(os, 'fork'): + doctest.DocTestSuite(module_name).run(result) + return + + # fork to make sure we do not keep the tested module loaded + input, output = os.pipe() + child_id = os.fork() + if not child_id: + result_code = 0 + try: + output = os.fdopen(output, 'wb') + try: + partial_result = PartialTestResult(result) + doctest.DocTestSuite(module_name).run(partial_result) + except Exception, e: + partial_result.addError(module_name, sys.exc_info()) + result_code = 1 + pickle.dump(partial_result.data(), output) + finally: + try: output.close() + except: pass + os._exit(result_code) + + input = os.fdopen(input, 'rb') + PartialTestResult.join_results(result, pickle.load(input)) + cid, result_code = os.waitpid(child_id, 0) + if result_code: + raise Exception("Tests in module '%s' exited with status %d" % + (module_name, result_code >> 8)) + + +class PartialTestResult(unittest._TextTestResult): + def __init__(self, base_result): + unittest._TextTestResult.__init__( + self, self._StringIO(), True, + base_result.dots + base_result.showAll*2) + + def data(self): + return (self.failures, self.errors, self.testsRun, + self.stream.getvalue()) + + def join_results(result, data): + """Static method for merging the result back into the main + result object. + """ + errors, failures, tests_run, output = data + if output: + result.stream.write(output) + result.errors.extend(errors) + result.failures.extend(failures) + result.testsRun += tests_run + + join_results = staticmethod(join_results) + + class _StringIO(StringIO): + def writeln(self, line): + self.write("%s\n" % line) + + class CythonUnitTestCase(CythonCompileTestCase): def shortDescription(self): return "compiling (%s) tests in %s" % (self.language, self.module) @@ -621,6 +688,8 @@ if __name__ == '__main__': if WITH_CYTHON: from Cython.Compiler.Version import version sys.stderr.write("Running tests against Cython %s\n" % version) + from Cython.Compiler import Options + #Options.generate_cleanup_code = 3 # complete cleanup code from Cython.Compiler import DebugFlags DebugFlags.debug_temp_code_comments = 1 else: -- 2.26.2