mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #3339 from marco-buttu/master
Added pyversion option for doctest (issue 3303)
This commit is contained in:
commit
0aa6ff3681
1
AUTHORS
1
AUTHORS
@ -21,6 +21,7 @@ Other contributors, listed alphabetically, are:
|
|||||||
* Henrique Bastos -- SVG support for graphviz extension
|
* Henrique Bastos -- SVG support for graphviz extension
|
||||||
* Daniel Bültmann -- todo extension
|
* Daniel Bültmann -- todo extension
|
||||||
* Jean-François Burnol -- LaTeX improvements
|
* Jean-François Burnol -- LaTeX improvements
|
||||||
|
* Marco Buttu -- doctest extension (pyversion option)
|
||||||
* Etienne Desautels -- apidoc module
|
* Etienne Desautels -- apidoc module
|
||||||
* Michael Droettboom -- inheritance_diagram extension
|
* Michael Droettboom -- inheritance_diagram extension
|
||||||
* Charles Duffy -- original graphviz extension
|
* Charles Duffy -- original graphviz extension
|
||||||
|
1
CHANGES
1
CHANGES
@ -43,6 +43,7 @@ Features added
|
|||||||
* C++, add ``:tparam-line-spec:`` option to templated declarations.
|
* C++, add ``:tparam-line-spec:`` option to templated declarations.
|
||||||
When specified, each template parameter will be rendered on a separate line.
|
When specified, each template parameter will be rendered on a separate line.
|
||||||
* #3359: Allow sphinx.js in a user locale dir to override sphinx.js from Sphinx
|
* #3359: Allow sphinx.js in a user locale dir to override sphinx.js from Sphinx
|
||||||
|
* #3303: Add ``:pyversion:`` option to the doctest directive.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
@ -63,7 +63,7 @@ a comma-separated list of group names.
|
|||||||
default set of flags is specified by the :confval:`doctest_default_flags`
|
default set of flags is specified by the :confval:`doctest_default_flags`
|
||||||
configuration variable.
|
configuration variable.
|
||||||
|
|
||||||
This directive supports two options:
|
This directive supports three options:
|
||||||
|
|
||||||
* ``hide``, a flag option, hides the doctest block in other builders. By
|
* ``hide``, a flag option, hides the doctest block in other builders. By
|
||||||
default it is shown as a highlighted doctest block.
|
default it is shown as a highlighted doctest block.
|
||||||
@ -73,6 +73,19 @@ a comma-separated list of group names.
|
|||||||
explicit flags per example, with doctest comments, but they will show up in
|
explicit flags per example, with doctest comments, but they will show up in
|
||||||
other builders too.)
|
other builders too.)
|
||||||
|
|
||||||
|
* ``pyversion``, a string option, can be used to specify the required Python
|
||||||
|
version for the example to be tested. For instance, in the following case
|
||||||
|
the example will be tested only for Python versions greather than 3.3::
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
:pyversion: > 3.3
|
||||||
|
|
||||||
|
The supported operands are ``<``, ``<=``, ``==``, ``>=``, ``>``, and
|
||||||
|
comparison is performed by `distutils.version.LooseVersion
|
||||||
|
<https://www.python.org/dev/peps/pep-0386/#distutils>`__.
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
Note that like with standard doctests, you have to use ``<BLANKLINE>`` to
|
Note that like with standard doctests, you have to use ``<BLANKLINE>`` to
|
||||||
signal a blank line in the expected output. The ``<BLANKLINE>`` is removed
|
signal a blank line in the expected output. The ``<BLANKLINE>`` is removed
|
||||||
when building presentation output (HTML, LaTeX etc.).
|
when building presentation output (HTML, LaTeX etc.).
|
||||||
|
@ -15,10 +15,12 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import codecs
|
import codecs
|
||||||
|
import platform
|
||||||
from os import path
|
from os import path
|
||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
from six import itervalues, StringIO, binary_type, text_type, PY2
|
from six import itervalues, StringIO, binary_type, text_type, PY2
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.parsers.rst import Directive, directives
|
from docutils.parsers.rst import Directive, directives
|
||||||
@ -29,6 +31,7 @@ from sphinx.util import force_decode, logging
|
|||||||
from sphinx.util.nodes import set_source_info
|
from sphinx.util.nodes import set_source_info
|
||||||
from sphinx.util.console import bold # type: ignore
|
from sphinx.util.console import bold # type: ignore
|
||||||
from sphinx.util.osutil import fs_encoding
|
from sphinx.util.osutil import fs_encoding
|
||||||
|
from sphinx.locale import _
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@ -54,6 +57,29 @@ else:
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def compare_version(ver1, ver2, operand):
|
||||||
|
"""Compare `ver1` to `ver2`, relying on `operand`.
|
||||||
|
|
||||||
|
Some examples:
|
||||||
|
|
||||||
|
>>> compare_version('3.3', '3.5', '<=')
|
||||||
|
True
|
||||||
|
>>> compare_version('3.3', '3.2', '<=')
|
||||||
|
False
|
||||||
|
>>> compare_version('3.3a0', '3.3', '<=')
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
if operand not in ('<=', '<', '==', '>=', '>'):
|
||||||
|
raise ValueError("'%s' is not a valid operand.")
|
||||||
|
v1 = LooseVersion(ver1)
|
||||||
|
v2 = LooseVersion(ver2)
|
||||||
|
return ((operand == '<=' and (v1 <= v2)) or
|
||||||
|
(operand == '<' and (v1 < v2)) or
|
||||||
|
(operand == '==' and (v1 == v2)) or
|
||||||
|
(operand == '>=' and (v1 >= v2)) or
|
||||||
|
(operand == '>' and (v1 > v2)))
|
||||||
|
|
||||||
|
|
||||||
# set up the necessary directives
|
# set up the necessary directives
|
||||||
|
|
||||||
class TestDirective(Directive):
|
class TestDirective(Directive):
|
||||||
@ -101,12 +127,32 @@ class TestDirective(Directive):
|
|||||||
# parse doctest-like output comparison flags
|
# parse doctest-like output comparison flags
|
||||||
option_strings = self.options['options'].replace(',', ' ').split()
|
option_strings = self.options['options'].replace(',', ' ').split()
|
||||||
for option in option_strings:
|
for option in option_strings:
|
||||||
if (option[0] not in '+-' or option[1:] not in
|
prefix, option_name = option[0], option[1:]
|
||||||
doctest.OPTIONFLAGS_BY_NAME): # type: ignore
|
if prefix not in '+-': # type: ignore
|
||||||
# XXX warn?
|
self.state.document.reporter.warning(
|
||||||
|
_("missing '+' or '-' in '%s' option.") % option,
|
||||||
|
line=self.lineno)
|
||||||
|
continue
|
||||||
|
if option_name not in doctest.OPTIONFLAGS_BY_NAME: # type: ignore
|
||||||
|
self.state.document.reporter.warning(
|
||||||
|
_("'%s' is not a valid option.") % option_name,
|
||||||
|
line=self.lineno)
|
||||||
continue
|
continue
|
||||||
flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] # type: ignore
|
flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]] # type: ignore
|
||||||
node['options'][flag] = (option[0] == '+')
|
node['options'][flag] = (option[0] == '+')
|
||||||
|
if self.name == 'doctest' and 'pyversion' in self.options:
|
||||||
|
try:
|
||||||
|
option = self.options['pyversion']
|
||||||
|
# :pyversion: >= 3.6 --> operand='>=', option_version='3.6'
|
||||||
|
operand, option_version = [item.strip() for item in option.split()]
|
||||||
|
running_version = platform.python_version()
|
||||||
|
if not compare_version(running_version, option_version, operand):
|
||||||
|
flag = doctest.OPTIONFLAGS_BY_NAME['SKIP']
|
||||||
|
node['options'][flag] = True # Skip the test
|
||||||
|
except ValueError:
|
||||||
|
self.state.document.reporter.warning(
|
||||||
|
_("'%s' is not a valid pyversion option") % option,
|
||||||
|
line=self.lineno)
|
||||||
return [node]
|
return [node]
|
||||||
|
|
||||||
|
|
||||||
@ -122,12 +168,14 @@ class DoctestDirective(TestDirective):
|
|||||||
option_spec = {
|
option_spec = {
|
||||||
'hide': directives.flag,
|
'hide': directives.flag,
|
||||||
'options': directives.unchanged,
|
'options': directives.unchanged,
|
||||||
|
'pyversion': directives.unchanged_required,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestcodeDirective(TestDirective):
|
class TestcodeDirective(TestDirective):
|
||||||
option_spec = {
|
option_spec = {
|
||||||
'hide': directives.flag,
|
'hide': directives.flag,
|
||||||
|
'pyversion': directives.unchanged_required,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -135,6 +183,7 @@ class TestoutputDirective(TestDirective):
|
|||||||
option_spec = {
|
option_spec = {
|
||||||
'hide': directives.flag,
|
'hide': directives.flag,
|
||||||
'options': directives.unchanged,
|
'options': directives.unchanged,
|
||||||
|
'pyversion': directives.unchanged_required,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ Special directives
|
|||||||
>>> squared(2)
|
>>> squared(2)
|
||||||
4
|
4
|
||||||
|
|
||||||
* options for testcode/testoutput blocks
|
* options for doctest/testcode/testoutput blocks
|
||||||
|
|
||||||
.. testcode::
|
.. testcode::
|
||||||
:hide:
|
:hide:
|
||||||
@ -82,6 +82,20 @@ Special directives
|
|||||||
|
|
||||||
Output text.
|
Output text.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
:pyversion: >= 2.0
|
||||||
|
|
||||||
|
>>> a = 3
|
||||||
|
>>> a
|
||||||
|
3
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
:pyversion: < 2.0
|
||||||
|
|
||||||
|
>>> a = 3
|
||||||
|
>>> a
|
||||||
|
4
|
||||||
|
|
||||||
* grouping
|
* grouping
|
||||||
|
|
||||||
.. testsetup:: group1
|
.. testsetup:: group1
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
from sphinx.ext.doctest import compare_version
|
||||||
|
|
||||||
cleanup_called = 0
|
cleanup_called = 0
|
||||||
|
|
||||||
@ -25,6 +26,21 @@ def test_build(app, status, warning):
|
|||||||
assert cleanup_called == 3, 'testcleanup did not get executed enough times'
|
assert cleanup_called == 3, 'testcleanup did not get executed enough times'
|
||||||
|
|
||||||
|
|
||||||
|
def test_compare_version():
|
||||||
|
assert compare_version('3.3', '3.4', '<') is True
|
||||||
|
assert compare_version('3.3', '3.2', '<') is False
|
||||||
|
assert compare_version('3.3', '3.4', '<=') is True
|
||||||
|
assert compare_version('3.3', '3.2', '<=') is False
|
||||||
|
assert compare_version('3.3', '3.3', '==') is True
|
||||||
|
assert compare_version('3.3', '3.4', '==') is False
|
||||||
|
assert compare_version('3.3', '3.2', '>=') is True
|
||||||
|
assert compare_version('3.3', '3.4', '>=') is False
|
||||||
|
assert compare_version('3.3', '3.2', '>') is True
|
||||||
|
assert compare_version('3.3', '3.4', '>') is False
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
compare_version('3.3', '3.4', '+')
|
||||||
|
|
||||||
|
|
||||||
def cleanup_call():
|
def cleanup_call():
|
||||||
global cleanup_called
|
global cleanup_called
|
||||||
cleanup_called += 1
|
cleanup_called += 1
|
||||||
|
Loading…
Reference in New Issue
Block a user