diff options
Diffstat (limited to 'sphinx/ext/doctest.py')
-rw-r--r-- | sphinx/ext/doctest.py | 51 |
1 files changed, 30 insertions, 21 deletions
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 70beb9bf..216325cb 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -14,24 +14,25 @@ import re import sys import time import codecs -import StringIO from os import path # circumvent relative import doctest = __import__('doctest') +from six import itervalues, StringIO, binary_type from docutils import nodes from docutils.parsers.rst import directives +import sphinx from sphinx.builders import Builder from sphinx.util import force_decode from sphinx.util.nodes import set_source_info from sphinx.util.compat import Directive from sphinx.util.console import bold -from sphinx.util.pycompat import bytes blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) + # set up the necessary directives class TestDirective(Directive): @@ -79,30 +80,35 @@ class TestDirective(Directive): option_strings = self.options['options'].replace(',', ' ').split() for option in option_strings: if (option[0] not in '+-' or option[1:] not in - doctest.OPTIONFLAGS_BY_NAME): + doctest.OPTIONFLAGS_BY_NAME): # XXX warn? continue flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] node['options'][flag] = (option[0] == '+') return [node] + class TestsetupDirective(TestDirective): option_spec = {} + class TestcleanupDirective(TestDirective): option_spec = {} + class DoctestDirective(TestDirective): option_spec = { 'hide': directives.flag, 'options': directives.unchanged, } + class TestcodeDirective(TestDirective): option_spec = { 'hide': directives.flag, } + class TestoutputDirective(TestDirective): option_spec = { 'hide': directives.flag, @@ -112,6 +118,7 @@ class TestoutputDirective(TestDirective): parser = doctest.DocTestParser() + # helper classes class TestGroup(object): @@ -158,7 +165,7 @@ class TestCode(object): class SphinxDocTestRunner(doctest.DocTestRunner): def summarize(self, out, verbose=None): - string_io = StringIO.StringIO() + string_io = StringIO() old_stdout = sys.stdout sys.stdout = string_io try: @@ -196,7 +203,7 @@ class DocTestBuilder(Builder): def init(self): # default options self.opt = doctest.DONT_ACCEPT_TRUE_FOR_1 | doctest.ELLIPSIS | \ - doctest.IGNORE_EXCEPTION_DETAIL + doctest.IGNORE_EXCEPTION_DETAIL # HACK HACK HACK # doctest compiles its snippets with type 'single'. That is nice @@ -233,7 +240,7 @@ Results of doctest builder run on %s self.info(text, nonl=True) if self.app.quiet: self.warn(text) - if isinstance(text, bytes): + if isinstance(text, binary_type): text = force_decode(text, None) self.outfile.write(text) @@ -247,6 +254,10 @@ Results of doctest builder run on %s # write executive summary def s(v): return v != 1 and 's' or '' + repl = (self.total_tries, s(self.total_tries), + self.total_failures, s(self.total_failures), + self.setup_failures, s(self.setup_failures), + self.cleanup_failures, s(self.cleanup_failures)) self._out(''' Doctest summary =============== @@ -254,10 +265,7 @@ Doctest summary %5d failure%s in tests %5d failure%s in setup code %5d failure%s in cleanup code -''' % (self.total_tries, s(self.total_tries), - self.total_failures, s(self.total_failures), - self.setup_failures, s(self.setup_failures), - self.cleanup_failures, s(self.cleanup_failures))) +''' % repl) self.outfile.close() if self.total_failures or self.setup_failures or self.cleanup_failures: @@ -289,14 +297,14 @@ Doctest summary if self.config.doctest_test_doctest_blocks: def condition(node): return (isinstance(node, (nodes.literal_block, nodes.comment)) - and node.has_key('testnodetype')) or \ - isinstance(node, nodes.doctest_block) + and 'testnodetype' in node) or \ + isinstance(node, nodes.doctest_block) else: def condition(node): return isinstance(node, (nodes.literal_block, nodes.comment)) \ - and node.has_key('testnodetype') + and 'testnodetype' in node for node in doctree.traverse(condition): - source = node.has_key('test') and node['test'] or node.astext() + source = 'test' in node and node['test'] or node.astext() if not source: self.warn('no code/output in %s block at %s:%s' % (node.get('testnodetype', 'doctest'), @@ -312,24 +320,24 @@ Doctest summary groups[groupname] = TestGroup(groupname) groups[groupname].add_code(code) for code in add_to_all_groups: - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code) if self.config.doctest_global_setup: code = TestCode(self.config.doctest_global_setup, 'testsetup', lineno=0) - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code, prepend=True) if self.config.doctest_global_cleanup: code = TestCode(self.config.doctest_global_cleanup, 'testcleanup', lineno=0) - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code) if not groups: return self._out('\nDocument: %s\n----------%s\n' % (docname, '-'*len(docname))) - for group in groups.itervalues(): + for group in itervalues(groups): self.test_group(group, self.env.doc2path(docname, base=None)) # Separately count results from setup code res_f, res_t = self.setup_runner.summarize(self._out, verbose=False) @@ -364,7 +372,7 @@ Doctest summary filename, 0, None) sim_doctest.globs = ns old_f = runner.failures - self.type = 'exec' # the snippet may contain multiple statements + self.type = 'exec' # the snippet may contain multiple statements runner.run(sim_doctest, out=self._warn_out, clear_globs=False) if runner.failures > old_f: return False @@ -394,7 +402,7 @@ Doctest summary new_opt = code[0].options.copy() new_opt.update(example.options) example.options = new_opt - self.type = 'single' # as for ordinary doctests + self.type = 'single' # as for ordinary doctests else: # testcode and output separate output = code[1] and code[1].code or '' @@ -413,7 +421,7 @@ Doctest summary options=options) test = doctest.DocTest([example], {}, group.name, filename, code[0].lineno, None) - self.type = 'exec' # multiple statements again + self.type = 'exec' # multiple statements again # DocTest.__init__ copies the globs namespace, which we don't want test.globs = ns # also don't clear the globs namespace after running the doctest @@ -435,3 +443,4 @@ def setup(app): app.add_config_value('doctest_test_doctest_blocks', 'default', False) app.add_config_value('doctest_global_setup', '', False) app.add_config_value('doctest_global_cleanup', '', False) + return {'version': sphinx.__version__, 'parallel_read_safe': True} |