diff --git a/CHANGES b/CHANGES index 1de89c59f..7c3e7b8dc 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,12 @@ Changes in trunk * sphinx.ext.doctest: Make the group in which doctest blocks are placed selectable, and default to ``'default'``. +* sphinx.ext.doctest: Replace in doctest blocks by + real blank lines for presentation output. + +* sphinx.environment: Move doctest_blocks out of block_quotes to + support indented doctest blocks. + Release 0.1.61611 ================= diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst index 6a2839199..1931a4472 100644 --- a/doc/ext/doctest.rst +++ b/doc/ext/doctest.rst @@ -65,6 +65,10 @@ names. explicit flags per example, with doctest comments, but they will show up in other builders too.) + Note that like with standard doctests, you have to use ```` to + signal a blank line in the expected output. The ```` is removed + when building presentation output (HTML, LaTeX etc.). + .. directive:: .. testcode:: [group] diff --git a/sphinx/environment.py b/sphinx/environment.py index 0dea7ee0d..d3d7b1163 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -129,6 +129,31 @@ class MoveModuleTargets(Transform): node.parent.remove(node) +class HandleCodeBlocks(Transform): + """ + Move doctest blocks out of blockquotes and connect adjacent code blocks. + """ + default_priority = 210 + + def apply(self): + for node in self.document.traverse(nodes.block_quote): + if len(node.children) == 1 and isinstance(node.children[0], + nodes.doctest_block): + node.replace_self(node.children[0]) + for node in self.document.traverse(nodes.literal_block): + if not node.parent: + continue + idx = node.parent.index(node) + try: + while isinstance(node.parent[idx+1], nodes.literal_block): + node.children[0] += '\n' + node.parent[idx+1].children[0] + import pdb; pdb.set_trace() + node.parent[idx+1].parent = None + del node.parent[idx+1] + except IndexError: + continue + + class MyStandaloneReader(standalone.Reader): """ Add our own transforms. @@ -136,7 +161,7 @@ class MyStandaloneReader(standalone.Reader): def get_transforms(self): tf = standalone.Reader.get_transforms(self) return tf + [DefaultSubstitutions, MoveModuleTargets, - FilterMessages] + FilterMessages, HandleCodeBlocks] class MyContentsFilter(ContentsFilter): diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 3639930f5..9de0821c7 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -28,10 +28,15 @@ except NameError: def prepare_docstring(s): - """Convert a docstring into lines of parseable reST.""" + """ + Convert a docstring into lines of parseable reST. Return it as a list of + lines usable for inserting into a docutils ViewList (used as argument + of nested_parse().) An empty line is added to act as a separator between + this docstring and following content. + """ if not s or s.isspace(): return [''] - nl = s.rstrip().find('\n') + nl = s.expandtabs().rstrip().find('\n') if nl == -1: # Only one line... return [s.strip(), ''] diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index a018409a2..a46a7a303 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -10,6 +10,7 @@ :license: BSD. """ +import re import sys import time import StringIO @@ -23,6 +24,8 @@ from docutils.parsers.rst import directives from sphinx.builder import Builder from sphinx.util.console import bold +blankline_re = re.compile(r'^\s*', re.MULTILINE) + # set up the necessary directives @@ -31,17 +34,23 @@ def test_directive(name, arguments, options, content, lineno, # 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' and '' in code: + # convert s to ordinary blank lines for presentation + test = code + code = blankline_re.sub('', code) nodetype = nodes.literal_block if name == 'testsetup' or 'hide' in options: nodetype = nodes.comment - node = nodetype(code, code) - node.line = lineno if arguments: groups = [x.strip() for x in arguments[0].split(',')] else: groups = ['default'] - node['testnodetype'] = name - node['groups'] = groups + 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' @@ -215,7 +224,7 @@ Doctest summary return isinstance(node, (nodes.literal_block, nodes.comment)) \ and node.has_key('testnodetype') for node in doctree.traverse(condition): - code = TestCode(node.astext(), + code = TestCode(node.has_key('test') and node['test'] or node.astext(), type=node.get('testnodetype', 'doctest'), lineno=node.line, options=node.get('options')) node_groups = node.get('groups', ['default']) @@ -281,7 +290,9 @@ Doctest summary self.type = 'single' # ordinary doctests else: output = code[1] and code[1].code or '' - options = code[1] and code[1].options or None + options = code[1] and code[1].options or {} + # disable processing as it is not needed + options[doctest.DONT_ACCEPT_BLANKLINE] = True example = doctest.Example(code[0].code, output, lineno=code[0].lineno, options=options)