Merge pull request #6555 from tk0miya/refactor_type_annotation_testing

Migrate to py3 style type annotation: sphinx.testing
This commit is contained in:
Takeshi KOMIYA
2019-07-06 14:54:57 +09:00
committed by GitHub
4 changed files with 53 additions and 108 deletions

View File

@@ -14,25 +14,20 @@ import sys
from collections import namedtuple from collections import namedtuple
from io import StringIO from io import StringIO
from subprocess import PIPE from subprocess import PIPE
from typing import Any, Dict
import pytest import pytest
from . import util from . import util
if False:
# For type annotation
from typing import Any, Dict, Union # NOQA
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def rootdir(): def rootdir() -> None:
# type: () -> None
return None return None
@pytest.fixture @pytest.fixture
def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir): def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir):
# type: (Any, Any, Any, Any, Any) -> None
""" """
parameters that is specified by 'pytest.mark.sphinx' for parameters that is specified by 'pytest.mark.sphinx' for
sphinx.application.Sphinx initialization sphinx.application.Sphinx initialization
@@ -158,10 +153,10 @@ def make_app(test_params, monkeypatch):
status, warning = StringIO(), StringIO() status, warning = StringIO(), StringIO()
kwargs.setdefault('status', status) kwargs.setdefault('status', status)
kwargs.setdefault('warning', warning) kwargs.setdefault('warning', warning)
app_ = util.SphinxTestApp(*args, **kwargs) # type: Union[util.SphinxTestApp, util.SphinxTestAppWrapperForSkipBuilding] # NOQA app_ = util.SphinxTestApp(*args, **kwargs) # type: Any
apps.append(app_) apps.append(app_)
if test_params['shared_result']: if test_params['shared_result']:
app_ = util.SphinxTestAppWrapperForSkipBuilding(app_) # type: ignore app_ = util.SphinxTestAppWrapperForSkipBuilding(app_)
return app_ return app_
yield make yield make

View File

