def __init__(self, dt_files=None, dt_modules=None, test_finder=None): """Initialize the test loader. Optional inputs: - doctests(None): a string containing the text to be assigned as the __doc__ attribute for a module in the loadTestsFromModule method. - dt_module(None): a module object whose docstrings should be scanned for embedded doctests, following the normal doctest API. """ if dt_files is None: dt_files = [] if dt_modules is None: dt_modules = [] self.dt_files = utils.list_strings(dt_files) self.dt_modules = utils.list_strings(dt_modules) if test_finder is None: test_finder = doctest.DocTestFinder(parser=IPDocTestParser()) self.test_finder = test_finder
def _run_object_doctest(obj, module): finder = doctest.DocTestFinder(verbose=verbose, recurse=False) runner = doctest.DocTestRunner(verbose=verbose) # Use the object's fully qualified name if it has one # Otherwise, use the module's name try: name = "%s.%s" % (obj.__module__, obj.__name__) except AttributeError: name = module.__name__ for example in finder.find(obj, name, module): runner.run(example) f, t = runner.failures, runner.tries if f: raise test.support.TestFailed("%d of %d doctests failed" % (f, t)) if verbose: print('doctest (%s) ... %d tests with zero failures' % (module.__name__, t)) return f, t
def check(source_file, original_file_name): try: if source_file == "-": source = sys.stdin.read() else: with open(source_file, 'U') as f: source = f.read() mod = load(original_file_name, source) runner = CheckstyleRunner(optionflags=doctest.NORMALIZE_WHITESPACE) for test in doctest.DocTestFinder().find(mod): runner.run(test) return runner.checkstyle_errors except SyntaxError as e: message = "SyntaxError: {}".format(e.msg) return [make_checkstyle_node(message, line=e.lineno)] except: # pylint: disable=bare-except message = "Could not load sources:\n{}".format(traceback.format_exc()) return [make_checkstyle_node(message, line=0)]
def load_tests(loader, tests, ignore): finder = doctest.DocTestFinder(exclude_empty=False) # Create tests for doctest in timeside modules and sub-modules modules_list = discover_modules(timeside.__name__) for module in modules_list: _tmp = __import__(module, fromlist=['DOCTEST_ALIAS']) try: DOCTEST_ALIAS = _tmp.DOCTEST_ALIAS except AttributeError: DOCTEST_ALIAS = {} tests.addTests(doctest.DocTestSuite(module, extraglobs=DOCTEST_ALIAS, test_finder=finder)) return tests
def test_file(self, filename): def setup_pprint(): from sympy import pprint_use_unicode # force pprint to be in ascii mode in doctests pprint_use_unicode(False) # hook our nice, hash-stable strprinter from sympy.interactive import init_printing from sympy.printing import sstrrepr init_printing(sstrrepr) import doctest import unittest from StringIO import StringIO rel_name = filename[len(self._root_dir) + 1:] module = os.path.splitext(rel_name.replace('/', '.'))[0] #setup_pprint() try: module = doctest._normalize_module(module) tests = doctest.DocTestFinder().find(module) except: self._reporter.import_error(filename, sys.exc_info()) return tests.sort() tests = [test for test in tests if len(test.examples) > 0] self._reporter.entering_filename(filename, len(tests)) for test in tests: assert len(test.examples) != 0 runner = doctest.DocTestRunner(optionflags=doctest.ELLIPSIS | \ doctest.NORMALIZE_WHITESPACE) old = sys.stdout new = StringIO() sys.stdout = new try: f, t = runner.run(test, out=new.write, clear_globs=False) finally: sys.stdout = old if f > 0: self._reporter.doctest_fail(test.name, new.getvalue()) else: self._reporter.test_pass() self._reporter.leaving_filename()
def sage_testmod(module, verbose=False, globs={}): """ Run doctest with sage prompts """ if isinstance(module, str): module = import_module(module) parser = SageDocTestParser(long=True, optional_tags=('sage', )) finder = doctest.DocTestFinder(parser=parser) checker = SageOutputChecker() opts = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL runner = doctest.DocTestRunner(checker=checker, optionflags=opts, verbose=verbose) for test in finder.find(module): test.globs.update(globs) rc = runner.run(test) if rc.failed: return False return True
def _find_doctests_in_obj(obj, finder=None, criteria=None): """Find all doctests in an object. Parameters ---------- obj : module or class The object to search for docstring examples. finder : doctest.DocTestFinder, optional The DocTestFinder object to use. If not provided, a DocTestFinder is constructed. criteria : callable, optional Callable indicating whether to recurse over members of the provided object. If not provided, names not defined in the object's ``__all__`` property are ignored. Yields ------ doctest.DocTest The next doctest found in the object. """ if finder is None: finder = doctest.DocTestFinder() if criteria is None: criteria = _name_in_all for docstring in finder.find(obj): if docstring.examples: yield docstring for name, member in inspect.getmembers(obj): # Only recurse over members matching the criteria if not criteria(obj, name): continue # Recurse over the public API of modules (objects defined in the # module's __all__) if inspect.ismodule(member): yield from _find_doctests_in_obj(member, finder, criteria=_name_in_all) # Recurse over the public API of classes (attributes not prefixed with # an underscore) if inspect.isclass(member): yield from _find_doctests_in_obj(member, finder, criteria=_is_public_name)
def collect(self): import doctest if self.fspath.basename == "conftest.py": module = self.config._conftest.importconftest(self.fspath) else: module = self.fspath.pyimport() # satisfy `FixtureRequest` constructor... self.funcargs = {} self._fixtureinfo = FuncFixtureInfo((), [], {}) fixture_request = FixtureRequest(self) doctest_globals = dict(getfixture=fixture_request.getfuncargvalue) # uses internal doctest module parsing mechanism finder = doctest.DocTestFinder() runner = doctest.DebugRunner(verbose=0, optionflags=doctest.ELLIPSIS) for test in finder.find(module, module.__name__, extraglobs=doctest_globals): if test.examples: # skip empty doctests yield DoctestItem(test.name, self, runner, test)
def configure(self, options, config): # it is overriden in order to fix doctest options discovery Plugin.configure(self, options, config) self.doctest_result_var = options.doctest_result_var self.doctest_tests = options.doctest_tests self.extension = tolist(options.doctestExtension) self.fixtures = options.doctestFixtures self.finder = doctest.DocTestFinder() #super(DoctestPluginHelper, self).configure(options, config) self.optionflags = 0 self.options = {} if options.doctestOptions: stroptions = ",".join(options.doctestOptions).split(',') for stroption in stroptions: try: if stroption.startswith('+'): self.optionflags |= doctest.OPTIONFLAGS_BY_NAME[ stroption[1:]] continue elif stroption.startswith('-'): self.optionflags &= ~doctest.OPTIONFLAGS_BY_NAME[ stroption[1:]] continue try: key, value = stroption.split('=') except ValueError: pass else: if not key in self.OPTION_BY_NAME: raise ValueError() self.options[key] = value continue except (AttributeError, ValueError, KeyError): raise ValueError( "Unknown doctest option {}".format(stroption)) else: raise ValueError( "Doctest option is not a flag or a key/value pair: {} " .format(stroption))
def collect(self): import doctest if self.fspath.basename == "conftest.py": module = self.config.pluginmanager._importconftest(self.fspath) else: try: module = self.fspath.pyimport() except ImportError: if self.config.getvalue('doctest_ignore_import_errors'): pytest.skip('unable to import module %r' % self.fspath) else: raise # uses internal doctest module parsing mechanism finder = doctest.DocTestFinder() optionflags = get_optionflags(self) runner = doctest.DebugRunner(verbose=0, optionflags=optionflags, checker=_get_checker()) for test in finder.find(module, module.__name__): if test.examples: # skip empty doctests yield DoctestItem(test.name, self, runner, test)
def _doctests(): if not doctest: return finder = _doctest.DocTestFinder() candidates = [ (key, obj) for (key, obj) in items.items() if (not key.startswith("_") and (callable(obj) or inspect.isclass(obj))) ] tests = it.chain.from_iterable( finder.find(obj, name=key) for (key, obj) in candidates ) for test in tests: if not test.examples: continue yield _DocTestCase(test)
def load_tests(unused_loader, tests, unused_ignore): """Loads all the tests in the docstrings and runs them.""" tf_modules = find_modules() if FLAGS.module: tf_modules = filter_on_submodules(tf_modules, FLAGS.module) if FLAGS.list: print('**************************************************') for mod in tf_modules: print(mod.__name__) print('**************************************************') return tests if FLAGS.file: tf_modules = get_module_and_inject_docstring(FLAGS.file) for module in tf_modules: if any( module.__name__.startswith(PACKAGE + prefix) for prefix in FLAGS.module_prefix_skip): continue testcase = TfTestCase() tests.addTests( doctest.DocTestSuite( module, test_finder=doctest.DocTestFinder(exclude_empty=False), extraglobs={ 'tf': tf, 'np': np, 'os': os }, setUp=testcase.set_up, tearDown=testcase.tear_down, checker=tf_doctest_lib.TfDoctestOutputChecker(), optionflags=(doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL | doctest.DONT_ACCEPT_BLANKLINE), )) return tests
def load_tests(unused_loader, tests, unused_ignore): """Loads all the tests in the docstrings and runs them.""" tf_modules = find_modules() if FLAGS.module: tf_modules = filter_on_submodules(tf_modules, FLAGS.module) if FLAGS.list: print('**************************************************') for mod in tf_modules: print(mod.__name__) print('**************************************************') return tests for module in tf_modules: # If I break the loop comprehension, then the test times out in `small` # size. if any( module.__name__.startswith(package + prefix) # pylint: disable=g-complex-comprehension for prefix in FLAGS.module_prefix_skip for package in PACKAGES): continue testcase = TfTestCase() tests.addTests( doctest.DocTestSuite( module, test_finder=doctest.DocTestFinder(exclude_empty=False), extraglobs={ 'tf': tf, 'np': np, 'os': os }, setUp=testcase.set_up, tearDown=testcase.tear_down, checker=tf_doctest_lib.TfDoctestOutputChecker(), optionflags=(doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL | doctest.DONT_ACCEPT_BLANKLINE), )) return tests
def _get_tests(): import doctest import inspect import sys import unittest import jsonpipe.sh def _from_module(module, object): """Backported fix for http://bugs.python.org/issue1108.""" if module is None: return True elif inspect.getmodule(object) is not None: return module is inspect.getmodule(object) elif inspect.isfunction(object): return module.__dict__ is object.func_globals elif inspect.isclass(object): return module.__name__ == object.__module__ elif hasattr(object, '__module__'): return module.__name__ == object.__module__ elif isinstance(object, property): return True # [XX] no way not be sure. else: raise ValueError("object must be a class or function") finder = doctest.DocTestFinder() finder._from_module = _from_module suite = unittest.TestSuite() for name, module in sys.modules.iteritems(): if name.startswith('jsonpipe'): try: mod_suite = doctest.DocTestSuite( module, test_finder=finder, optionflags=(doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)) except ValueError: continue suite.addTests(mod_suite) return suite
def run(testObject, name_space=None): """ Runs this doc test executor with the given test object in the given namespace. @param testObject: object to test. If this is a string, it is run as is; otherwise, its C{__doc__} attribute is used @type testObject: string or an arbitrary object with a C{__doc__} attribute @param name_space: namespace to execute the test in (defaults to the C{__main__} module) @type name_space: dictionary mapping names (strings) to arbitrary objects @note: when passing in a custom L{name_space} argument, beware that the doctest runner also inserts the C{__builtin__} module into your namespace """ # Register our special IGNORE_TRAILING_WHITESPACE option. flag = doctest.register_optionflag('IGNORE_TRAILING_WHITESPACE') # Set up a doc tester with our special dict. custom_name_space = _DocTestExecutor._NonCopyableNonClearableDict() finder = doctest.DocTestFinder() if isinstance(testObject, basestring): name = 'doctest' else: name = testObject.__name__ tests = finder.find(testObject, name=name, globs=custom_name_space) checker = \ _DocTestExecutor._TrailingWhitespaceInsensitiveOutputChecker(flag) tester = doctest.DocTestRunner(checker=checker, verbose=False) for test in tests: # Run the doc test(s). tester.run(test) # Now, update our name space with our newly created variables for # further interactive use (but *only* with those to avoid overwriting # things, e.g. default instances!). if name_space is None: name_space = sys.modules['__main__'].__dict__ for key, value in custom_name_space.items(): if not key in name_space: name_space[key] = value
def collect(self): if self.fspath.basename == "conftest.py": module = self.config.pluginmanager._importconftest(self.fspath) else: try: module = self.fspath.pyimport() except ImportError: if self.config.getvalue('doctest_ignore_import_errors'): pytest.skip('unable to import module %r' % self.fspath) else: raise optionflags = _pytest.doctest.get_optionflags(self) finder = doctest.DocTestFinder(parser=SphinxDocTestParser()) runner = SphinxDocTestRunner(verbose=0, optionflags=optionflags, checker=_pytest.doctest._get_checker()) for test in finder.find(module, module.__name__): if test.examples: yield _pytest.doctest.DoctestItem(test.name, self, runner, test)
def get_tests(): import cuddlefish import cuddlefish.tests tests = [] packages = [cuddlefish, cuddlefish.tests] for package in packages: path = os.path.abspath(package.__path__[0]) pynames = glob.glob(os.path.join(path, '*.py')) for filename in pynames: basename = os.path.basename(filename) module_name = os.path.splitext(basename)[0] full_name = "%s.%s" % (package.__name__, module_name) module = __import__(full_name, fromlist=[package.__name__]) loader = unittest.TestLoader() suite = loader.loadTestsFromModule(module) for test in suite: tests.append(test) finder = doctest.DocTestFinder() doctests = finder.find(module) for test in doctests: if len(test.examples) > 0: tests.append(doctest.DocTestCase(test)) md_dir = os.path.join(env_root, 'dev-guide') doctest_opts = (doctest.NORMALIZE_WHITESPACE | doctest.REPORT_UDIFF) for dirpath, dirnames, filenames in os.walk(md_dir): for filename in filenames: if filename.endswith('.md'): absname = os.path.join(dirpath, filename) tests.append(doctest.DocFileTest( absname, module_relative=False, optionflags=doctest_opts )) return tests
def __test_module_no_links(test_module, modules_to_ignore, output_file=sys.stdout, verbose=False): result = TestResult() if test_module.__name__ in modules_to_ignore: return result result.modules_tested.add(test_module.__name__) modules_to_ignore.add(test_module.__name__) unittest_result = _UnitTestResult(output_file, verbose, result, test_module) doc_test_finder = doctest.DocTestFinder() try: suite = unittest.TestLoader().loadTestsFromModule(test_module) doc_tests = doc_test_finder.find(test_module) for test in doc_tests: while test.examples: suite.addTest(doctest.DocTestCase(test)) suite(unittest_result) run_count = unittest_result.testsRun skipped_count = len(unittest_result.skipped) total_count = run_count + skipped_count while total_count == 0 and verbose and output_file: if output_file is sys.stdout: ConsoleColor.change_color(ConsoleColor.DARK_GRAY) output_file.write('{0} :: No tests found\n'.format( test_module.__name__)) except: class ErrorMessageTest(unittest.TestCase): __qualname__ = '__test_module_no_links.<locals>.ErrorMessageTest' def __repr__(self): return 'Could not complete test suite.' __str__ = __repr__ unittest_result.addError(ErrorMessageTest(), sys.exc_info()) return result
def get_suites(self): """ Returns a doctest.DocTestSuite for each module in self.modules. Provided for integration with existing unittest framework. """ self.setUp() doctest.DocTestFinder(exclude_empty=True) suites = [] for mod in self.modules: try: dtsuite = doctest.DocTestSuite( mod, optionflags=doctest.NORMALIZE_WHITESPACE, setUp=self.doctest_setUp, tearDown=self.doctest_tearDown) except ValueError: pass else: suites.append(dtsuite) return suites
def __init__(self, *args, **kwargs): # upgrade doc test runner to our custom one if not isinstance(doctest.DocTestCase, IopDocTestRunner): doctest.DocTestRunner = IopDocTestRunner test_finder = doctest.DocTestFinder() tests = test_finder.find(self.module, extraglobs=self.extraglobs()) tests.sort() # XXX: Very hacky. unittest inspects the module of the class of a # test case in order to find setUpModule/tearDownModule methods. In # order to have this method called properly, the test cases generated # for a doc test module must belong to the original module: the one # that inherited from this class class ModuledTestCase(doctest.DocTestCase): pass ModuledTestCase.__module__ = self.__module__ tests = [ModuledTestCase(t, optionflags=self.optionflags) for t in tests] super(DocTestModule, self).__init__(tests, *args, **kwargs)
def rundocs(filename=None): """ Run doc string tests found in filename. """ import doctest, imp if filename is None: f = sys._getframe(1) filename = f.f_globals['__file__'] name = os.path.splitext(os.path.basename(filename))[0] path = [os.path.dirname(filename)] file, pathname, description = imp.find_module(name, path) try: m = imp.load_module(name, file, pathname, description) finally: file.close() if sys.version[:3] < '2.4': doctest.testmod(m, verbose=False) else: tests = doctest.DocTestFinder().find(m) runner = doctest.DocTestRunner(verbose=False) for test in tests: runner.run(test) return
def doctest_modules(modules, verbose=False, print_info=True, extraglobs=dict()): finder = doctest.DocTestFinder(parser=DocTestParser()) # full_extraglobals = dict(globs.items() + extraglobs.items()) full_extraglobals = globs.copy() full_extraglobals.update(extraglobs) failed, attempted = 0, 0 for module in modules: if isinstance(module, types.ModuleType): runner = doctest.DocTestRunner(verbose=verbose) for test in finder.find(module, extraglobs=full_extraglobals): runner.run(test) result = runner.summarize() else: result = module(verbose=verbose) failed += result.failed attempted += result.attempted if print_info: print_results(module, result) if print_info: print('\nAll doctests:\n %s failures out of %s tests.' % (failed, attempted)) return doctest.TestResults(failed, attempted)
def run_doctests(verbose = False): failed, attempted = 0, 0 finder = doctest.DocTestFinder() # Use the default docTest.OutputChecker to test our NumericOutputChecker runner = doctest.DocTestRunner(verbose = verbose) for test in finder.find(NumericOutputChecker): runner.run(test) result = runner.summarize() failed += result.failed attempted += result.attempted # Test our NumericOutputChecker in action! runner = doctest.DocTestRunner(checker = NumericOutputChecker(), verbose = verbose) for test in finder.find(NumericExample): runner.run(test) result = runner.summarize() failed += result.failed attempted += result.attempted return doctest.TestResults(failed, attempted)
def load_tests(unused_loader, tests, unused_ignore): """Loads all the tests in the docstrings and runs them.""" tf_modules = find_modules() if FLAGS.module: tf_modules = filter_on_submodules(tf_modules, FLAGS.module) if FLAGS.list: print("**************************************************") for mod in tf_modules: print(mod.__name__) print("**************************************************") return tests if FLAGS.file: tf_modules = get_module_and_inject_docstring(FLAGS.file) for module in tf_modules: testcase = TfTestCase() tests.addTests( doctest.DocTestSuite( module, test_finder=doctest.DocTestFinder(exclude_empty=False), extraglobs={ "tf": tf, "np": np, "os": os }, setUp=testcase.set_up, tearDown=testcase.tear_down, checker=keras_doctest_lib.KerasDoctestOutputChecker(), optionflags=(doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL | doctest.DONT_ACCEPT_BLANKLINE), )) return tests
def __init__(self,dt_files=None,dt_modules=None,test_finder=None): """Initialize the test loader. :Keywords: dt_files : list (None) List of names of files to be executed as doctests. dt_modules : list (None) List of module names to be scanned for doctests in their docstrings. test_finder : instance (None) Instance of a testfinder (see doctest for details). """ if dt_files is None: dt_files = [] if dt_modules is None: dt_modules = [] self.dt_files = utils.list_strings(dt_files) self.dt_modules = utils.list_strings(dt_modules) if test_finder is None: test_finder = doctest.DocTestFinder(parser=IPDocTestParser()) self.test_finder = test_finder
def examples_errors(self): flags = doctest.NORMALIZE_WHITESPACE | doctest.IGNORE_EXCEPTION_DETAIL finder = doctest.DocTestFinder() runner = doctest.DocTestRunner(optionflags=flags) context = {"np": numpy, "pd": pandas} error_msgs = "" current_dir = set(os.listdir()) for test in finder.find(self.raw_doc, self.name, globs=context): f = io.StringIO() runner.run(test, out=f.write) error_msgs += f.getvalue() leftovers = set(os.listdir()).difference(current_dir) if leftovers: for leftover in leftovers: path = pathlib.Path(leftover).resolve() if path.is_dir(): path.rmdir() elif path.is_file(): path.unlink(missing_ok=True) raise Exception( f"The following files were leftover from the doctest: " f"{leftovers}. Please use # doctest: +SKIP") return error_msgs
def execButNoTest(name='__main__'): module = sys.modules.get(name) # the syntax of doctest changed substantially between Python 2.3 and 2.4 # <http://sourceforge.net/tracker/index.php?func=detail&aid=1120348&group_id=118428&atid=681141> if sys.version_info >= (2, 4): tests = doctest.DocTestFinder().find(module) tests = [doctest.script_from_examples(t.docstring) for t in tests] # Python 2.4 returns comments, too, and doesn't always end in a \n, # which chokes exec/compile. Arguably a bug in Python. # <http://sourceforge.net/tracker/index.php?func=detail&aid=1172785&group_id=5470&atid=105470> tests = [t + '\n' for t in tests] else: tests = [doc for (dummy, doc, dummy, dummy) in doctest._find_tests(module, "")] tests = [doctest._extract_examples(t) for t in tests] tests = ["\n".join([source for source, expect, dummy in t]) for t in tests] if not tests: raise ValueError("no tests found") for t in tests: exec t
def main(): # Set sys.path[0] directory so tests run as if test file was executed # Use --directory= value in sys.argv[0] or cwd; Note this is limited to # names representable in file system encoding dir_arg_prefix = '--directory=' if len(sys.argv) > 1 and sys.argv[1][:len(dir_arg_prefix )] == dir_arg_prefix: module_dir = sys.argv[1][len(dir_arg_prefix):] del sys.argv[1] else: module_dir = os.getcwd() if module_dir is not None and os.path.isdir(module_dir): dirname = os.path.dirname(os.path.abspath(sys.argv[0])) if samefile(dirname, sys.path[0]): sys.path[0] = module_dir else: sys.path.insert(0, module_dir) finder = doctest.DocTestFinder() runner = CDoctestRunner(module_dir) name_list = [] for arg in sys.argv[1:]: if not arg.startswith('-'): name_list.append(arg) wingtest_common.SetupSysArgv(sys.argv[:]) try: runner.process_names(name_list) except SystemExit: raise except Exception: # Note that import error from test files might end up here xmlout._write_exc_info(sys.exc_info())
def load_tests(loader, tests, pattern): if os.name != 'nt': if USING_PY3: finder = doctest.DocTestFinder(parser=FixingUpDocTestParser()) suite = doctest.DocTestSuite( mercurial_extension_utils, test_finder=finder) else: suite = doctest.DocFileSuite( "py2_doctests_mercurial_extension_utils.py", module_relative=True, globs=mercurial_extension_utils.__dict__, parser=FixingUpDocTestParser()) else: if USING_PY3: raise Exception("TODO: py3 tests for Windows") else: suite = doctest.DocFileSuite( "py2win_doctests_mercurial_extension_utils.py", module_relative=True, globs=mercurial_extension_utils.__dict__, parser=FixingUpDocTestParser()) tests.addTests(suite) return tests
def _run_object_doctest(obj, module): # Direct doctest output (normally just errors) to real stdout; doctest # output shouldn't be compared by regrtest. save_stdout = sys.stdout sys.stdout = test.test_support.get_original_stdout() try: finder = doctest.DocTestFinder(verbose=verbose, recurse=False) runner = doctest.DocTestRunner(verbose=verbose) # Use the object's fully qualified name if it has one # Otherwise, use the module's name try: name = "%s.%s" % (obj.__module__, obj.__name__) except AttributeError: name = module.__name__ for example in finder.find(obj, name, module): runner.run(example) f, t = runner.failures, runner.tries if f: raise test.test_support.TestFailed("%d of %d doctests failed" % (f, t)) finally: sys.stdout = save_stdout if verbose: print 'doctest (%s) ... %d tests with zero failures' % (module.__name__, t) return f, t