refactor: Update type annotations in sphinx.testing.*

This commit is contained in:
Takeshi KOMIYA 2020-02-16 20:32:04 +09:00
parent a69bea29e9
commit 58c53c92f4
3 changed files with 55 additions and 46 deletions

View File

@ -9,7 +9,7 @@
""" """
import difflib import difflib
import pathlib import pathlib
from typing import List, Union from typing import Any, List, Union
class PathComparer: class PathComparer:
@ -38,13 +38,13 @@ class PathComparer:
""" """
self.path = pathlib.Path(path) self.path = pathlib.Path(path)
def __str__(self): def __str__(self) -> str:
return self.path.as_posix() return self.path.as_posix()
def __repr__(self): def __repr__(self) -> str:
return "<{0.__class__.__name__}: '{0}'>".format(self) return "<{0.__class__.__name__}: '{0}'>".format(self)
def __eq__(self, other): def __eq__(self, other: Union[str, pathlib.Path]) -> bool: # type: ignore
return not bool(self.ldiff(other)) return not bool(self.ldiff(other))
def diff(self, other: Union[str, pathlib.Path]) -> List[str]: def diff(self, other: Union[str, pathlib.Path]) -> List[str]:
@ -94,8 +94,10 @@ class PathComparer:
return [line.strip() for line in difflib.Differ().compare([s_path], [o_path])] return [line.strip() for line in difflib.Differ().compare([s_path], [o_path])]
def pytest_assertrepr_compare(op, left, right): def pytest_assertrepr_compare(op: str, left: Any, right: Any) -> List[str]:
if isinstance(left, PathComparer) and op == "==": if isinstance(left, PathComparer) and op == "==":
return ['Comparing path:'] + left.ldiff(right) return ['Comparing path:'] + left.ldiff(right)
if isinstance(right, PathComparer) and op == "==": elif isinstance(right, PathComparer) and op == "==":
return ['Comparing path:'] + right.rdiff(left) return ['Comparing path:'] + right.rdiff(left)
else:
raise RuntimeError

View File

