Skip to content

Commit

Permalink
Close sphinx-doc#6698: doctest: Add :trim-flags: and :no-trim-flags: …
Browse files Browse the repository at this point in the history
…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 sphinx-doc#6698).
  • Loading branch information
tk0miya committed Aug 1, 2020
1 parent a443538 commit 61717af
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -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.
Expand Down
21 changes: 18 additions & 3 deletions doc/usage/extensions/doctest.rst
Expand Up @@ -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.
Expand Down Expand Up @@ -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 ``<BLANKLINE>`` markers are removed (or not removed) individually.
Default is ``trim-flags``.

Note that like with standard doctests, you have to use ``<BLANKLINE>`` to
signal a blank line in the expected output. The ``<BLANKLINE>`` is removed
when building presentation output (HTML, LaTeX etc.).
Expand All @@ -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 ``<BLANKLINE>`` 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
Expand All @@ -149,14 +159,19 @@ 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.

* ``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 ``<BLANKLINE>`` markers are removed (or not removed) individually.
Default is ``trim-flags``.

Example::

.. testcode::
Expand Down
12 changes: 11 additions & 1 deletion sphinx/ext/doctest.py
Expand Up @@ -91,7 +91,7 @@ def run(self) -> List[Node]:
# convert <BLANKLINE>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)
Expand Down Expand Up @@ -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]


Expand All @@ -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,
}


Expand Down
11 changes: 5 additions & 6 deletions sphinx/transforms/post_transforms/code.py
Expand Up @@ -12,7 +12,7 @@
from typing import Any, Dict, List, NamedTuple, Union

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
Expand Down Expand Up @@ -93,18 +93,17 @@ 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)

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)
Expand Down
14 changes: 13 additions & 1 deletion tests/roots/test-trim_doctest_flags/index.rst
Expand Up @@ -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)
19 changes: 19 additions & 0 deletions tests/test_transforms_post_transforms_code.py
Expand Up @@ -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')
Expand All @@ -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

0 comments on commit 61717af

Please sign in to comment.