mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Migrate to py3 style type annotation: sphinx.ext.doctest
This commit is contained in:
parent
f82d6c429b
commit
dcff6d7cbc
@ -16,8 +16,10 @@ import time
|
||||
import warnings
|
||||
from io import StringIO
|
||||
from os import path
|
||||
from typing import Any, Callable, Dict, Iterable, List, Sequence, Set, Tuple, Type
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element, Node, TextElement
|
||||
from docutils.parsers.rst import directives
|
||||
from packaging.specifiers import SpecifierSet, InvalidSpecifier
|
||||
from packaging.version import Version
|
||||
@ -33,8 +35,8 @@ from sphinx.util.osutil import relpath
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Type # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -42,15 +44,13 @@ blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
|
||||
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
|
||||
|
||||
|
||||
def doctest_encode(text, encoding):
|
||||
# type: (str, str) -> str
|
||||
def doctest_encode(text: str, encoding: str) -> str:
|
||||
warnings.warn('doctest_encode() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
return text
|
||||
|
||||
|
||||
def is_allowed_version(spec, version):
|
||||
# type: (str, str) -> bool
|
||||
def is_allowed_version(spec: str, version: str) -> bool:
|
||||
"""Check `spec` satisfies `version` or not.
|
||||
|
||||
This obeys PEP-440 specifiers:
|
||||
@ -80,8 +80,7 @@ class TestDirective(SphinxDirective):
|
||||
optional_arguments = 1
|
||||
final_argument_whitespace = True
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
def run(self) -> List[Node]:
|
||||
# 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)
|
||||
@ -95,7 +94,7 @@ class TestDirective(SphinxDirective):
|
||||
if not test:
|
||||
test = code
|
||||
code = doctestopt_re.sub('', code)
|
||||
nodetype = nodes.literal_block # type: Type[nodes.TextElement]
|
||||
nodetype = nodes.literal_block # type: Type[TextElement]
|
||||
if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options:
|
||||
nodetype = nodes.comment
|
||||
if self.arguments:
|
||||
@ -194,15 +193,13 @@ parser = doctest.DocTestParser()
|
||||
# helper classes
|
||||
|
||||
class TestGroup:
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.setup = [] # type: List[TestCode]
|
||||
self.tests = [] # type: List[List[TestCode]]
|
||||
self.cleanup = [] # type: List[TestCode]
|
||||
|
||||
def add_code(self, code, prepend=False):
|
||||
# type: (TestCode, bool) -> None
|
||||
def add_code(self, code: "TestCode", prepend: bool = False) -> None:
|
||||
if code.type == 'testsetup':
|
||||
if prepend:
|
||||
self.setup.insert(0, code)
|
||||
@ -220,30 +217,28 @@ class TestGroup:
|
||||
else:
|
||||
raise RuntimeError(__('invalid TestCode type'))
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return 'TestGroup(name=%r, setup=%r, cleanup=%r, tests=%r)' % (
|
||||
self.name, self.setup, self.cleanup, self.tests)
|
||||
|
||||
|
||||
class TestCode:
|
||||
def __init__(self, code, type, filename, lineno, options=None):
|
||||
# type: (str, str, Optional[str], int, Optional[Dict]) -> None
|
||||
def __init__(self, code: str, type: str, filename: str,
|
||||
lineno: int, options: Dict = None) -> None:
|
||||
self.code = code
|
||||
self.type = type
|
||||
self.filename = filename
|
||||
self.lineno = lineno
|
||||
self.options = options or {}
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
def __repr__(self) -> str:
|
||||
return 'TestCode(%r, %r, filename=%r, lineno=%r, options=%r)' % (
|
||||
self.code, self.type, self.filename, self.lineno, self.options)
|
||||
|
||||
|
||||
class SphinxDocTestRunner(doctest.DocTestRunner):
|
||||
def summarize(self, out, verbose=None): # type: ignore
|
||||
# type: (Callable, bool) -> Tuple[int, int]
|
||||
def summarize(self, out: Callable, verbose: bool = None # type: ignore
|
||||
) -> Tuple[int, int]:
|
||||
string_io = StringIO()
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = string_io
|
||||
@ -254,9 +249,8 @@ class SphinxDocTestRunner(doctest.DocTestRunner):
|
||||
out(string_io.getvalue())
|
||||
return res
|
||||
|
||||
def _DocTestRunner__patched_linecache_getlines(self, filename,
|
||||
module_globals=None):
|
||||
# type: (str, Any) -> Any
|
||||
def _DocTestRunner__patched_linecache_getlines(self, filename: str,
|
||||
module_globals: Any = None) -> Any:
|
||||
# this is overridden from DocTestRunner adding the try-except below
|
||||
m = self._DocTestRunner__LINECACHE_FILENAME_RE.match(filename) # type: ignore
|
||||
if m and m.group('name') == self.test.name:
|
||||
@ -282,8 +276,7 @@ class DocTestBuilder(Builder):
|
||||
epilog = __('Testing of doctests in the sources finished, look at the '
|
||||
'results in %(outdir)s/output.txt.')
|
||||
|
||||
def init(self):
|
||||
# type: () -> None
|
||||
def init(self) -> None:
|
||||
# default options
|
||||
self.opt = self.config.doctest_default_flags
|
||||
|
||||
@ -312,32 +305,26 @@ class DocTestBuilder(Builder):
|
||||
'==================================%s\n') %
|
||||
(date, '=' * len(date)))
|
||||
|
||||
def _out(self, text):
|
||||
# type: (str) -> None
|
||||
def _out(self, text: str) -> None:
|
||||
logger.info(text, nonl=True)
|
||||
self.outfile.write(text)
|
||||
|
||||
def _warn_out(self, text):
|
||||
# type: (str) -> None
|
||||
def _warn_out(self, text: str) -> None:
|
||||
if self.app.quiet or self.app.warningiserror:
|
||||
logger.warning(text)
|
||||
else:
|
||||
logger.info(text, nonl=True)
|
||||
self.outfile.write(text)
|
||||
|
||||
def get_target_uri(self, docname, typ=None):
|
||||
# type: (str, str) -> str
|
||||
def get_target_uri(self, docname: str, typ: str = None) -> str:
|
||||
return ''
|
||||
|
||||
def get_outdated_docs(self):
|
||||
# type: () -> Set[str]
|
||||
def get_outdated_docs(self) -> Set[str]:
|
||||
return self.env.found_docs
|
||||
|
||||
def finish(self):
|
||||
# type: () -> None
|
||||
def finish(self) -> None:
|
||||
# write executive summary
|
||||
def s(v):
|
||||
# type: (int) -> str
|
||||
def s(v: int) -> str:
|
||||
return v != 1 and 's' or ''
|
||||
repl = (self.total_tries, s(self.total_tries),
|
||||
self.total_failures, s(self.total_failures),
|
||||
@ -356,8 +343,8 @@ Doctest summary
|
||||
if self.total_failures or self.setup_failures or self.cleanup_failures:
|
||||
self.app.statuscode = 1
|
||||
|
||||
def write(self, build_docnames, updated_docnames, method='update'):
|
||||
# type: (Iterable[str], Sequence[str], str) -> None
|
||||
def write(self, build_docnames: Iterable[str], updated_docnames: Sequence[str],
|
||||
method: str = 'update') -> None:
|
||||
if build_docnames is None:
|
||||
build_docnames = sorted(self.env.all_docs)
|
||||
|
||||
@ -367,8 +354,7 @@ Doctest summary
|
||||
doctree = self.env.get_doctree(docname)
|
||||
self.test_doc(docname, doctree)
|
||||
|
||||
def get_filename_for_node(self, node, docname):
|
||||
# type: (nodes.Node, str) -> str
|
||||
def get_filename_for_node(self, node: Node, docname: str) -> str:
|
||||
"""Try to get the file which actually contains the doctest, not the
|
||||
filename of the document it's included in."""
|
||||
try:
|
||||
@ -379,8 +365,7 @@ Doctest summary
|
||||
return filename
|
||||
|
||||
@staticmethod
|
||||
def get_line_number(node):
|
||||
# type: (nodes.Node) -> Optional[int]
|
||||
def get_line_number(node: Node) -> int:
|
||||
"""Get the real line number or admit we don't know."""
|
||||
# TODO: Work out how to store or calculate real (file-relative)
|
||||
# line numbers for doctest blocks in docstrings.
|
||||
@ -395,8 +380,7 @@ Doctest summary
|
||||
return node.line - 1
|
||||
return None
|
||||
|
||||
def skipped(self, node):
|
||||
# type: (nodes.Element) -> bool
|
||||
def skipped(self, node: Element) -> bool:
|
||||
if 'skipif' not in node:
|
||||
return False
|
||||
else:
|
||||
@ -409,8 +393,7 @@ Doctest summary
|
||||
exec(self.config.doctest_global_cleanup, context)
|
||||
return should_skip
|
||||
|
||||
def test_doc(self, docname, doctree):
|
||||
# type: (str, nodes.Node) -> None
|
||||
def test_doc(self, docname: str, doctree: Node) -> None:
|
||||
groups = {} # type: Dict[str, TestGroup]
|
||||
add_to_all_groups = []
|
||||
self.setup_runner = SphinxDocTestRunner(verbose=False,
|
||||
@ -424,17 +407,15 @@ Doctest summary
|
||||
self.cleanup_runner._fakeout = self.setup_runner._fakeout # type: ignore
|
||||
|
||||
if self.config.doctest_test_doctest_blocks:
|
||||
def condition(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def condition(node: Node) -> bool:
|
||||
return (isinstance(node, (nodes.literal_block, nodes.comment)) and
|
||||
'testnodetype' in node) or \
|
||||
isinstance(node, nodes.doctest_block)
|
||||
else:
|
||||
def condition(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
def condition(node: Node) -> bool:
|
||||
return isinstance(node, (nodes.literal_block, nodes.comment)) \
|
||||
and 'testnodetype' in node
|
||||
for node in doctree.traverse(condition): # type: nodes.Element
|
||||
for node in doctree.traverse(condition): # type: Element
|
||||
if self.skipped(node):
|
||||
continue
|
||||
|
||||
@ -490,16 +471,13 @@ Doctest summary
|
||||
self.cleanup_failures += res_f
|
||||
self.cleanup_tries += res_t
|
||||
|
||||
def compile(self, code, name, type, flags, dont_inherit):
|
||||
# type: (str, str, str, Any, bool) -> Any
|
||||
def compile(self, code: str, name: str, type: str, flags: Any, dont_inherit: bool) -> Any:
|
||||
return compile(code, name, self.type, flags, dont_inherit)
|
||||
|
||||
def test_group(self, group):
|
||||
# type: (TestGroup) -> None
|
||||
def test_group(self, group: TestGroup) -> None:
|
||||
ns = {} # type: Dict
|
||||
|
||||
def run_setup_cleanup(runner, testcodes, what):
|
||||
# type: (Any, List[TestCode], Any) -> bool
|
||||
def run_setup_cleanup(runner: Any, testcodes: List[TestCode], what: Any) -> bool:
|
||||
examples = []
|
||||
for testcode in testcodes:
|
||||
example = doctest.Example(testcode.code, '', lineno=testcode.lineno)
|
||||
@ -568,8 +546,7 @@ Doctest summary
|
||||
run_setup_cleanup(self.cleanup_runner, group.cleanup, 'cleanup')
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
def setup(app: "Sphinx") -> Dict[str, Any]:
|
||||
app.add_directive('testsetup', TestsetupDirective)
|
||||
app.add_directive('testcleanup', TestcleanupDirective)
|
||||
app.add_directive('doctest', DoctestDirective)
|
||||
|
Loading…
Reference in New Issue
Block a user