From 75465458764a7d8d6dc80a0cfcf2d6a0af18e71b Mon Sep 17 00:00:00 2001 From: danieleades <33452915+danieleades@users.noreply.github.com> Date: Thu, 3 Oct 2024 20:17:40 +0100 Subject: [PATCH] Further improve typing in `tests/` (#12816) Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- pyproject.toml | 3 -- tests/test_events.py | 9 +++-- .../test_ext_autodoc_autofunction.py | 4 ++- tests/test_extensions/test_ext_napoleon.py | 5 ++- tests/test_quickstart.py | 33 +++++++++++-------- tests/test_search.py | 28 +++++++++------- tests/test_util/test_util_fileutil.py | 4 +-- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9e00b59c2..aa650803a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,7 +146,6 @@ files = [ exclude = [ "tests/roots", # tests/ - "^tests/test_events\\.py$", "^tests/test_quickstart\\.py$", "^tests/test_search\\.py$", # tests/test_builders @@ -175,14 +174,12 @@ exclude = [ # tests/test_extensions "^tests/test_extensions/test_ext_apidoc\\.py$", "^tests/test_extensions/test_ext_autodoc\\.py$", - "^tests/test_extensions/test_ext_autodoc_autofunction\\.py$", "^tests/test_extensions/test_ext_autodoc_events\\.py$", "^tests/test_extensions/test_ext_autodoc_mock\\.py$", "^tests/test_extensions/test_ext_autosummary\\.py$", "^tests/test_extensions/test_ext_doctest\\.py$", "^tests/test_extensions/test_ext_inheritance_diagram\\.py$", "^tests/test_extensions/test_ext_intersphinx\\.py$", - "^tests/test_extensions/test_ext_napoleon\\.py$", "^tests/test_extensions/test_ext_napoleon_docstring\\.py$", # tests/test_intl "^tests/test_intl/test_intl\\.py$", diff --git a/tests/test_events.py b/tests/test_events.py index 685646f4d..df175787a 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -8,7 +8,8 @@ from sphinx.events import EventManager def test_event_priority(): result = [] - events = EventManager(object()) # pass an dummy object as an app + app = object() # pass a dummy object as an app + events = EventManager(app) # type: ignore[arg-type] events.connect('builder-inited', lambda app: result.append(1), priority=500) events.connect('builder-inited', lambda app: result.append(2), priority=500) # earlier @@ -30,7 +31,8 @@ def test_event_allowed_exceptions(): def raise_error(app): raise RuntimeError - events = EventManager(FakeApp()) # pass an dummy object as an app + app = FakeApp() # pass a dummy object as an app + events = EventManager(app) # type: ignore[arg-type] events.connect('builder-inited', raise_error, priority=500) # all errors are converted to ExtensionError @@ -46,7 +48,8 @@ def test_event_pdb(): def raise_error(app): raise RuntimeError - events = EventManager(FakeApp(pdb=True)) # pass an dummy object as an app + app = FakeApp(pdb=True) # pass a dummy object as an app + events = EventManager(app) # type: ignore[arg-type] events.connect('builder-inited', raise_error, priority=500) # errors aren't converted diff --git a/tests/test_extensions/test_ext_autodoc_autofunction.py b/tests/test_extensions/test_ext_autodoc_autofunction.py index 5dfa42d5a..e70069bfd 100644 --- a/tests/test_extensions/test_ext_autodoc_autofunction.py +++ b/tests/test_extensions/test_ext_autodoc_autofunction.py @@ -4,6 +4,8 @@ This tests mainly the Documenters; the auto directives are tested in a test source file translated by test_build. """ +from typing import Any + import pytest from tests.test_extensions.autodoc_util import do_autodoc @@ -109,7 +111,7 @@ def test_decorated(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_singledispatch(app): - options = {} + options: dict[str, Any] = {} actual = do_autodoc(app, 'function', 'target.singledispatch.func', options) assert list(actual) == [ '', diff --git a/tests/test_extensions/test_ext_napoleon.py b/tests/test_extensions/test_ext_napoleon.py index 9fd9c2d12..59477dfe0 100644 --- a/tests/test_extensions/test_ext_napoleon.py +++ b/tests/test_extensions/test_ext_napoleon.py @@ -2,7 +2,6 @@ import functools from collections import namedtuple -from typing import Any from unittest import mock import pytest @@ -111,7 +110,7 @@ class TestProcessDocstring: class TestSetup: def test_unknown_app_type(self): - setup(object()) + setup(object()) # type: ignore[arg-type] def test_add_config_values(self): app = mock.Mock(Sphinx) @@ -146,7 +145,7 @@ class TestSkipMember: self, what: str, member: str, - obj: Any, + obj: object, expect_default_skip: bool, config_name: str, ) -> None: diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 7634637b3..0507d0c82 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -1,8 +1,11 @@ """Test the sphinx.quickstart module.""" import time +from collections.abc import Callable from io import StringIO from os import path +from pathlib import Path +from typing import Any import pytest @@ -17,10 +20,12 @@ def setup_module(): nocolor() -def mock_input(answers, needanswer=False): +def mock_input( + answers: dict[str, str], needanswer: bool = False +) -> Callable[[str], str]: called = set() - def input_(prompt): + def input_(prompt: str) -> str: if prompt in called: raise AssertionError( 'answer for %r missing and no default present' % prompt @@ -36,7 +41,7 @@ def mock_input(answers, needanswer=False): return input_ -real_input = input +real_input: Callable[[str], str] = input def teardown_module(): @@ -95,13 +100,13 @@ def test_quickstart_defaults(tmp_path): 'Project version': '0.1', } qs.term_input = mock_input(answers) - d = {} + d: dict[str, Any] = {} qs.ask_user(d) qs.generate(d) conffile = tmp_path / 'conf.py' assert conffile.is_file() - ns = {} + ns: dict[str, Any] = {} exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 assert ns['extensions'] == [] assert ns['templates_path'] == ['_templates'] @@ -145,13 +150,13 @@ def test_quickstart_all_answers(tmp_path): 'Do you want to use the epub builder': 'yes', } qs.term_input = mock_input(answers, needanswer=True) - d = {} + d: dict[str, Any] = {} qs.ask_user(d) qs.generate(d) conffile = tmp_path / 'source' / 'conf.py' assert conffile.is_file() - ns = {} + ns: dict[str, Any] = {} exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 assert ns['extensions'] == [ 'sphinx.ext.autodoc', @@ -184,11 +189,11 @@ def test_generated_files_eol(tmp_path): 'Project version': '0.1', } qs.term_input = mock_input(answers) - d = {} + d: dict[str, Any] = {} qs.ask_user(d) qs.generate(d) - def assert_eol(filename, eol): + def assert_eol(filename: Path, eol: str) -> None: content = filename.read_bytes().decode() assert all(l[-len(eol) :] == eol for l in content.splitlines(keepends=True)) @@ -204,7 +209,7 @@ def test_quickstart_and_build(tmp_path): 'Project version': '0.1', } qs.term_input = mock_input(answers) - d = {} + d: dict[str, Any] = {} qs.ask_user(d) qs.generate(d) @@ -223,13 +228,13 @@ def test_default_filename(tmp_path): 'Project version': '0.1', } qs.term_input = mock_input(answers) - d = {} + d: dict[str, Any] = {} qs.ask_user(d) qs.generate(d) conffile = tmp_path / 'conf.py' assert conffile.is_file() - ns = {} + ns: dict[str, Any] = {} exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 @@ -247,7 +252,7 @@ def test_extensions(tmp_path): conffile = tmp_path / 'conf.py' assert conffile.is_file() - ns = {} + ns: dict[str, Any] = {} exec(conffile.read_text(encoding='utf8'), ns) # NoQA: S102 assert ns['extensions'] == ['foo', 'bar', 'baz'] @@ -263,6 +268,6 @@ def test_exits_when_existing_confpy(monkeypatch): qs.term_input = mock_input({ 'Please enter a new root path (or just Enter to exit)': '', }) - d = {} + d: dict[str, Any] = {} with pytest.raises(SystemExit): qs.ask_user(d) diff --git a/tests/test_search.py b/tests/test_search.py index f350d192a..31918587a 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -17,6 +17,10 @@ from tests.utils import TESTS_ROOT if TYPE_CHECKING: from collections.abc import Iterator + from pathlib import Path + from typing import Any + + from sphinx.domains import ObjType JAVASCRIPT_TEST_ROOTS = [ directory @@ -52,9 +56,9 @@ class DummyDomain: def __init__(self, name: str, data: dict) -> None: self.name = name self.data = data - self.object_types = {} + self.object_types: dict[str, ObjType] = {} - def get_objects(self): + def get_objects(self) -> list[tuple[str, str, str, str, str, int]]: return self.data @@ -72,7 +76,7 @@ def setup_module(): parser = rst.Parser() -def load_searchindex(path): +def load_searchindex(path: Path) -> Any: searchindex = path.read_text(encoding='utf8') assert searchindex.startswith('Search.setIndex(') assert searchindex.endswith(')') @@ -80,7 +84,7 @@ def load_searchindex(path): return json.loads(searchindex[16:-1]) -def is_registered_term(index, keyword): +def is_registered_term(index: Any, keyword: str) -> bool: return index['terms'].get(keyword, []) != [] @@ -191,12 +195,12 @@ def test_IndexBuilder(): ], ) env = DummyEnvironment('1.0', DummyDomainsContainer(dummy1=domain1, dummy2=domain2)) - doc = utils.new_document(b'test data', settings) + doc = utils.new_document('test data', settings) doc['file'] = 'dummy' parser.parse(FILE_CONTENTS, doc) # feed - index = IndexBuilder(env, 'en', {}, None) + index = IndexBuilder(env, 'en', {}, '') index.feed('docname1_1', 'filename1_1', 'title1_1', doc) index.feed('docname1_2', 'filename1_2', 'title1_2', doc) index.feed('docname2_2', 'filename2_2', 'title2_2', doc) @@ -285,7 +289,7 @@ def test_IndexBuilder(): index.dump(stream, 'pickle') stream.seek(0) - index2 = IndexBuilder(env, 'en', {}, None) + index2 = IndexBuilder(env, 'en', {}, '') index2.load(stream, 'pickle') assert index2._titles == index._titles @@ -366,11 +370,11 @@ def test_IndexBuilder_lookup(): env = DummyEnvironment('1.0', {}) # zh - index = IndexBuilder(env, 'zh', {}, None) + index = IndexBuilder(env, 'zh', {}, '') assert index.lang.lang == 'zh' # zh_CN - index = IndexBuilder(env, 'zh_CN', {}, None) + index = IndexBuilder(env, 'zh_CN', {}, '') assert index.lang.lang == 'zh' @@ -426,7 +430,7 @@ def test_search_index_is_deterministic(app): assert_is_sorted(index, '') -def is_title_tuple_type(item: list[int | str]): +def is_title_tuple_type(item: list[int | str]) -> bool: """ In the search index, titles inside .alltitles are stored as a tuple of (document_idx, title_anchor). Tuples are represented as lists in JSON, @@ -436,7 +440,9 @@ def is_title_tuple_type(item: list[int | str]): return len(item) == 2 and isinstance(item[0], int) and isinstance(item[1], str) -def assert_is_sorted(item, path: str): +def assert_is_sorted( + item: dict[str, str] | list[int | str] | int | str, path: str +) -> None: lists_not_to_sort = { # Each element of .titles is related to the element of .docnames in the same position. # The ordering is deterministic because .docnames is sorted. diff --git a/tests/test_util/test_util_fileutil.py b/tests/test_util/test_util_fileutil.py index 33633a8b0..7ffa83fc2 100644 --- a/tests/test_util/test_util_fileutil.py +++ b/tests/test_util/test_util_fileutil.py @@ -6,12 +6,12 @@ from unittest import mock import pytest from sphinx.jinja2glue import BuiltinTemplateLoader -from sphinx.util import strip_colors +from sphinx.util.console import strip_colors from sphinx.util.fileutil import _template_basename, copy_asset, copy_asset_file class DummyTemplateLoader(BuiltinTemplateLoader): - def __init__(self): + def __init__(self) -> None: super().__init__() builder = mock.Mock() builder.config.templates_path = []