summaryrefslogtreecommitdiff
path: root/sphinx/ext/doctest.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext/doctest.py')
-rw-r--r--sphinx/ext/doctest.py51
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}