@@ -5,14 +5,12 @@
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import builtins
import os import os
import shutil import shutil
import sys import sys
from typing import Any, Callable, IO, List
if False:
# For type annotation
import builtins # NOQA
from typing import Any, Callable, IO, List # NOQA
FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
@@ -24,61 +22,52 @@ class path(str):
""" """
@property @property
def parent(self): def parent(self) -> "path":
# type: () -> path
""" """
The name of the directory the file or directory is in. The name of the directory the file or directory is in.
""" """
return self.__class__(os.path.dirname(self)) return self.__class__(os.path.dirname(self))
def basename(self): def basename(self) -> str:
# type: () -> str
return os.path.basename(self) return os.path.basename(self)
def abspath(self): def abspath(self) -> "path":
# type: () -> path
""" """
Returns the absolute path. Returns the absolute path.
""" """
return self.__class__(os.path.abspath(self)) return self.__class__(os.path.abspath(self))
def isabs(self): def isabs(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path is absolute. Returns ``True`` if the path is absolute.
""" """
return os.path.isabs(self) return os.path.isabs(self)
def isdir(self): def isdir(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path is a directory. Returns ``True`` if the path is a directory.
""" """
return os.path.isdir(self) return os.path.isdir(self)
def isfile(self): def isfile(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path is a file. Returns ``True`` if the path is a file.
""" """
return os.path.isfile(self) return os.path.isfile(self)
def islink(self): def islink(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path is a symbolic link. Returns ``True`` if the path is a symbolic link.
""" """
return os.path.islink(self) return os.path.islink(self)
def ismount(self): def ismount(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path is a mount point. Returns ``True`` if the path is a mount point.
""" """
return os.path.ismount(self) return os.path.ismount(self)
def rmtree(self, ignore_errors=False, onerror=None): def rmtree(self, ignore_errors: bool = False, onerror: Callable = None) -> None:
# type: (bool, Callable) -> None
""" """
Removes the file or directory and any files or directories it may Removes the file or directory and any files or directories it may
contain. contain.
@@ -96,8 +85,7 @@ class path(str):
""" """
shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror) shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
def copytree(self, destination, symlinks=False): def copytree(self, destination: str, symlinks: bool = False) -> None:
# type: (str, bool) -> None
""" """
Recursively copy a directory to the given `destination`. If the given Recursively copy a directory to the given `destination`. If the given
`destination` does not exist it will be created. `destination` does not exist it will be created.
@@ -109,8 +97,7 @@ class path(str):
""" """
shutil.copytree(self, destination, symlinks=symlinks) shutil.copytree(self, destination, symlinks=symlinks)
def movetree(self, destination): def movetree(self, destination: str) -> None:
# type: (str) -> None
""" """
Recursively move the file or directory to the given `destination` Recursively move the file or directory to the given `destination`
similar to the Unix "mv" command. similar to the Unix "mv" command.
@@ -122,54 +109,46 @@ class path(str):
move = movetree move = movetree
def unlink(self): def unlink(self) -> None:
# type: () -> None
""" """
Removes a file. Removes a file.
""" """
os.unlink(self) os.unlink(self)
def stat(self): def stat(self) -> Any:
# type: () -> Any
""" """
Returns a stat of the file. Returns a stat of the file.
""" """
return os.stat(self) return os.stat(self)
def utime(self, arg): def utime(self, arg: Any) -> None:
# type: (Any) -> None
os.utime(self, arg) os.utime(self, arg)
def open(self, mode='r', **kwargs): def open(self, mode: str = 'r', **kwargs) -> IO:
# type: (str, Any) -> IO
return open(self, mode, **kwargs) return open(self, mode, **kwargs)
def write_text(self, text, encoding='utf-8', **kwargs): def write_text(self, text: str, encoding: str = 'utf-8', **kwargs) -> None:
# type: (str, str, Any) -> None
""" """
Writes the given `text` to the file. Writes the given `text` to the file.
""" """
with open(self, 'w', encoding=encoding, **kwargs) as f: with open(self, 'w', encoding=encoding, **kwargs) as f:
f.write(text) f.write(text)
def text(self, encoding='utf-8', **kwargs): def text(self, encoding: str = 'utf-8', **kwargs) -> str:
# type: (str, Any) -> str
""" """
Returns the text in the file. Returns the text in the file.
""" """
with open(self, encoding=encoding, **kwargs) as f: with open(self, encoding=encoding, **kwargs) as f:
return f.read() return f.read()
def bytes(self): def bytes(self) -> builtins.bytes:
# type: () -> builtins.bytes
""" """
Returns the bytes in the file. Returns the bytes in the file.
""" """
with open(self, mode='rb') as f: with open(self, mode='rb') as f:
return f.read() return f.read()
def write_bytes(self, bytes, append=False): def write_bytes(self, bytes: str, append: bool = False) -> None:
# type: (str, bool) -> None
""" """
Writes the given `bytes` to the file. Writes the given `bytes` to the file.
@@ -183,41 +162,35 @@ class path(str):
with open(self, mode=mode) as f: with open(self, mode=mode) as f:
f.write(bytes) f.write(bytes)
def exists(self): def exists(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path exist. Returns ``True`` if the path exist.
""" """
return os.path.exists(self) return os.path.exists(self)
def lexists(self): def lexists(self) -> bool:
# type: () -> bool
""" """
Returns ``True`` if the path exists unless it is a broken symbolic Returns ``True`` if the path exists unless it is a broken symbolic
link. link.
""" """
return os.path.lexists(self) return os.path.lexists(self)
def makedirs(self, mode=0o777, exist_ok=False): def makedirs(self, mode: int = 0o777, exist_ok: bool = False) -> None:
# type: (int, bool) -> None
""" """
Recursively create directories. Recursively create directories.
""" """
os.makedirs(self, mode, exist_ok=exist_ok) os.makedirs(self, mode, exist_ok=exist_ok)
def joinpath(self, *args): def joinpath(self, *args) -> "path":
# type: (Any) -> path
""" """
Joins the path with the argument given and returns the result. Joins the path with the argument given and returns the result.
""" """
return self.__class__(os.path.join(self, *map(self.__class__, args))) return self.__class__(os.path.join(self, *map(self.__class__, args)))
def listdir(self): def listdir(self) -> List[str]:
# type: () -> List[str]
return os.listdir(self) return os.listdir(self)
__div__ = __truediv__ = joinpath __div__ = __truediv__ = joinpath
def __repr__(self): def __repr__(self) -> str:
# type: () -> str
return '%s(%s)' % (self.__class__.__name__, super().__repr__()) return '%s(%s)' % (self.__class__.__name__, super().__repr__())

View File

@@ -8,21 +8,16 @@
from os import path from os import path
from docutils import nodes
from docutils.core import publish_doctree from docutils.core import publish_doctree
from sphinx.application import Sphinx
from sphinx.io import SphinxStandaloneReader from sphinx.io import SphinxStandaloneReader
from sphinx.parsers import RSTParser from sphinx.parsers import RSTParser
from sphinx.util.docutils import sphinx_domains from sphinx.util.docutils import sphinx_domains
if False: def parse(app: Sphinx, text: str, docname: str = 'index') -> nodes.document:
# For type annotation
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
def parse(app, text, docname='index'):
# type: (Sphinx, str, str) -> nodes.document
"""Parse a string as reStructuredText with Sphinx application.""" """Parse a string as reStructuredText with Sphinx application."""
try: try:
app.env.temp_data['docname'] = docname app.env.temp_data['docname'] = docname

View File

@@ -11,6 +11,7 @@ import os
import re import re
import sys import sys
import warnings import warnings
from typing import Any, Dict, Generator, IO, List, Pattern
from xml.etree import ElementTree from xml.etree import ElementTree
from docutils import nodes from docutils import nodes
@@ -23,10 +24,6 @@ from sphinx.pycode import ModuleAnalyzer
from sphinx.testing.path import path from sphinx.testing.path import path
from sphinx.util.osutil import relpath from sphinx.util.osutil import relpath
if False:
# For type annotation
from typing import Any, Dict, Generator, IO, List, Pattern # NOQA
__all__ = [ __all__ = [
'Struct', 'Struct',
@@ -35,26 +32,22 @@ __all__ = [
] ]
def assert_re_search(regex, text, flags=0): def assert_re_search(regex: Pattern, text: str, flags: int = 0) -> None:
# type: (Pattern, str, int) -> None
if not re.search(regex, text, flags): if not re.search(regex, text, flags):
assert False, '%r did not match %r' % (regex, text) assert False, '%r did not match %r' % (regex, text)
def assert_not_re_search(regex, text, flags=0): def assert_not_re_search(regex: Pattern, text: str, flags: int = 0) -> None:
# type: (Pattern, str, int) -> None
if re.search(regex, text, flags): if re.search(regex, text, flags):
assert False, '%r did match %r' % (regex, text) assert False, '%r did match %r' % (regex, text)
def assert_startswith(thing, prefix): def assert_startswith(thing: str, prefix: str) -> None:
# type: (str, str) -> None
if not thing.startswith(prefix): if not thing.startswith(prefix):
assert False, '%r does not start with %r' % (thing, prefix) assert False, '%r does not start with %r' % (thing, prefix)
def assert_node(node, cls=None, xpath="", **kwargs): def assert_node(node: nodes.Node, cls: Any = None, xpath: str = "", **kwargs) -> None:
# type: (nodes.Node, Any, str, Any) -> None
if cls: if cls:
if isinstance(cls, list): if isinstance(cls, list):
assert_node(node, cls[0], xpath=xpath, **kwargs) assert_node(node, cls[0], xpath=xpath, **kwargs)
@@ -92,16 +85,14 @@ def assert_node(node, cls=None, xpath="", **kwargs):
'The node%s[%s] is not %r: %r' % (xpath, key, value, node[key]) 'The node%s[%s] is not %r: %r' % (xpath, key, value, node[key])
def etree_parse(path): def etree_parse(path: str) -> Any:
# type: (str) -> Any
with warnings.catch_warnings(record=False): with warnings.catch_warnings(record=False):
warnings.filterwarnings("ignore", category=DeprecationWarning) warnings.filterwarnings("ignore", category=DeprecationWarning)
return ElementTree.parse(path) return ElementTree.parse(path)
class Struct: class Struct:
def __init__(self, **kwds): def __init__(self, **kwds) -> None:
# type: (Any) -> None
self.__dict__.update(kwds) self.__dict__.update(kwds)
@@ -111,10 +102,9 @@ class SphinxTestApp(application.Sphinx):
better default values for the initialization parameters. better default values for the initialization parameters.
""" """
def __init__(self, buildername='html', srcdir=None, def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False,
freshenv=False, confoverrides=None, status=None, warning=None, confoverrides: Dict = None, status: IO = None, warning: IO = None,
tags=None, docutilsconf=None): tags: List[str] = None, docutilsconf: str = None) -> None:
# type: (str, path, bool, Dict, IO, IO, List[str], str) -> None
if docutilsconf is not None: if docutilsconf is not None:
(srcdir / 'docutils.conf').write_text(docutilsconf) (srcdir / 'docutils.conf').write_text(docutilsconf)
@@ -144,8 +134,7 @@ class SphinxTestApp(application.Sphinx):
self.cleanup() self.cleanup()
raise raise
def cleanup(self, doctrees=False): def cleanup(self, doctrees: bool = False) -> None:
# type: (bool) -> None
ModuleAnalyzer.cache.clear() ModuleAnalyzer.cache.clear()
LaTeXBuilder.usepackages = [] LaTeXBuilder.usepackages = []
locale.translators.clear() locale.translators.clear()
@@ -159,8 +148,7 @@ class SphinxTestApp(application.Sphinx):
delattr(nodes.GenericNodeVisitor, 'visit_' + method[6:]) delattr(nodes.GenericNodeVisitor, 'visit_' + method[6:])
delattr(nodes.GenericNodeVisitor, 'depart_' + method[6:]) delattr(nodes.GenericNodeVisitor, 'depart_' + method[6:])
def __repr__(self): def __repr__(self) -> str:
# type: () -> str
return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name) return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name)
@@ -171,16 +159,13 @@ class SphinxTestAppWrapperForSkipBuilding:
file. file.
""" """
def __init__(self, app_): def __init__(self, app_: SphinxTestApp) -> None:
# type: (SphinxTestApp) -> None
self.app = app_ self.app = app_
def __getattr__(self, name): def __getattr__(self, name: str) -> Any:
# type: (str) -> Any
return getattr(self.app, name) return getattr(self.app, name)
def build(self, *args, **kw): def build(self, *args, **kw) -> None:
# type: (Any, Any) -> None
if not self.app.outdir.listdir(): # type: ignore if not self.app.outdir.listdir(): # type: ignore
# if listdir is empty, do build. # if listdir is empty, do build.
self.app.build(*args, **kw) self.app.build(*args, **kw)
@@ -190,15 +175,13 @@ class SphinxTestAppWrapperForSkipBuilding:
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')') _unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
def remove_unicode_literals(s): def remove_unicode_literals(s: str) -> str:
# type: (str) -> str
warnings.warn('remove_unicode_literals() is deprecated.', warnings.warn('remove_unicode_literals() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2) RemovedInSphinx40Warning, stacklevel=2)
return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)
def find_files(root, suffix=None): def find_files(root: str, suffix: bool = None) -> Generator[str, None, None]:
# type: (str, bool) -> Generator
for dirpath, dirs, files in os.walk(root, followlinks=True): for dirpath, dirs, files in os.walk(root, followlinks=True):
dirpath = path(dirpath) dirpath = path(dirpath)
for f in [f for f in files if not suffix or f.endswith(suffix)]: # type: ignore for f in [f for f in files if not suffix or f.endswith(suffix)]: # type: ignore
@@ -206,6 +189,5 @@ def find_files(root, suffix=None):
yield relpath(fpath, root) yield relpath(fpath, root)
def strip_escseq(text): def strip_escseq(text: str) -> str:
# type: (str) -> str
return re.sub('\x1b.*?m', '', text) return re.sub('\x1b.*?m', '', text)