Remove 'untyped_defs' mypy overrides (#14103)

This commit is contained in:
Adam Turner authored and GitHub committed 2025-11-27 21:16:15 +00:00
1 parent 227b230be8
commit a82b34feea
13 files changed
+336 -177

No files matched your search

-26
View File
@@ -217,32 +217,6 @@ module = [
]
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = [
# tests/
"tests.test_versioning",
# tests/test_builders
"tests.test_builders.test_build",
"tests.test_builders.test_build_html",
"tests.test_builders.test_build_linkcheck",
# tests/test_directives
"tests.test_directives.test_directive_code",
"tests.test_directives.test_directives_no_typesetting",
# tests/test_ext_autodoc
"tests.test_ext_autodoc.test_ext_autodoc_autoclass",
# tests/test_ext_autosummary
"tests.test_ext_autosummary.test_ext_autosummary_imports",
# tests/test_ext_intersphinx
"tests.test_ext_intersphinx.test_ext_intersphinx_cache",
# tests/test_ext_napoleon
"tests.test_ext_napoleon.test_ext_napoleon",
# tests/test_extensions
"tests.test_extensions.test_ext_math",
# tests/test_theming
"tests.test_theming.test_templating",
]
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = [
# tests/
+9 -2
View File
@@ -10,10 +10,15 @@ from docutils import nodes
from sphinx.errors import SphinxError
if TYPE_CHECKING:
from collections.abc import Callable
from pathlib import Path
from sphinx.testing.util import SphinxTestApp
def test_root_doc_not_found(tmp_path, make_app):
def test_root_doc_not_found(
tmp_path: Path, make_app: Callable[..., SphinxTestApp]
) -> None:
(tmp_path / 'conf.py').touch()
assert [p.name for p in tmp_path.iterdir()] == ['conf.py']
@@ -56,11 +61,12 @@ def test_multiple_parents_toctree(app: SphinxTestApp) -> None:
@pytest.mark.usefixtures('_http_teapot')
@pytest.mark.sphinx('dummy', testroot='images')
def test_image_glob(app):
def test_image_glob(app: SphinxTestApp) -> None:
app.build(force_all=True)
# index.rst
doctree = app.env.get_doctree('index')
assert isinstance(doctree[0], nodes.Element)
assert isinstance(doctree[0][1], nodes.image)
assert doctree[0][1]['candidates'] == {'*': 'rimg.png'}
@@ -90,6 +96,7 @@ def test_image_glob(app):
# subdir/index.rst
doctree = app.env.get_doctree('subdir/index')
assert isinstance(doctree[0], nodes.Element)
assert isinstance(doctree[0][1], nodes.image)
assert doctree[0][1]['candidates'] == {'*': 'subdir/rimg.png'}
+71 -23
View File
@@ -30,10 +30,17 @@ from tests.test_builders.xpath_data import FIGURE_CAPTION
from tests.test_builders.xpath_util import check_xpath
if TYPE_CHECKING:
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import Any
from xml.etree.ElementTree import Element, ElementTree
from sphinx.testing.util import SphinxTestApp
def test_html_sidebars_error(make_app, tmp_path):
def test_html_sidebars_error(
make_app: Callable[..., SphinxTestApp], tmp_path: Path
) -> None:
(tmp_path / 'conf.py').touch()
with pytest.raises(
ConfigError,
@@ -48,7 +55,7 @@ def test_html_sidebars_error(make_app, tmp_path):
)
def test_html4_error(make_app, tmp_path):
def test_html4_error(make_app: Callable[..., SphinxTestApp], tmp_path: Path) -> None:
(tmp_path / 'conf.py').touch()
with pytest.raises(
ConfigError,
@@ -137,7 +144,13 @@ def test_html4_error(make_app, tmp_path):
)
@pytest.mark.sphinx('html', testroot='root')
@pytest.mark.test_params(shared_result='test_build_html_output_docutils18')
def test_docutils_output(app, cached_etree_parse, fname, path, check):
def test_docutils_output(
app: SphinxTestApp,
cached_etree_parse: Callable[[Path], ElementTree],
fname: str,
path: str,
check: str,
) -> None:
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, path, check)
@@ -186,7 +199,7 @@ def test_html_translator(app: SphinxTestApp) -> None:
@pytest.mark.parametrize(
'expect',
('path', 'check', 'be_found'),
[
(FIGURE_CAPTION + "//span[@class='caption-number']", 'Fig. 1', True),
(FIGURE_CAPTION + "//span[@class='caption-number']", 'Fig. 2', True),
@@ -205,9 +218,21 @@ def test_html_translator(app: SphinxTestApp) -> None:
testroot='add_enumerable_node',
srcdir='test_enumerable_node',
)
def test_enumerable_node(app, cached_etree_parse, expect):
def test_enumerable_node(
app: SphinxTestApp,
cached_etree_parse: Callable[[Path], ElementTree],
path: str,
check: str,
be_found: bool,
) -> None:
app.build()
check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect)
check_xpath(
cached_etree_parse(app.outdir / 'index.html'),
'index.html',
path,
check,
be_found,
)
@pytest.mark.sphinx(
@@ -336,7 +361,7 @@ def test_html_raw_directive(app: SphinxTestApp) -> None:
@pytest.mark.parametrize(
'expect',
('path', 'check', 'be_found'),
[
(".//link[@href='_static/persistent.css'][@rel='stylesheet']", '', True),
(
@@ -383,9 +408,21 @@ def test_html_raw_directive(app: SphinxTestApp) -> None:
],
)
@pytest.mark.sphinx('html', testroot='stylesheets')
def test_alternate_stylesheets(app, cached_etree_parse, expect):
def test_alternate_stylesheets(
app: SphinxTestApp,
cached_etree_parse: Callable[[Path], ElementTree],
path: str,
check: str,
be_found: bool,
) -> None:
app.build()
check_xpath(cached_etree_parse(app.outdir / 'index.html'), 'index.html', *expect)
check_xpath(
cached_etree_parse(app.outdir / 'index.html'),
'index.html',
path,
check,
be_found,
)
@pytest.mark.sphinx('html', testroot='html_style')
@@ -478,12 +515,12 @@ def test_html_sidebar(app: SphinxTestApp) -> None:
@pytest.mark.parametrize(
('fname', 'expect'),
('fname', 'path', 'check', 'be_found'),
[
('index.html', (".//h1/em/a[@href='https://example.com/cp.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/man.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/ls.1']", '', True)),
('index.html', (".//em/a[@href='https://example.com/sphinx.']", '', True)),
('index.html', ".//h1/em/a[@href='https://example.com/cp.1']", '', True),
('index.html', ".//em/a[@href='https://example.com/man.1']", '', True),
('index.html', ".//em/a[@href='https://example.com/ls.1']", '', True),
('index.html', ".//em/a[@href='https://example.com/sphinx.']", '', True),
],
)
@pytest.mark.sphinx(
@@ -492,9 +529,16 @@ def test_html_sidebar(app: SphinxTestApp) -> None:
confoverrides={'manpages_url': 'https://example.com/{page}.{section}'},
)
@pytest.mark.test_params(shared_result='test_build_html_manpage_url')
def test_html_manpage(app, cached_etree_parse, fname, expect):
def test_html_manpage(
app: SphinxTestApp,
cached_etree_parse: Callable[[Path], ElementTree],
fname: str,
path: str,
check: str,
be_found: bool,
) -> None:
app.build()
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
check_xpath(cached_etree_parse(app.outdir / fname), fname, path, check, be_found)
@pytest.mark.sphinx(
@@ -629,7 +673,7 @@ def test_html_remove_sources_before_write_gh_issue_10786(app: SphinxTestApp) ->
# See: https://github.com/sphinx-doc/sphinx/issues/10786
target = app.srcdir / 'img.png'
def handler(app):
def handler(app: SphinxTestApp) -> list[tuple[str, dict[str, Any], str]]:
assert target.exists()
target.unlink()
return []
@@ -651,7 +695,9 @@ def test_html_remove_sources_before_write_gh_issue_10786(app: SphinxTestApp) ->
testroot='domain-py-python_maximum_signature_line_length',
confoverrides={'python_maximum_signature_line_length': 1},
)
def test_html_pep_695_one_type_per_line(app, cached_etree_parse):
def test_html_pep_695_one_type_per_line(
app: SphinxTestApp, cached_etree_parse: Callable[[Path], ElementTree]
) -> None:
app.build()
fname = app.outdir / 'index.html'
etree = cached_etree_parse(fname)
@@ -660,7 +706,7 @@ def test_html_pep_695_one_type_per_line(app, cached_etree_parse):
def __init__(self, expect: str) -> None:
self.expect = expect
def __call__(self, nodes):
def __call__(self, nodes: Sequence[Element]) -> None:
assert len(nodes) == 1, nodes
objnode = ''.join(nodes[0].itertext()).replace('\n\n', '')
objnode = objnode.rstrip(chr(182)) # remove '¶' symbol
@@ -719,7 +765,7 @@ def test_html_pep_695_trailing_comma_in_multi_line_signatures(
def __init__(self, expect: str) -> None:
self.expect = expect
def __call__(self, nodes):
def __call__(self, nodes: Sequence[Element]) -> None:
assert len(nodes) == 1, nodes
objnode = ''.join(nodes[0].itertext()).replace('\n\n', '')
objnode = objnode.rstrip(chr(182)) # remove '¶' symbol
@@ -765,15 +811,17 @@ def test_html_admonition_collapse(app: SphinxTestApp) -> None:
fname = app.outdir / 'index.html'
etree = etree_parse(fname)
def _create_check(text: str, open: bool): # type: ignore[no-untyped-def]
def _check(els):
def _create_check(text: str, open: bool) -> Callable[[Sequence[Element]], None]:
def _check(els: Sequence[Element]) -> None:
assert len(els) == 1
el = els[0]
if open:
assert el.attrib['open'] == 'open'
else:
assert 'open' not in el.attrib
assert el.find('p').text == text
p = el.find('p')
assert p is not None
assert p.text == text
return _check
+62 -41
View File
@@ -40,7 +40,7 @@ ts_re = re.compile(r'.*\[(?P<ts>.*)\].*')
if TYPE_CHECKING:
from collections.abc import Callable, Iterable
from pathlib import Path
from typing import Any
from typing import Any, Self
from urllib3 import HTTPConnectionPool
@@ -53,7 +53,7 @@ if TYPE_CHECKING:
class DefaultsHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_HEAD(self):
def do_HEAD(self) -> None:
if self.path[1:].rstrip() in {'', 'anchor.html'}:
self.send_response(200, 'OK')
self.send_header('Content-Length', '0')
@@ -63,7 +63,7 @@ class DefaultsHandler(BaseHTTPRequestHandler):
self.send_header('Content-Length', '0')
self.end_headers()
def do_GET(self):
def do_GET(self) -> None:
if self.path[1:].rstrip() == '':
content = b'ok\n\n'
elif self.path[1:].rstrip() == 'anchor.html':
@@ -95,25 +95,25 @@ class ConnectionMeasurement:
new=self._collect_connections(),
)
def _collect_connections(self) -> Callable[[object, str], HTTPConnectionPool]:
def connection_collector(obj, url):
def _collect_connections(self) -> Callable[[PoolManager, str], HTTPConnectionPool]:
def connection_collector(obj: PoolManager, url: str) -> HTTPConnectionPool:
connection = self.urllib3_connection_from_url(obj, url) # type: ignore[no-untyped-call]
self.connections.add(connection)
return connection
return connection_collector
def __enter__(self):
def __enter__(self) -> Self:
self.patcher.start()
return self
def __exit__(self, *args, **kwargs):
def __exit__(self, *args: object, **kwargs: Any) -> None:
for connection in self.connections:
connection.close()
self.patcher.stop()
@property
def connection_count(self):
def connection_count(self) -> int:
return len(self.connections)
@@ -347,14 +347,14 @@ class AnchorsIgnoreForUrlHandler(BaseHTTPRequestHandler):
return False
return True
def do_HEAD(self):
def do_HEAD(self) -> None:
if self.path in {'/valid', '/ignored'}:
self.send_response(200, 'OK')
else:
self.send_response(404, 'Not Found')
self.end_headers()
def do_GET(self):
def do_GET(self) -> None:
if self.path == '/valid':
self.send_response(200, 'OK')
content = "<h1 id='valid-anchor'>valid anchor</h1>\n"
@@ -424,7 +424,7 @@ def test_raises_for_invalid_status(app: SphinxTestApp) -> None:
class InternalServerErrorHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
self.send_error(500, 'Internal Server Error')
with serve_application(app, InternalServerErrorHandler) as address:
@@ -446,7 +446,7 @@ def test_incomplete_html_anchor(app: SphinxTestApp) -> None:
class IncompleteHTMLDocumentHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
content = b'this is <div id="anchor">not</div> a valid HTML document'
self.send_response(200, 'OK')
self.send_header('Content-Length', str(len(content)))
@@ -472,7 +472,7 @@ def test_decoding_error_anchor_ignored(app: SphinxTestApp) -> None:
class NonASCIIHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
content = b'\x80\x00\x80\x00' # non-ASCII byte-string
self.send_response(200, 'OK')
self.send_header('Content-Length', str(len(content)))
@@ -491,7 +491,7 @@ def test_decoding_error_anchor_ignored(app: SphinxTestApp) -> None:
def custom_handler(
valid_credentials: tuple[str, str] | None = None,
success_criteria: Callable[[Any], bool] = lambda _: True,
success_criteria: Callable[[BaseHTTPRequestHandler], bool] = lambda _: True,
) -> type[BaseHTTPRequestHandler]:
"""Returns an HTTP request handler that authenticates the client and then determines
an appropriate HTTP response code, based on caller-provided credentials and optional
@@ -526,11 +526,11 @@ def custom_handler(
protocol_version = 'HTTP/1.1'
@authenticated
def do_HEAD(self):
def do_HEAD(self) -> None:
self.do_GET()
@authenticated
def do_GET(self):
def do_GET(self) -> None:
if success_criteria(self):
self.send_response(200, 'OK')
self.send_header('Content-Length', '0')
@@ -608,7 +608,7 @@ def test_auth_header_no_match(app: SphinxTestApp) -> None:
freshenv=True,
)
def test_linkcheck_request_headers(app: SphinxTestApp) -> None:
def check_headers(self):
def check_headers(self: BaseHTTPRequestHandler) -> bool:
if 'X-Secret' in self.headers:
return False
return self.headers['Accept'] == 'text/html'
@@ -634,7 +634,7 @@ def test_linkcheck_request_headers(app: SphinxTestApp) -> None:
freshenv=True,
)
def test_linkcheck_request_headers_no_slash(app: SphinxTestApp) -> None:
def check_headers(self):
def check_headers(self: BaseHTTPRequestHandler) -> bool:
if 'X-Secret' in self.headers:
return False
return self.headers['Accept'] == 'application/json'
@@ -666,7 +666,7 @@ def test_linkcheck_request_headers_no_slash(app: SphinxTestApp) -> None:
},
)
def test_linkcheck_request_headers_default(app: SphinxTestApp) -> None:
def check_headers(self):
def check_headers(self: BaseHTTPRequestHandler) -> bool:
if self.headers['X-Secret'] != 'open sesami':
return False
return self.headers['Accept'] != 'application/json'
@@ -701,7 +701,7 @@ def make_redirect_handler(*, support_head: bool = True) -> type[BaseHTTPRequestH
self.send_header('Content-Length', '0')
self.end_headers()
def log_date_time_string(self):
def log_date_time_string(self) -> str:
"""Strip date and time from logged messages for assertions."""
return ''
@@ -713,7 +713,9 @@ def make_redirect_handler(*, support_head: bool = True) -> type[BaseHTTPRequestH
testroot='linkcheck-localserver',
freshenv=True,
)
def test_follows_redirects_on_HEAD(app, capsys):
def test_follows_redirects_on_HEAD(
app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
with serve_application(app, make_redirect_handler(support_head=True)) as address:
compile_linkcheck_allowed_redirects(app, app.config)
app.build()
@@ -740,7 +742,9 @@ def test_follows_redirects_on_HEAD(app, capsys):
testroot='linkcheck-localserver',
freshenv=True,
)
def test_follows_redirects_on_GET(app, capsys):
def test_follows_redirects_on_GET(
app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
with serve_application(app, make_redirect_handler(support_head=False)) as address:
compile_linkcheck_allowed_redirects(app, app.config)
app.build()
@@ -769,7 +773,9 @@ def test_follows_redirects_on_GET(app, capsys):
freshenv=True,
confoverrides={'linkcheck_allowed_redirects': {}}, # warn about any redirects
)
def test_warns_disallowed_redirects(app, capsys):
def test_warns_disallowed_redirects(
app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
with serve_application(app, make_redirect_handler()) as address:
compile_linkcheck_allowed_redirects(app, app.config)
app.build()
@@ -848,12 +854,12 @@ def test_linkcheck_allowed_redirects(app: SphinxTestApp) -> None:
class OKHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_HEAD(self):
def do_HEAD(self) -> None:
self.send_response(200, 'OK')
self.send_header('Content-Length', '0')
self.end_headers()
def do_GET(self):
def do_GET(self) -> None:
content = b'ok\n'
self.send_response(200, 'OK')
self.send_header('Content-Length', str(len(content)))
@@ -867,7 +873,7 @@ class OKHandler(BaseHTTPRequestHandler):
testroot='linkcheck-localserver-https',
freshenv=True,
)
def test_invalid_ssl(get_request, app):
def test_invalid_ssl(get_request: mock.Mock, app: SphinxTestApp) -> None:
# Link indicates SSL should be used (https) but the server does not handle it.
with serve_application(app, OKHandler) as address:
app.build()
@@ -949,7 +955,9 @@ def test_connect_to_selfsigned_with_tls_cacerts(app: SphinxTestApp) -> None:
testroot='linkcheck-localserver-https',
freshenv=True,
)
def test_connect_to_selfsigned_with_requests_env_var(monkeypatch, app):
def test_connect_to_selfsigned_with_requests_env_var(
monkeypatch: pytest.MonkeyPatch, app: SphinxTestApp
) -> None:
monkeypatch.setenv('REQUESTS_CA_BUNDLE', CERT_FILE)
with serve_application(app, OKHandler, tls_enabled=True) as address:
app.build()
@@ -991,13 +999,13 @@ def test_connect_to_selfsigned_nonexistent_cert_file(app: SphinxTestApp) -> None
class InfiniteRedirectOnHeadHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_HEAD(self):
def do_HEAD(self) -> None:
self.send_response(302, 'Found')
self.send_header('Location', '/redirected')
self.send_header('Content-Length', '0')
self.end_headers()
def do_GET(self):
def do_GET(self) -> None:
content = b'ok\n'
self.send_response(200, 'OK')
self.send_header('Content-Length', str(len(content)))
@@ -1013,7 +1021,9 @@ class InfiniteRedirectOnHeadHandler(BaseHTTPRequestHandler):
testroot='linkcheck-localserver',
freshenv=True,
)
def test_TooManyRedirects_on_HEAD(app, monkeypatch):
def test_TooManyRedirects_on_HEAD(
app: SphinxTestApp, monkeypatch: pytest.MonkeyPatch
) -> None:
import requests.sessions
monkeypatch.setattr(requests.sessions, 'DEFAULT_REDIRECT_LIMIT', 5)
@@ -1054,7 +1064,7 @@ def test_ignore_local_redirection(app: SphinxTestApp) -> None:
class RemoteDomainRedirectHandler(InfiniteRedirectOnHeadHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
self.send_response(301, 'Found')
if self.path == '/':
self.send_header('Location', '/local')
@@ -1088,7 +1098,7 @@ def make_retry_after_handler(
class RetryAfterHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_HEAD(self):
def do_HEAD(self) -> None:
status, retry_after = responses.pop(0)
self.send_response(status)
if retry_after:
@@ -1096,7 +1106,7 @@ def make_retry_after_handler(
self.send_header('Content-Length', '0')
self.end_headers()
def log_date_time_string(self):
def log_date_time_string(self) -> str:
"""Strip date and time from logged messages for assertions."""
return ''
@@ -1108,7 +1118,9 @@ def make_retry_after_handler(
testroot='linkcheck-localserver',
freshenv=True,
)
def test_too_many_requests_retry_after_int_delay(app, capsys):
def test_too_many_requests_retry_after_int_delay(
app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
with (
serve_application(
app, make_retry_after_handler([(429, '0'), (200, None)])
@@ -1143,7 +1155,12 @@ def test_too_many_requests_retry_after_int_delay(app, capsys):
testroot='linkcheck-localserver',
freshenv=True,
)
def test_too_many_requests_retry_after_HTTP_date(tz, app, monkeypatch, capsys):
def test_too_many_requests_retry_after_HTTP_date(
tz: str | None,
app: SphinxTestApp,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
retry_after = wsgiref.handlers.format_date_time(time.time())
with monkeypatch.context() as m:
@@ -1189,7 +1206,9 @@ def test_too_many_requests_retry_after_HTTP_date(tz, app, monkeypatch, capsys):
testroot='linkcheck-localserver',
freshenv=True,
)
def test_too_many_requests_retry_after_without_header(app, capsys):
def test_too_many_requests_retry_after_without_header(
app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
with (
serve_application(
app, make_retry_after_handler([(429, None), (200, None)])
@@ -1228,7 +1247,7 @@ def test_requests_timeout(app: SphinxTestApp) -> None:
class DelayedResponseHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
time.sleep(0.2) # wait before sending any response data
self.send_response(200, 'OK')
self.send_header('Content-Length', '0')
@@ -1329,7 +1348,9 @@ def test_limit_rate_bails_out_after_waiting_max_time(app: SphinxTestApp) -> None
@mock.patch('sphinx.util.requests.requests.Session.get_adapter')
@pytest.mark.sphinx('html', testroot='root')
def test_connection_contention(get_adapter, app, capsys):
def test_connection_contention(
get_adapter: mock.Mock, app: SphinxTestApp, capsys: pytest.CaptureFixture[str]
) -> None:
# Create a shared, but limited-size, connection pool
import requests
@@ -1380,10 +1401,10 @@ def test_connection_contention(get_adapter, app, capsys):
class ConnectionResetHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_HEAD(self):
def do_HEAD(self) -> None:
self.close_connection = True
def do_GET(self):
def do_GET(self) -> None:
self.send_response(200, 'OK')
self.send_header('Content-Length', '0')
self.end_headers()
@@ -1446,7 +1467,7 @@ class CapitalisePathHandler(BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def do_GET(self):
def do_GET(self) -> None:
if self.path.islower():
# Redirect lowercase paths to uppercase versions
self.send_response(301, 'Moved Permanently')
+3 -3
View File
@@ -250,7 +250,7 @@ def test_LiteralIncludeReader_dedent_and_append_and_prepend(
assert content == 'class Foo:\n def baz():\n pass\n\n# comment\n'
def test_LiteralIncludeReader_tabwidth(testroot):
def test_LiteralIncludeReader_tabwidth(testroot: Path) -> None:
# tab-width: 4
options = {'tab-width': 4, 'pyobject': 'Qux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
@@ -264,14 +264,14 @@ def test_LiteralIncludeReader_tabwidth(testroot):
assert content == 'class Qux:\n def quux(self):\n pass\n'
def test_LiteralIncludeReader_tabwidth_dedent(testroot):
def test_LiteralIncludeReader_tabwidth_dedent(testroot: Path) -> None:
options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
content, _lines = reader.read()
assert content == 'def quux(self):\n pass\n'
def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
def test_LiteralIncludeReader_diff(testroot: Path, literal_inc_path: Path) -> None:
literal_diff_path = testroot / 'literal-diff.inc'
options = {'diff': literal_diff_path}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
@@ -9,6 +9,14 @@ from sphinx import addnodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import TypeAlias
from sphinx.testing.util import SphinxTestApp
_IndexEntry: TypeAlias = tuple[str, str, str, str, str | None]
DOMAINS = [
# directive, no-index, no-index-entry, signature of f, signature of g, index entry of g
(
@@ -76,8 +84,14 @@ DOMAINS = [
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
text = f'.. {directive}:: {sig_f}\n :no-typesetting:\n'
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, nodes.target))
@@ -89,8 +103,14 @@ def test_object_description_no_typesetting(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_twice(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
text = (
f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
@@ -108,8 +128,14 @@ def test_object_description_no_typesetting_twice(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_noindex_orig(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
if not no_index:
pytest.skip(f'{directive} does not support :no-index: option')
text = f'.. {directive}:: {sig_f}\n :no-index:\n.. {directive}:: {sig_g}\n'
@@ -124,8 +150,14 @@ def test_object_description_no_typesetting_noindex_orig(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_noindex(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
if not no_index:
pytest.skip(f'{directive} does not support :no-index: option')
text = (
@@ -147,8 +179,14 @@ def test_object_description_no_typesetting_noindex(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_no_index_entry(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
if not no_index_entry:
pytest.skip(f'{directive} does not support :no-index-entry: option')
text = (
@@ -170,8 +208,14 @@ def test_object_description_no_typesetting_no_index_entry(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_code(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
text = (
f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
@@ -201,8 +245,14 @@ def test_object_description_no_typesetting_code(
)
@pytest.mark.sphinx('html', testroot='root')
def test_object_description_no_typesetting_heading(
app, directive, no_index, no_index_entry, sig_f, sig_g, index_g
):
app: SphinxTestApp,
directive: str,
no_index: bool,
no_index_entry: bool,
sig_f: str,
sig_g: str,
index_g: _IndexEntry,
) -> None:
text = (
f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
@@ -12,6 +12,12 @@ import pytest
from tests.test_ext_autodoc.autodoc_util import FakeEvents, do_autodoc
TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import Any
from sphinx.application import Sphinx
pytestmark = pytest.mark.usefixtures('inject_autodoc_root_into_sys_path')
@@ -298,19 +304,22 @@ def test_show_inheritance_for_decendants_of_generic_type() -> None:
]
def _autodoc_process_bases(
app: Sphinx, name: str, obj: Any, options: dict[str, bool], bases: list[type]
) -> None:
assert name == 'target.classes.Quux'
assert obj.__module__ == 'target.classes'
assert obj.__name__ == 'Quux'
assert vars(options) == {}
assert bases == [typing.List[typing.Union[int, float]]] # NoQA: UP006, UP007
bases.pop()
bases.extend([int, str])
def test_autodoc_process_bases() -> None:
def autodoc_process_bases(app, name, obj, options, bases):
assert name == 'target.classes.Quux'
assert obj.__module__ == 'target.classes'
assert obj.__name__ == 'Quux'
assert vars(options) == {}
assert bases == [typing.List[typing.Union[int, float]]] # NoQA: UP006, UP007
bases.pop()
bases.extend([int, str])
events = FakeEvents()
events.connect('autodoc-process-bases', autodoc_process_bases)
events.connect('autodoc-process-bases', _autodoc_process_bases)
options = {'show-inheritance': None}
actual = do_autodoc('class', 'target.classes.Quux', events=events, options=options)
@@ -377,7 +386,7 @@ def test_class_doc_from_both() -> None:
def test_class_alias() -> None:
def autodoc_process_docstring(*args):
def autodoc_process_docstring(*args: Any) -> None:
"""A handler always raises an error.
This confirms this handler is never called for class aliases.
"""
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-import_cycle')
@pytest.mark.usefixtures('rollback_sysmodules')
def test_autosummary_import_cycle(app):
def test_autosummary_import_cycle(app: SphinxTestApp) -> None:
app.build()
doctree = app.env.get_doctree('index')
@@ -46,12 +46,27 @@ def test_autosummary_import_cycle(app):
addnodes.desc, # [6]
),
)
doctree_4 = doctree[4]
assert isinstance(doctree_4, nodes.Element)
doctree_4_0 = doctree_4[0]
assert isinstance(doctree_4_0, nodes.Element)
doctree_4_0_0 = doctree_4_0[0]
assert isinstance(doctree_4_0_0, nodes.Element)
doctree_4_0_0_2 = doctree_4_0_0[2]
assert isinstance(doctree_4_0_0_2, nodes.Element)
doctree_4_0_0_2_0 = doctree_4_0_0_2[0]
assert isinstance(doctree_4_0_0_2_0, nodes.Element)
doctree_4_0_0_2_0_0 = doctree_4_0_0_2_0[0]
assert isinstance(doctree_4_0_0_2_0_0, nodes.Element)
doctree_4_0_0_2_0_0_0 = doctree_4_0_0_2_0_0[0]
assert isinstance(doctree_4_0_0_2_0_0_0, nodes.Element)
assert_node(
doctree[4][0][0][2][0],
doctree_4_0_0_2[0],
([nodes.entry, nodes.paragraph, (nodes.reference, nodes.Text)], nodes.entry),
)
assert_node(
doctree[4][0][0][2][0][0][0][0],
doctree_4_0_0_2_0_0_0[0],
nodes.reference,
refid='spam.eggs.Ham',
reftitle='spam.eggs.Ham',
@@ -18,9 +18,9 @@ from tests.utils import http_server
if TYPE_CHECKING:
from collections.abc import Iterable
from pathlib import Path
from typing import Any, BinaryIO
BASE_CONFIG = {
'extensions': ['sphinx.ext.intersphinx'],
'intersphinx_timeout': 0.1,
@@ -198,7 +198,7 @@ def make_inventory_handler(
assert all(p.port == port for p in projects)
class InventoryHandler(BaseHTTPRequestHandler):
def do_GET(self):
def do_GET(self) -> None:
self.send_response(200, 'OK')
data = b''
@@ -225,7 +225,7 @@ def test_intersphinx_project_fixture() -> None:
assert project.url == 'http://localhost:9341/route'
def test_load_mappings_cache(tmp_path):
def test_load_mappings_cache(tmp_path: Path) -> None:
tmp_path.joinpath('conf.py').touch()
tmp_path.joinpath('index.rst').touch()
project = SingleEntryProject(1, 'a')
@@ -249,7 +249,7 @@ def test_load_mappings_cache(tmp_path):
assert inventories.named_inventory == {'spam': {'py:module': item}}
def test_load_mappings_cache_update(tmp_path):
def test_load_mappings_cache_update(tmp_path: Path) -> None:
tmp_path.joinpath('conf.py').touch()
tmp_path.joinpath('index.rst').touch()
old_project = SingleEntryProject(1337, 'old')
@@ -282,7 +282,7 @@ def test_load_mappings_cache_update(tmp_path):
assert inventories.named_inventory == {'spam': {'py:module': item}}
def test_load_mappings_cache_revert_update(tmp_path):
def test_load_mappings_cache_revert_update(tmp_path: Path) -> None:
tmp_path.joinpath('conf.py').touch()
tmp_path.joinpath('index.rst').touch()
old_project = SingleEntryProject(1337, 'old')
+40 -32
View File
@@ -11,69 +11,77 @@ import pytest
from sphinx.application import Sphinx
from sphinx.ext.napoleon import Config, _process_docstring, _skip_member, setup
TYPE_CHECKING = False
if TYPE_CHECKING:
from collections.abc import Callable
from typing import ParamSpec, TypeVar
def simple_decorator(f):
_P = ParamSpec('_P')
_R = TypeVar('_R')
def simple_decorator(f: Callable[_P, _R]) -> Callable[_P, _R]:
"""A simple decorator that does nothing, for tests to use."""
@functools.wraps(f)
def wrapper(*args, **kwargs):
def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
return f(*args, **kwargs)
return wrapper
def _private_doc():
def _private_doc() -> None:
"""module._private_doc.DOCSTRING"""
pass
def _private_undoc():
def _private_undoc() -> None:
pass
def __special_doc__(): # NoQA: N807
def __special_doc__() -> None: # NoQA: N807
"""module.__special_doc__.DOCSTRING"""
pass
def __special_undoc__(): # NoQA: N807
def __special_undoc__() -> None: # NoQA: N807
pass
class SampleClass:
def _private_doc(self):
def _private_doc(self) -> None:
"""SampleClass._private_doc.DOCSTRING"""
pass
def _private_undoc(self):
def _private_undoc(self) -> None:
pass
def __special_doc__(self): # NoQA: PLW3201
def __special_doc__(self) -> None: # NoQA: PLW3201
"""SampleClass.__special_doc__.DOCSTRING"""
pass
def __special_undoc__(self): # NoQA: PLW3201
def __special_undoc__(self) -> None: # NoQA: PLW3201
pass
@simple_decorator
def __decorated_func__(self): # NoQA: PLW3201
def __decorated_func__(self) -> None: # NoQA: PLW3201
"""Doc"""
pass
class SampleError(Exception):
def _private_doc(self):
def _private_doc(self) -> None:
"""SampleError._private_doc.DOCSTRING"""
pass
def _private_undoc(self):
def _private_undoc(self) -> None:
pass
def __special_doc__(self): # NoQA: PLW3201
def __special_doc__(self) -> None: # NoQA: PLW3201
"""SampleError.__special_doc__.DOCSTRING"""
pass
def __special_undoc__(self): # NoQA: PLW3201
def __special_undoc__(self) -> None: # NoQA: PLW3201
pass
@@ -81,7 +89,7 @@ SampleNamedTuple = namedtuple('SampleNamedTuple', 'user_id block_type def_id')
class TestProcessDocstring:
def test_modify_in_place(self):
def test_modify_in_place(self) -> None:
lines = [
'Summary line.',
'',
@@ -109,10 +117,10 @@ class TestProcessDocstring:
class TestSetup:
def test_unknown_app_type(self):
def test_unknown_app_type(self) -> None:
setup(object()) # type: ignore[arg-type]
def test_add_config_values(self):
def test_add_config_values(self) -> None:
app = mock.Mock(Sphinx)
setup(app)
for name, _default, _rebuild, _types in Config._config_values:
@@ -160,7 +168,7 @@ class TestSkipMember:
setattr(app.config, config_name, False)
assert None is _skip_member(app, what, member, obj, skip, mock.Mock())
def test_namedtuple(self):
def test_namedtuple(self) -> None:
# Since python 3.7, namedtuple._asdict() has not been documented
# because there is no way to check the method is a member of the
# namedtuple class. This testcase confirms only it does not
@@ -174,7 +182,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_class_private_doc(self):
def test_class_private_doc(self) -> None:
self.assert_skip(
'class',
'_private_doc',
@@ -183,7 +191,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_class_private_undoc(self):
def test_class_private_undoc(self) -> None:
self.assert_skip(
'class',
'_private_undoc',
@@ -192,7 +200,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_class_special_doc(self):
def test_class_special_doc(self) -> None:
self.assert_skip(
'class',
'__special_doc__',
@@ -201,7 +209,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_class_special_undoc(self):
def test_class_special_undoc(self) -> None:
self.assert_skip(
'class',
'__special_undoc__',
@@ -210,7 +218,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_class_decorated_doc(self):
def test_class_decorated_doc(self) -> None:
self.assert_skip(
'class',
'__decorated_func__',
@@ -219,7 +227,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_exception_private_doc(self):
def test_exception_private_doc(self) -> None:
self.assert_skip(
'exception',
'_private_doc',
@@ -228,7 +236,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_exception_private_undoc(self):
def test_exception_private_undoc(self) -> None:
self.assert_skip(
'exception',
'_private_undoc',
@@ -237,7 +245,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_exception_special_doc(self):
def test_exception_special_doc(self) -> None:
self.assert_skip(
'exception',
'__special_doc__',
@@ -246,7 +254,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_exception_special_undoc(self):
def test_exception_special_undoc(self) -> None:
self.assert_skip(
'exception',
'__special_undoc__',
@@ -255,7 +263,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_module_private_doc(self):
def test_module_private_doc(self) -> None:
self.assert_skip(
'module',
'_private_doc',
@@ -264,7 +272,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_module_private_undoc(self):
def test_module_private_undoc(self) -> None:
self.assert_skip(
'module',
'_private_undoc',
@@ -273,7 +281,7 @@ class TestSkipMember:
'napoleon_include_private_with_doc',
)
def test_module_special_doc(self):
def test_module_special_doc(self) -> None:
self.assert_skip(
'module',
'__special_doc__',
@@ -282,7 +290,7 @@ class TestSkipMember:
'napoleon_include_special_with_doc',
)
def test_module_special_undoc(self):
def test_module_special_undoc(self) -> None:
self.assert_skip(
'module',
'__special_undoc__',
+7 -3
View File
@@ -320,10 +320,14 @@ def test_imgmath_numfig_html(app: SphinxTestApp) -> None:
@pytest.mark.sphinx('dummy', testroot='ext-math-compat')
def test_math_compat(app):
def test_math_compat(app: SphinxTestApp) -> None:
with warnings.catch_warnings(record=True):
app.build(force_all=True)
doctree = app.env.get_and_resolve_doctree('index', app.builder, tags=app.tags)
doctree_0 = doctree[0]
assert isinstance(doctree_0, nodes.Element)
doctree_0_1 = doctree_0[1]
assert isinstance(doctree_0_1, nodes.Element)
assert_node(
doctree,
@@ -338,7 +342,7 @@ def test_math_compat(app):
],
)
assert_node(
doctree[0][1][1],
doctree_0_1[1],
(
'Inline: ',
[nodes.math, 'E=mc^2'],
@@ -347,7 +351,7 @@ def test_math_compat(app):
),
)
assert_node(
doctree[0][2],
doctree_0[2],
(
[nodes.title, 'block'],
[nodes.math_block, 'a^2+b^2=c^2\n\n'],
+17 -3
View File
@@ -2,11 +2,21 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
if TYPE_CHECKING:
from collections.abc import Callable
from sphinx.testing.fixtures import _app_params
from sphinx.testing.util import SphinxTestApp
@pytest.mark.sphinx('html', testroot='templating', copy_test_root=True)
def test_layout_overloading(make_app, app_params):
def test_layout_overloading(
make_app: Callable[..., SphinxTestApp], app_params: _app_params
) -> None:
args, kwargs = app_params
app = make_app(*args, **kwargs)
app.build()
@@ -16,7 +26,9 @@ def test_layout_overloading(make_app, app_params):
@pytest.mark.sphinx('html', testroot='templating', copy_test_root=True)
def test_autosummary_class_template_overloading(make_app, app_params):
def test_autosummary_class_template_overloading(
make_app: Callable[..., SphinxTestApp], app_params: _app_params
) -> None:
args, kwargs = app_params
app = make_app(*args, **kwargs)
app.build()
@@ -34,7 +46,9 @@ def test_autosummary_class_template_overloading(make_app, app_params):
confoverrides={'autosummary_context': {'sentence': 'foobar'}},
copy_test_root=True,
)
def test_autosummary_context(make_app, app_params):
def test_autosummary_context(
make_app: Callable[..., SphinxTestApp], app_params: _app_params
) -> None:
args, kwargs = app_params
app = make_app(*args, **kwargs)
app.build()
+19 -10
View File
@@ -10,11 +10,20 @@ import pytest
from sphinx.testing.util import SphinxTestApp
from sphinx.versioning import add_uids, get_ratio, merge_doctrees
TYPE_CHECKING = False
if TYPE_CHECKING:
from collections.abc import Iterator
from pathlib import Path
from docutils import nodes
from sphinx.application import Sphinx
original = original_uids = None
@pytest.fixture(scope='module', autouse=True)
def _setup_module(rootdir, sphinx_test_tempdir):
def _setup_module(rootdir: Path, sphinx_test_tempdir: Path) -> Iterator[None]:
global original, original_uids # NoQA: PLW0603
srcdir = sphinx_test_tempdir / 'test-versioning'
if not srcdir.exists():
@@ -32,11 +41,11 @@ def _setup_module(rootdir, sphinx_test_tempdir):
doctrees = {}
def on_doctree_resolved(app, doctree, docname):
def on_doctree_resolved(app: Sphinx, doctree: nodes.document, docname: str) -> None:
doctrees[docname] = doctree
def is_paragraph(node):
def is_paragraph(node: nodes.Node) -> bool:
return node.__class__.__name__ == 'paragraph'
@@ -64,7 +73,7 @@ def test_pickleablility() -> None:
def test_modified() -> None:
modified = doctrees['modified']
new_nodes = list(merge_doctrees(original, modified, is_paragraph))
uids = [n.uid for n in modified.findall(is_paragraph)]
uids = [n.uid for n in modified.findall(is_paragraph)] # type: ignore[attr-defined]
assert not new_nodes
assert original_uids == uids
@@ -72,7 +81,7 @@ def test_modified() -> None:
def test_added() -> None:
added = doctrees['added']
new_nodes = list(merge_doctrees(original, added, is_paragraph))
uids = [n.uid for n in added.findall(is_paragraph)]
uids = [n.uid for n in added.findall(is_paragraph)] # type: ignore[attr-defined]
assert len(new_nodes) == 1
assert original_uids == uids[:-1]
@@ -80,7 +89,7 @@ def test_added() -> None:
def test_deleted() -> None:
deleted = doctrees['deleted']
new_nodes = list(merge_doctrees(original, deleted, is_paragraph))
uids = [n.uid for n in deleted.findall(is_paragraph)]
uids = [n.uid for n in deleted.findall(is_paragraph)] # type: ignore[attr-defined]
assert not new_nodes
assert original_uids[::2] == uids
@@ -88,7 +97,7 @@ def test_deleted() -> None:
def test_deleted_end() -> None:
deleted_end = doctrees['deleted_end']
new_nodes = list(merge_doctrees(original, deleted_end, is_paragraph))
uids = [n.uid for n in deleted_end.findall(is_paragraph)]
uids = [n.uid for n in deleted_end.findall(is_paragraph)] # type: ignore[attr-defined]
assert not new_nodes
assert original_uids[:-1] == uids
@@ -96,7 +105,7 @@ def test_deleted_end() -> None:
def test_insert() -> None:
insert = doctrees['insert']
new_nodes = list(merge_doctrees(original, insert, is_paragraph))
uids = [n.uid for n in insert.findall(is_paragraph)]
uids = [n.uid for n in insert.findall(is_paragraph)] # type: ignore[attr-defined]
assert len(new_nodes) == 1
assert original_uids[0] == uids[0]
assert original_uids[1:] == uids[2:]
@@ -105,7 +114,7 @@ def test_insert() -> None:
def test_insert_beginning() -> None:
insert_beginning = doctrees['insert_beginning']
new_nodes = list(merge_doctrees(original, insert_beginning, is_paragraph))
uids = [n.uid for n in insert_beginning.findall(is_paragraph)]
uids = [n.uid for n in insert_beginning.findall(is_paragraph)] # type: ignore[attr-defined]
assert len(new_nodes) == 1
assert len(uids) == 4
assert original_uids == uids[1:]
@@ -115,7 +124,7 @@ def test_insert_beginning() -> None:
def test_insert_similar() -> None:
insert_similar = doctrees['insert_similar']
new_nodes = list(merge_doctrees(original, insert_similar, is_paragraph))
uids = [n.uid for n in insert_similar.findall(is_paragraph)]
uids = [n.uid for n in insert_similar.findall(is_paragraph)] # type: ignore[attr-defined]
assert len(new_nodes) == 1
assert new_nodes[0].rawsource == 'Anyway I need more' # type: ignore[attr-defined]
assert original_uids[0] == uids[0]