diff options
Diffstat (limited to 'sphinx/ext/doctest.py')
| -rw-r--r-- | sphinx/ext/doctest.py | 151 |
1 files changed, 87 insertions, 64 deletions
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 3a44c3f7..51463661 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -22,7 +22,8 @@ doctest = __import__('doctest') from docutils import nodes from docutils.parsers.rst import directives -from sphinx.builder import Builder +from sphinx.builders import Builder +from sphinx.util.compat import Directive from sphinx.util.console import bold blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) @@ -30,63 +31,77 @@ doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) # set up the necessary directives -def test_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - # use ordinary docutils nodes for test code: they get special attributes - # so that our builder recognizes them, and the other builders are happy. - code = '\n'.join(content) - test = None - if name == 'doctest': - if '<BLANKLINE>' in code: - # convert <BLANKLINE>s to ordinary blank lines for presentation - test = code - code = blankline_re.sub('', code) - if doctestopt_re.search(code): - if not test: - test = code - code = doctestopt_re.sub('', code) - nodetype = nodes.literal_block - if name == 'testsetup' or 'hide' in options: - nodetype = nodes.comment - if arguments: - groups = [x.strip() for x in arguments[0].split(',')] - else: - groups = ['default'] - node = nodetype(code, code, testnodetype=name, groups=groups) - node.line = lineno - if test is not None: - # only save if it differs from code - node['test'] = test - if name == 'testoutput': - # don't try to highlight output - node['language'] = 'none' - node['options'] = {} - if name in ('doctest', 'testoutput') and 'options' in options: - # parse doctest-like output comparison flags - option_strings = options['options'].replace(',', ' ').split() - for option in option_strings: - if (option[0] not in '+-' or option[1:] not in - doctest.OPTIONFLAGS_BY_NAME): - # XXX warn? - continue - flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] - node['options'][flag] = (option[0] == '+') - return [node] +class TestDirective(Directive): + """ + Base class for doctest-related directives. + """ -# need to have individual functions for each directive due to different -# options they accept + has_content = True + required_arguments = 0 + optional_arguments = 1 + final_argument_whitespace = True + + def run(self): + # use ordinary docutils nodes for test code: they get special attributes + # so that our builder recognizes them, and the other builders are happy. + code = '\n'.join(self.content) + test = None + if self.name == 'doctest': + if '<BLANKLINE>' in code: + # convert <BLANKLINE>s to ordinary blank lines for presentation + test = code + code = blankline_re.sub('', code) + if doctestopt_re.search(code): + if not test: + test = code + code = doctestopt_re.sub('', code) + nodetype = nodes.literal_block + if self.name == 'testsetup' or 'hide' in self.options: + nodetype = nodes.comment + if self.arguments: + groups = [x.strip() for x in self.arguments[0].split(',')] + else: + groups = ['default'] + node = nodetype(code, code, testnodetype=self.name, groups=groups) + node.line = self.lineno + if test is not None: + # only save if it differs from code + node['test'] = test + if self.name == 'testoutput': + # don't try to highlight output + node['language'] = 'none' + node['options'] = {} + if self.name in ('doctest', 'testoutput') and 'options' in self.options: + # parse doctest-like output comparison flags + 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): + # XXX warn? + continue + flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] + node['options'][flag] = (option[0] == '+') + return [node] -def testsetup_directive(*args): - return test_directive(*args) +class TestsetupDirective(TestDirective): + option_spec = {} -def doctest_directive(*args): - return test_directive(*args) +class DoctestDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + 'options': directives.unchanged, + } -def testcode_directive(*args): - return test_directive(*args) +class TestcodeDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + } -def testoutput_directive(*args): - return test_directive(*args) +class TestoutputDirective(TestDirective): + option_spec = { + 'hide': directives.flag, + 'options': directives.unchanged, + } parser = doctest.DocTestParser() @@ -99,9 +114,12 @@ class TestGroup(object): self.setup = [] self.tests = [] - def add_code(self, code): + def add_code(self, code, prepend=False): if code.type == 'testsetup': - self.setup.append(code) + if prepend: + self.setup.insert(0, code) + else: + self.setup.append(code) elif code.type == 'doctest': self.tests.append([code]) elif code.type == 'testcode': @@ -258,10 +276,16 @@ Doctest summary for code in add_to_all_groups: for group in groups.itervalues(): 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(): + group.add_code(code, prepend=True) if not groups: return - self._out('\nDocument: %s\n----------%s\n' % (docname, '-'*len(docname))) + self._out('\nDocument: %s\n----------%s\n' % + (docname, '-'*len(docname))) for group in groups.itervalues(): self.test_group(group, self.env.doc2path(docname, base=None)) # Separately count results from setup code @@ -280,7 +304,8 @@ Doctest summary ns = {} examples = [] for setup in group.setup: - examples.append(doctest.Example(setup.code, '', lineno=setup.lineno)) + examples.append(doctest.Example(setup.code, '', + lineno=setup.lineno)) if examples: # simulate a doctest with the setup code setup_doctest = doctest.DocTest(examples, {}, @@ -324,14 +349,12 @@ Doctest summary def setup(app): - app.add_directive('testsetup', testsetup_directive, 1, (0, 1, 1)) - app.add_directive('doctest', doctest_directive, 1, (0, 1, 1), - hide=directives.flag, options=directives.unchanged) - app.add_directive('testcode', testcode_directive, 1, (0, 1, 1), - hide=directives.flag) - app.add_directive('testoutput', testoutput_directive, 1, (0, 1, 1), - hide=directives.flag, options=directives.unchanged) + app.add_directive('testsetup', TestsetupDirective) + app.add_directive('doctest', DoctestDirective) + app.add_directive('testcode', TestcodeDirective) + app.add_directive('testoutput', TestoutputDirective) app.add_builder(DocTestBuilder) # this config value adds to sys.path app.add_config_value('doctest_path', [], False) app.add_config_value('doctest_test_doctest_blocks', 'default', False) + app.add_config_value('doctest_global_setup', '', False) |
