From c2dd3008365968a6abb2ce515131b47db5432378 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 1 Aug 2020 14:31:42 +0900 Subject: [PATCH] Close #6698: doctest: Add :trim-flags: and :no-trim-flags: options To control trimming doctest flags manually, this adds new options :trim-flags: and :no-trim-flags: to doctest directives. It helps to describes doctest module itself in python doc (see #6698). --- CHANGES | 2 ++ doc/usage/extensions/doctest.rst | 21 ++++++++++++++++--- sphinx/ext/doctest.py | 12 ++++++++++- sphinx/transforms/post_transforms/code.py | 13 ++++++------ tests/roots/test-trim_doctest_flags/index.rst | 14 ++++++++++++- tests/test_transforms_post_transforms_code.py | 19 +++++++++++++++++ 6 files changed, 69 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 790bb1f0ac6..7aa9427b973 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,8 @@ Features added * #7840: i18n: Optimize the dependencies check on bootstrap * #5208: linkcheck: Support checks for local links * #5090: setuptools: Link verbosity to distutils' -v and -q option +* #6698: doctest: Add ``:trim-flags:`` and ``:no-trim-flags:`` options to + doctest, testcode and testoutput directives * #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains. Update the documentation to better reflect the relationship between this option and the ``:noindex:`` option. diff --git a/doc/usage/extensions/doctest.rst b/doc/usage/extensions/doctest.rst index 33d6cf01661..45621bcc3fd 100644 --- a/doc/usage/extensions/doctest.rst +++ b/doc/usage/extensions/doctest.rst @@ -67,7 +67,7 @@ a comma-separated list of group names. default set of flags is specified by the :confval:`doctest_default_flags` configuration variable. - This directive supports three options: + This directive supports five options: * ``hide``, a flag option, hides the doctest block in other builders. By default it is shown as a highlighted doctest block. @@ -102,6 +102,11 @@ a comma-separated list of group names. Supported PEP-440 operands and notations + * ``trim-flags`` and ``no-trim-flags``, a flag option, doctest flags + (comments looking like ``# doctest: FLAG, ...``) at the ends of lines + and ```` markers are removed (or not removed) individually. + Default is ``trim-flags``. + 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.). @@ -119,11 +124,16 @@ a comma-separated list of group names. A code block for a code-output-style test. - This directive supports one option: + This directive supports three options: * ``hide``, a flag option, hides the code block in other builders. By default it is shown as a highlighted code block. + * ``trim-flags`` and ``no-trim-flags``, a flag option, doctest flags + (comments looking like ``# doctest: FLAG, ...``) at the ends of lines + and ```` markers are removed (or not removed) individually. + Default is ``trim-flags``. + .. note:: Code in a ``testcode`` block is always executed all at once, no matter how @@ -149,7 +159,7 @@ a comma-separated list of group names. The corresponding output, or the exception message, for the last :rst:dir:`testcode` block. - This directive supports two options: + This directive supports four options: * ``hide``, a flag option, hides the output block in other builders. By default it is shown as a literal block without highlighting. @@ -157,6 +167,11 @@ a comma-separated list of group names. * ``options``, a string option, can be used to give doctest flags (comma-separated) just like in normal doctest blocks. + * ``trim-flags`` and ``no-trim-flags``, a flag option, doctest flags + (comments looking like ``# doctest: FLAG, ...``) at the ends of lines + and ```` markers are removed (or not removed) individually. + Default is ``trim-flags``. + Example:: .. testcode:: diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 26966016dc5..b05a19ccc58 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -91,7 +91,7 @@ def run(self) -> List[Node]: # convert s to ordinary blank lines for presentation test = code code = blankline_re.sub('', code) - if doctestopt_re.search(code): + if doctestopt_re.search(code) and 'no-trim-flags' not in self.options: if not test: test = code code = doctestopt_re.sub('', code) @@ -151,6 +151,10 @@ def run(self) -> List[Node]: line=self.lineno) if 'skipif' in self.options: node['skipif'] = self.options['skipif'] + if 'trim-flags' in self.options: + node['trim_flags'] = True + elif 'no-trim-flags' in self.options: + node['trim_flags'] = False return [node] @@ -165,26 +169,32 @@ class TestcleanupDirective(TestDirective): class DoctestDirective(TestDirective): option_spec = { 'hide': directives.flag, + 'no-trim-flags': directives.flag, 'options': directives.unchanged, 'pyversion': directives.unchanged_required, 'skipif': directives.unchanged_required, + 'trim-flags': directives.flag, } class TestcodeDirective(TestDirective): option_spec = { 'hide': directives.flag, + 'no-trim-flags': directives.flag, 'pyversion': directives.unchanged_required, 'skipif': directives.unchanged_required, + 'trim-flags': directives.flag, } class TestoutputDirective(TestDirective): option_spec = { 'hide': directives.flag, + 'no-trim-flags': directives.flag, 'options': directives.unchanged, 'pyversion': directives.unchanged_required, 'skipif': directives.unchanged_required, + 'trim-flags': directives.flag, } diff --git a/sphinx/transforms/post_transforms/code.py b/sphinx/transforms/post_transforms/code.py index add35647bbb..2012d6e119f 100644 --- a/sphinx/transforms/post_transforms/code.py +++ b/sphinx/transforms/post_transforms/code.py @@ -9,10 +9,10 @@ """ import sys -from typing import Any, Dict, List, NamedTuple, Union +from typing import Any, Dict, List, NamedTuple from docutils import nodes -from docutils.nodes import Node +from docutils.nodes import Node, TextElement from pygments.lexers import PythonConsoleLexer, guess_lexer from sphinx import addnodes @@ -93,9 +93,6 @@ class TrimDoctestFlagsTransform(SphinxTransform): default_priority = HighlightLanguageTransform.default_priority + 1 def apply(self, **kwargs: Any) -> None: - if not self.config.trim_doctest_flags: - return - for lbnode in self.document.traverse(nodes.literal_block): # type: nodes.literal_block if self.is_pyconsole(lbnode): self.strip_doctest_flags(lbnode) @@ -103,8 +100,10 @@ def apply(self, **kwargs: Any) -> None: for dbnode in self.document.traverse(nodes.doctest_block): # type: nodes.doctest_block self.strip_doctest_flags(dbnode) - @staticmethod - def strip_doctest_flags(node: Union[nodes.literal_block, nodes.doctest_block]) -> None: + def strip_doctest_flags(self, node: TextElement) -> None: + if not node.get('trim_flags', self.config.trim_doctest_flags): + return + source = node.rawsource source = doctest.blankline_re.sub('', source) source = doctest.doctestopt_re.sub('', source) diff --git a/tests/roots/test-trim_doctest_flags/index.rst b/tests/roots/test-trim_doctest_flags/index.rst index 63ce234cdfc..969d6cb7620 100644 --- a/tests/roots/test-trim_doctest_flags/index.rst +++ b/tests/roots/test-trim_doctest_flags/index.rst @@ -22,7 +22,19 @@ test-trim_doctest_flags >>> datetime.date.now() # doctest: +QUX datetime.date(2008, 1, 1) -.. doctest_block:: +.. doctest:: >>> datetime.date.now() # doctest: +QUUX datetime.date(2008, 1, 1) + +.. doctest:: + :trim-flags: + + >>> datetime.date.now() # doctest: +CORGE + datetime.date(2008, 1, 1) + +.. doctest:: + :no-trim-flags: + + >>> datetime.date.now() # doctest: +GRAULT + datetime.date(2008, 1, 1) diff --git a/tests/test_transforms_post_transforms_code.py b/tests/test_transforms_post_transforms_code.py index 880f4711bbe..2e5da05ae28 100644 --- a/tests/test_transforms_post_transforms_code.py +++ b/tests/test_transforms_post_transforms_code.py @@ -19,6 +19,23 @@ def test_trim_doctest_flags_html(app, status, warning): assert 'BAZ' not in result assert 'QUX' not in result assert 'QUUX' not in result + assert 'CORGE' not in result + assert 'GRAULT' in result + + +@pytest.mark.sphinx('html', testroot='trim_doctest_flags', + confoverrides={'trim_doctest_flags': False}) +def test_trim_doctest_flags_disabled(app, status, warning): + app.build() + + result = (app.outdir / 'index.html').read_text() + assert 'FOO' in result + assert 'BAR' in result + assert 'BAZ' in result + assert 'QUX' in result + assert 'QUUX' not in result + assert 'CORGE' not in result + assert 'GRAULT' in result @pytest.mark.sphinx('latex', testroot='trim_doctest_flags') @@ -31,3 +48,5 @@ def test_trim_doctest_flags_latex(app, status, warning): assert 'BAZ' not in result assert 'QUX' not in result assert 'QUUX' not in result + assert 'CORGE' not in result + assert 'GRAULT' in result