@ -14,20 +14,44 @@ 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 from typing import Any, Callable, Dict, Generator, Tuple
import pytest import pytest
from . import util from sphinx.testing import util
from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def rootdir() -> None: def rootdir() -> str:
return None return None
class SharedResult:
cache = {} # type: Dict[str, Dict[str, str]]
def store(self, key: str, app_: SphinxTestApp) -> Any:
if key in self.cache:
return
data = {
'status': app_._status.getvalue(),
'warning': app_._warning.getvalue(),
}
self.cache[key] = data
def restore(self, key: str) -> Dict[str, StringIO]:
if key not in self.cache:
return {}
data = self.cache[key]
return {
'status': StringIO(data['status']),
'warning': StringIO(data['warning']),
}
@pytest.fixture @pytest.fixture
def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir): def app_params(request: Any, test_params: Dict, shared_result: SharedResult,
sphinx_test_tempdir: str, rootdir: str) -> Tuple[Dict, Dict]:
""" """
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
@ -40,7 +64,7 @@ def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir
else: else:
markers = request.node.get_marker("sphinx") markers = request.node.get_marker("sphinx")
pargs = {} pargs = {}
kwargs = {} # type: Dict[str, str] kwargs = {} # type: Dict[str, Any]
if markers is not None: if markers is not None:
# to avoid stacking positional args # to avoid stacking positional args
@ -75,7 +99,7 @@ def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir
@pytest.fixture @pytest.fixture
def test_params(request): def test_params(request: Any) -> Dict:
""" """
test parameters that is specified by 'pytest.mark.test_params' test parameters that is specified by 'pytest.mark.test_params'
@ -102,7 +126,8 @@ def test_params(request):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def app(test_params, app_params, make_app, shared_result): def app(test_params: Dict, app_params: Tuple[Dict, Dict], make_app: Callable,
shared_result: SharedResult) -> Generator[SphinxTestApp, None, None]:
""" """
provides sphinx.application.Sphinx object provides sphinx.application.Sphinx object
""" """
@ -122,7 +147,7 @@ def app(test_params, app_params, make_app, shared_result):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def status(app): def status(app: SphinxTestApp) -> StringIO:
""" """
compat for testing with previous @with_app decorator compat for testing with previous @with_app decorator
""" """
@ -130,7 +155,7 @@ def status(app):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def warning(app): def warning(app: SphinxTestApp) -> StringIO:
""" """
compat for testing with previous @with_app decorator compat for testing with previous @with_app decorator
""" """
@ -138,7 +163,7 @@ def warning(app):
@pytest.fixture() @pytest.fixture()
def make_app(test_params, monkeypatch): def make_app(test_params: Dict, monkeypatch: Any) -> Generator[Callable, None, None]:
""" """
provides make_app function to initialize SphinxTestApp instance. provides make_app function to initialize SphinxTestApp instance.
if you want to initialize 'app' in your test function. please use this if you want to initialize 'app' in your test function. please use this
@ -153,10 +178,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: Any app_ = 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_) app_ = SphinxTestAppWrapperForSkipBuilding(app_)
return app_ return app_
yield make yield make
@ -165,40 +190,18 @@ def make_app(test_params, monkeypatch):
app_.cleanup() app_.cleanup()
class SharedResult:
cache = {} # type: Dict[str, Dict[str, str]]
def store(self, key, app_):
if key in self.cache:
return
data = {
'status': app_._status.getvalue(),
'warning': app_._warning.getvalue(),
}
self.cache[key] = data
def restore(self, key):
if key not in self.cache:
return {}
data = self.cache[key]
return {
'status': StringIO(data['status']),
'warning': StringIO(data['warning']),
}
@pytest.fixture @pytest.fixture
def shared_result(): def shared_result() -> SharedResult:
return SharedResult() return SharedResult()
@pytest.fixture(scope='module', autouse=True) @pytest.fixture(scope='module', autouse=True)
def _shared_result_cache(): def _shared_result_cache() -> None:
SharedResult.cache.clear() SharedResult.cache.clear()
@pytest.fixture @pytest.fixture
def if_graphviz_found(app): def if_graphviz_found(app: SphinxTestApp) -> None:
""" """
The test will be skipped when using 'if_graphviz_found' fixture and graphviz The test will be skipped when using 'if_graphviz_found' fixture and graphviz
dot command is not found. dot command is not found.
@ -215,7 +218,7 @@ def if_graphviz_found(app):
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def sphinx_test_tempdir(tmpdir_factory): def sphinx_test_tempdir(tmpdir_factory: Any) -> "util.path":
""" """
temporary directory that wrapped with `path` class. temporary directory that wrapped with `path` class.
""" """
@ -227,7 +230,7 @@ def sphinx_test_tempdir(tmpdir_factory):
@pytest.fixture @pytest.fixture
def tempdir(tmpdir): def tempdir(tmpdir: str) -> "util.path":
""" """
temporary directory that wrapped with `path` class. temporary directory that wrapped with `path` class.
this fixture is for compat with old test implementation. this fixture is for compat with old test implementation.

View File

@ -11,10 +11,12 @@ import os
import re import re
import sys import sys
import warnings import warnings
from io import StringIO
from typing import Any, Dict, Generator, IO, List, Pattern 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
from docutils.nodes import Node
from docutils.parsers.rst import directives, roles from docutils.parsers.rst import directives, roles
from sphinx import application, locale from sphinx import application, locale
@ -47,7 +49,7 @@ def assert_startswith(thing: str, prefix: str) -> None:
assert False, '%r does not start with %r' % (thing, prefix) assert False, '%r does not start with %r' % (thing, prefix)
def assert_node(node: nodes.Node, cls: Any = None, xpath: str = "", **kwargs: Any) -> None: def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: 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)
@ -101,6 +103,8 @@ class SphinxTestApp(application.Sphinx):
A subclass of :class:`Sphinx` that runs on the test root, with some A subclass of :class:`Sphinx` that runs on the test root, with some
better default values for the initialization parameters. better default values for the initialization parameters.
""" """
_status = None # type: StringIO
_warning = None # type: StringIO
def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False, def __init__(self, buildername: str = 'html', srcdir: path = None, freshenv: bool = False,
confoverrides: Dict = None, status: IO = None, warning: IO = None, confoverrides: Dict = None, status: IO = None, warning: IO = None,