From e4e78b3e1c5a737e5542f709dda3f1ef82515b80 Mon Sep 17 00:00:00 2001 From: khanxmetu Date: Wed, 18 Sep 2024 03:23:00 +0300 Subject: [PATCH 1/4] Fix mypy failures in intersphinx (#12879) Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com> --- AUTHORS.rst | 1 + pyproject.toml | 3 ++- sphinx/ext/intersphinx/_load.py | 21 +++++++++++-------- sphinx/util/inventory.py | 8 +++---- sphinx/util/typing.py | 23 +++++++++++++++++++++ tests/test_builders/test_build_linkcheck.py | 2 +- 6 files changed, 43 insertions(+), 15 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 1b4d6e7c85b..5c463beed8e 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -72,6 +72,7 @@ Contributors * Michael Wilson -- Intersphinx HTTP basic auth support * Nathan Damon -- bugfix in validation of static paths in html builders * Pauli Virtanen -- autodoc improvements, autosummary extension +* A. Rafey Khan -- improved intersphinx typing * Rob Ruana -- napoleon extension * Robert Lehmann -- gettext builder (GSOC project) * Roland Meister -- epub builder diff --git a/pyproject.toml b/pyproject.toml index 93a3dd405ea..b21b3865426 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,7 +89,8 @@ lint = [ "types-docutils==0.21.0.20240724", "types-Pillow==10.2.0.20240822", "types-Pygments==2.18.0.20240506", - "types-requests>=2.30.0", # align with requests + "types-requests==2.32.0.20240914", # align with requests + "types-urllib3==1.26.25.14", "tomli>=2", # for mypy (Python<=3.10) "pytest>=6.0", ] diff --git a/sphinx/ext/intersphinx/_load.py b/sphinx/ext/intersphinx/_load.py index 358ae1f7ccb..53324f7103f 100644 --- a/sphinx/ext/intersphinx/_load.py +++ b/sphinx/ext/intersphinx/_load.py @@ -3,7 +3,6 @@ from __future__ import annotations import concurrent.futures -import functools import posixpath import time from operator import itemgetter @@ -20,7 +19,8 @@ if TYPE_CHECKING: from pathlib import Path - from typing import IO + + from urllib3.response import HTTPResponse from sphinx.application import Sphinx from sphinx.config import Config @@ -31,7 +31,7 @@ InventoryName, InventoryURI, ) - from sphinx.util.typing import Inventory + from sphinx.util.typing import Inventory, _ReadableStream def validate_intersphinx_mapping(app: Sphinx, config: Config) -> None: @@ -278,7 +278,7 @@ def _fetch_inventory( target_uri = _strip_basic_auth(target_uri) try: if '://' in inv_location: - f = _read_from_url(inv_location, config=config) + f: _ReadableStream[bytes] = _read_from_url(inv_location, config=config) else: f = open(path.join(srcdir, inv_location), 'rb') # NoQA: SIM115 except Exception as err: @@ -357,7 +357,7 @@ def _strip_basic_auth(url: str) -> str: return urlunsplit(frags) -def _read_from_url(url: str, *, config: Config) -> IO: +def _read_from_url(url: str, *, config: Config) -> HTTPResponse: """Reads data from *url* with an HTTP *GET*. This function supports fetching from resources which use basic HTTP auth as @@ -377,8 +377,11 @@ def _read_from_url(url: str, *, config: Config) -> IO: _user_agent=config.user_agent, _tls_info=(config.tls_verify, config.tls_cacerts)) r.raise_for_status() - r.raw.url = r.url - # decode content-body based on the header. - # ref: https://github.com/psf/requests/issues/2155 - r.raw.read = functools.partial(r.raw.read, decode_content=True) + + # For inv_location / new_inv_location + r.raw.url = r.url # type: ignore[union-attr] + + # Decode content-body based on the header. + # xref: https://github.com/psf/requests/issues/2155 + r.raw.decode_content = True return r.raw diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py index c48922cfb61..8ef3831e280 100644 --- a/sphinx/util/inventory.py +++ b/sphinx/util/inventory.py @@ -4,7 +4,7 @@ import os import re import zlib -from typing import IO, TYPE_CHECKING +from typing import TYPE_CHECKING from sphinx.locale import __ from sphinx.util import logging @@ -17,7 +17,7 @@ from sphinx.builders import Builder from sphinx.environment import BuildEnvironment - from sphinx.util.typing import Inventory, InventoryItem + from sphinx.util.typing import Inventory, InventoryItem, _ReadableStream class InventoryFileReader: @@ -26,7 +26,7 @@ class InventoryFileReader: This reader supports mixture of texts and compressed texts. """ - def __init__(self, stream: IO[bytes]) -> None: + def __init__(self, stream: _ReadableStream[bytes]) -> None: self.stream = stream self.buffer = b'' self.eof = False @@ -80,7 +80,7 @@ class InventoryFile: @classmethod def load( cls: type[InventoryFile], - stream: IO[bytes], + stream: _ReadableStream[bytes], uri: str, joinfunc: Callable[[str, str], str], ) -> Inventory: diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index dbad5457ce5..31dfa8f2940 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -117,6 +117,29 @@ def __call__( # title getter functions for enumerable nodes (see sphinx.domains.std) TitleGetter: TypeAlias = Callable[[nodes.Node], str] +# Readable file stream for inventory loading +if TYPE_CHECKING: + from types import TracebackType + + from typing_extensions import Self + + _T_co = TypeVar('_T_co', str, bytes, covariant=True) + + class _ReadableStream(Protocol[_T_co]): + def read(self, size: int = ...) -> _T_co: + ... + + def __enter__(self) -> Self: + ... + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None + ) -> None: + ... + # inventory data on memory InventoryItem: TypeAlias = tuple[ str, # project name diff --git a/tests/test_builders/test_build_linkcheck.py b/tests/test_builders/test_build_linkcheck.py index 845b1aedc92..c27ad189f3e 100644 --- a/tests/test_builders/test_build_linkcheck.py +++ b/tests/test_builders/test_build_linkcheck.py @@ -91,7 +91,7 @@ def __init__(self) -> None: def _collect_connections(self) -> Callable[[object, str], HTTPConnectionPool]: def connection_collector(obj, url): - connection = self.urllib3_connection_from_url(obj, url) + connection = self.urllib3_connection_from_url(obj, url) # type: ignore[no-untyped-call] self.connections.add(connection) return connection From 5907ee4102c4a6fe51311a50da7319c632dafb05 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Wed, 18 Sep 2024 01:24:11 +0100 Subject: [PATCH 2/4] Resolve a mypy ``comparison-overlap`` error (#12884) --- sphinx/ext/apidoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py index e08c0b1e91c..77a4381f5d6 100644 --- a/sphinx/ext/apidoc.py +++ b/sphinx/ext/apidoc.py @@ -69,7 +69,7 @@ def module_join(*modnames: str | None) -> str: def is_packagedir(dirname: str | None = None, files: list[str] | None = None) -> bool: """Check given *files* contains __init__ file.""" - if files is dirname is None: + if files is None and dirname is None: return False if files is None: From 56716ed5142834f547dc2a41540a0b8a44cc7769 Mon Sep 17 00:00:00 2001 From: khanxmetu Date: Wed, 18 Sep 2024 03:42:47 +0300 Subject: [PATCH 3/4] Clearly specify ``latex_engine`` defaults (#12876) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- doc/usage/configuration.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 389a26d6013..213c879ea88 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -2783,6 +2783,7 @@ These options influence LaTeX output. * :code-py:`'pdflatex'` -- PDFLaTeX (default) * :code-py:`'xelatex'` -- XeLaTeX + (default if :confval:`language` is one of ``el``, ``zh_CN``, or ``zh_TW``) * :code-py:`'lualatex'` -- LuaLaTeX * :code-py:`'platex'` -- pLaTeX * :code-py:`'uplatex'` -- upLaTeX From 71b1a06024268e39f59e3772e2420555cb9bf174 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 18 Sep 2024 02:10:10 +0100 Subject: [PATCH 4/4] Fix tests for Docutils revision 9928 (#12897) --- tests/test_builders/test_build_html_image.py | 41 +++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tests/test_builders/test_build_html_image.py b/tests/test_builders/test_build_html_image.py index 6472a97504d..5a061ed4dec 100644 --- a/tests/test_builders/test_build_html_image.py +++ b/tests/test_builders/test_build_html_image.py @@ -61,21 +61,36 @@ def test_html_scaled_image_link(app): assert re.search('\n_images/img.png', context) # scaled_image_link - # Docutils 0.21 adds a newline before the closing tag - closing_space = '\n' if docutils.__version_info__[:2] >= (0, 21) else '' - assert re.search( - '\n' - '_images/img.png' - f'{closing_space}', - context, - ) + if docutils.__version_info__[:2] >= (0, 22): + assert re.search( + '\n' + '_images/img.png' + '\n', + context, + ) + else: + # Docutils 0.21 adds a newline before the closing tag + closing_space = '\n' if docutils.__version_info__[:2] >= (0, 21) else '' + assert re.search( + '\n' + '_images/img.png' + f'{closing_space}', + context, + ) # no-scaled-link class disables the feature - assert re.search( - '\n_images/img.png', - context, - ) + if docutils.__version_info__[:2] >= (0, 22): + assert re.search( + '\n_images/img.png', + context, + ) + else: + assert re.search( + '\n_images/img.png', + context, + ) @pytest.mark.usefixtures('_http_teapot')