Skip to content

Commit

Permalink
[lint] whitelist all test files except configuration and utils in tes…
Browse files Browse the repository at this point in the history
…ts (#12109)
  • Loading branch information
picnixz committed Mar 16, 2024
1 parent 6aaeee2 commit 2df5b0a
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 25 deletions.
151 changes: 150 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ exclude = [
]

[tool.mypy]
files = ["sphinx", "utils"]
files = ["sphinx", "utils", "tests"]
exclude = ["tests/certs", "tests/js", "tests/roots"]
check_untyped_defs = true
disallow_incomplete_defs = true
python_version = "3.9"
Expand Down Expand Up @@ -212,6 +213,154 @@ module = [
]
disallow_any_generics = false

[[tool.mypy.overrides]]
module = [
# tests/
# "tests.conftest",
"tests.test_addnodes",
"tests.test_application",
"tests.test_errors",
"tests.test_events",
"tests.test_highlighting",
"tests.test_project",
"tests.test_quickstart",
"tests.test_roles",
"tests.test_search",
"tests.test_toctree",
"tests.test_versioning",
# "tests.utils",
# tests/test_builders
# "tests.test_builders.conftest",
"tests.test_builders.test_build",
"tests.test_builders.test_build_changes",
"tests.test_builders.test_build_dirhtml",
"tests.test_builders.test_build_epub",
"tests.test_builders.test_builder",
"tests.test_builders.test_build_gettext",
"tests.test_builders.test_build_html",
"tests.test_builders.test_build_html_5_output",
"tests.test_builders.test_build_html_assets",
"tests.test_builders.test_build_html_code",
"tests.test_builders.test_build_html_download",
"tests.test_builders.test_build_html_highlight",
"tests.test_builders.test_build_html_image",
"tests.test_builders.test_build_html_maths",
"tests.test_builders.test_build_html_numfig",
"tests.test_builders.test_build_html_tocdepth",
"tests.test_builders.test_build_latex",
"tests.test_builders.test_build_linkcheck",
"tests.test_builders.test_build_manpage",
"tests.test_builders.test_build_texinfo",
"tests.test_builders.test_build_text",
"tests.test_builders.test_build_warnings",
# tests/test_config
"tests.test_config.test_config",
"tests.test_config.test_correct_year",
# tests/test_directives
"tests.test_directives.test_directive_code",
"tests.test_directives.test_directive_object_description",
"tests.test_directives.test_directive_only",
"tests.test_directives.test_directive_option",
"tests.test_directives.test_directive_other",
"tests.test_directives.test_directive_patch",
"tests.test_directives.test_directives_no_typesetting",
# tests/test_domains
"tests.test_domains.test_domain_c",
"tests.test_domains.test_domain_cpp",
"tests.test_domains.test_domain_js",
"tests.test_domains.test_domain_py",
"tests.test_domains.test_domain_py_canonical",
"tests.test_domains.test_domain_py_fields",
"tests.test_domains.test_domain_py_pyfunction",
"tests.test_domains.test_domain_py_pyobject",
"tests.test_domains.test_domain_rst",
"tests.test_domains.test_domain_std",
# tests/test_environment
"tests.test_environment.test_environment",
"tests.test_environment.test_environment_indexentries",
"tests.test_environment.test_environment_record_dependencies",
"tests.test_environment.test_environment_toctree",
# tests/test_extensions
"tests.test_extensions.ext_napoleon_pep526_data_google",
"tests.test_extensions.ext_napoleon_pep526_data_numpy",
"tests.test_extensions.test_ext_apidoc",
"tests.test_extensions.test_ext_autodoc",
"tests.test_extensions.test_ext_autodoc_autoattribute",
"tests.test_extensions.test_ext_autodoc_autoclass",
"tests.test_extensions.test_ext_autodoc_autodata",
"tests.test_extensions.test_ext_autodoc_autofunction",
"tests.test_extensions.test_ext_autodoc_automodule",
"tests.test_extensions.test_ext_autodoc_autoproperty",
"tests.test_extensions.test_ext_autodoc_configs",
"tests.test_extensions.test_ext_autodoc_events",
"tests.test_extensions.test_ext_autodoc_mock",
"tests.test_extensions.test_ext_autodoc_preserve_defaults",
"tests.test_extensions.test_ext_autodoc_private_members",
"tests.test_extensions.test_ext_autosectionlabel",
"tests.test_extensions.test_ext_autosummary",
"tests.test_extensions.test_ext_coverage",
"tests.test_extensions.test_ext_doctest",
"tests.test_extensions.test_ext_duration",
"tests.test_extensions.test_extension",
"tests.test_extensions.test_ext_extlinks",
"tests.test_extensions.test_ext_githubpages",
"tests.test_extensions.test_ext_graphviz",
"tests.test_extensions.test_ext_ifconfig",
"tests.test_extensions.test_ext_imgconverter",
"tests.test_extensions.test_ext_imgmockconverter",
"tests.test_extensions.test_ext_inheritance_diagram",
"tests.test_extensions.test_ext_intersphinx",
"tests.test_extensions.test_ext_math",
"tests.test_extensions.test_ext_napoleon",
"tests.test_extensions.test_ext_napoleon_docstring",
"tests.test_extensions.test_ext_todo",
"tests.test_extensions.test_ext_viewcode",
# tests/test_intl
"tests.test_intl.test_catalogs",
"tests.test_intl.test_intl",
"tests.test_intl.test_locale",
# tests/test_markup
"tests.test_markup.test_markup",
"tests.test_markup.test_metadata",
"tests.test_markup.test_parser",
"tests.test_markup.test_smartquotes",
# tests/test_pycode
"tests.test_pycode.test_pycode",
"tests.test_pycode.test_pycode_ast",
"tests.test_pycode.test_pycode_parser",
# tests/test_theming
"tests.test_theming.test_html_theme",
"tests.test_theming.test_templating",
"tests.test_theming.test_theming",
# tests/test_transforms
"tests.test_transforms.test_transforms_move_module_targets",
"tests.test_transforms.test_transforms_post_transforms",
"tests.test_transforms.test_transforms_post_transforms_code",
"tests.test_transforms.test_transforms_reorder_nodes",
# tests/test_util
"tests.test_util.test_util",
"tests.test_util.test_util_display",
"tests.test_util.test_util_docstrings",
"tests.test_util.test_util_docutils",
"tests.test_util.test_util_fileutil",
"tests.test_util.test_util_i18n",
"tests.test_util.test_util_images",
"tests.test_util.test_util_inspect",
"tests.test_util.test_util_inventory",
"tests.test_util.test_util_logging",
"tests.test_util.test_util_matching",
"tests.test_util.test_util_nodes",
"tests.test_util.test_util_rst",
"tests.test_util.test_util_template",
"tests.test_util.test_util_typing",
"tests.test_util.typing_test_data",
# tests/test_writers
"tests.test_writers.test_api_translator",
"tests.test_writers.test_docutilsconf",
"tests.test_writers.test_writer_latex",
]
ignore_errors = true

[tool.pytest.ini_options]
minversion = 6.0
addopts = [
Expand Down
18 changes: 13 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations

import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING

import docutils
import pytest
Expand All @@ -10,8 +13,13 @@
import sphinx.pycode
from sphinx.testing.util import _clean_up_global_state

if TYPE_CHECKING:
from collections.abc import Generator


def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):
def _init_console(
locale_dir: str | None = sphinx.locale._LOCALE_DIR, catalog: str = 'sphinx',
) -> tuple[sphinx.locale.NullTranslations, bool]:
"""Monkeypatch ``init_console`` to skip its action.
Some tests rely on warning messages in English. We don't want
Expand All @@ -23,7 +31,7 @@ def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):

sphinx.locale.init_console = _init_console

pytest_plugins = 'sphinx.testing.fixtures'
pytest_plugins = ['sphinx.testing.fixtures']

# Exclude 'roots' dirs for pytest test collector
collect_ignore = ['roots']
Expand All @@ -32,19 +40,19 @@ def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):


@pytest.fixture(scope='session')
def rootdir():
def rootdir() -> Path:
return Path(__file__).parent.resolve() / 'roots'


def pytest_report_header(config):
def pytest_report_header(config: pytest.Config) -> str:
header = f"libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}"
if hasattr(config, '_tmp_path_factory'):
header += f"\nbase tmp_path: {config._tmp_path_factory.getbasetemp()}"
return header


@pytest.fixture(autouse=True)
def _cleanup_docutils():
def _cleanup_docutils() -> Generator[None, None, None]:
saved_path = sys.path
yield # run the test
sys.path[:] = saved_path
Expand Down
8 changes: 5 additions & 3 deletions tests/test_builders/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
from html5lib import HTMLParser

if TYPE_CHECKING:
from collections.abc import Callable, Generator
from pathlib import Path
from xml.etree.ElementTree import Element

etree_cache = {}
etree_cache: dict[Path, Element] = {}


def _parse(fname: Path) -> HTMLParser:
def _parse(fname: Path) -> Element:
if fname in etree_cache:
return etree_cache[fname]
with fname.open('rb') as fp:
Expand All @@ -21,6 +23,6 @@ def _parse(fname: Path) -> HTMLParser:


@pytest.fixture(scope='package')
def cached_etree_parse():
def cached_etree_parse() -> Generator[Callable[[Path], Element], None, None]:
yield _parse
etree_cache.clear()
49 changes: 33 additions & 16 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,71 @@
from __future__ import annotations

import contextlib
import http.server
import pathlib
import threading
from http.server import ThreadingHTTPServer
from pathlib import Path
from ssl import PROTOCOL_TLS_SERVER, SSLContext
from threading import Thread
from typing import TYPE_CHECKING, TypeVar

import filelock

if TYPE_CHECKING:
from collections.abc import Callable, Generator
from contextlib import AbstractContextManager
from socketserver import BaseRequestHandler
from typing import Any, Final

# Generated with:
# $ openssl req -new -x509 -days 3650 -nodes -out cert.pem \
# -keyout cert.pem -addext "subjectAltName = DNS:localhost"
TESTS_ROOT = pathlib.Path(__file__).parent
CERT_FILE = str(TESTS_ROOT / "certs" / "cert.pem")
TESTS_ROOT: Final[Path] = Path(__file__).parent
CERT_FILE: Final[str] = str(TESTS_ROOT / "certs" / "cert.pem")

# File lock for tests
LOCK_PATH = str(TESTS_ROOT / 'test-server.lock')
LOCK_PATH: Final[str] = str(TESTS_ROOT / 'test-server.lock')


class HttpServerThread(threading.Thread):
def __init__(self, handler, *args, **kwargs):
class HttpServerThread(Thread):
def __init__(self, handler: type[BaseRequestHandler], /, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.server = http.server.ThreadingHTTPServer(("localhost", 7777), handler)
self.server = ThreadingHTTPServer(("localhost", 7777), handler)

def run(self):
def run(self) -> None:
self.server.serve_forever(poll_interval=0.001)

def terminate(self):
def terminate(self) -> None:
self.server.shutdown()
self.server.server_close()
self.join()


class HttpsServerThread(HttpServerThread):
def __init__(self, handler, *args, **kwargs):
def __init__(
self, handler: type[BaseRequestHandler], /, *args: Any, **kwargs: Any,
) -> None:
super().__init__(handler, *args, **kwargs)
sslcontext = SSLContext(PROTOCOL_TLS_SERVER)
sslcontext.load_cert_chain(CERT_FILE)
self.server.socket = sslcontext.wrap_socket(self.server.socket, server_side=True)


def create_server(thread_class):
def server(handler):
_T_co = TypeVar('_T_co', bound=HttpServerThread, covariant=True)


def create_server(
server_thread_class: type[_T_co],
) -> Callable[[type[BaseRequestHandler]], AbstractContextManager[_T_co]]:
@contextlib.contextmanager
def server(handler_class: type[BaseRequestHandler]) -> Generator[_T_co, None, None]:
lock = filelock.FileLock(LOCK_PATH)
with lock:
server_thread = thread_class(handler, daemon=True)
server_thread = server_thread_class(handler_class, daemon=True)
server_thread.start()
try:
yield server_thread
finally:
server_thread.terminate()
return contextlib.contextmanager(server)
return server


http_server = create_server(HttpServerThread)
Expand Down

0 comments on commit 2df5b0a

Please sign in to comment.