From f807729ae5060538fffa9d03b6973d5652284fda Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 16 Aug 2024 10:45:24 +0100 Subject: [PATCH 1/5] Avoid a potential ``NameError`` (#12789) --- sphinx/util/parallel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py index 32912e014d7..f17ef71294b 100644 --- a/sphinx/util/parallel.py +++ b/sphinx/util/parallel.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) # our parallel functionality only works for the forking Process -parallel_available = multiprocessing and os.name == 'posix' +parallel_available = HAS_MULTIPROCESSING and os.name == 'posix' class SerialTasks: From 0530f9808b6afe39bfdfc5f606418382e6a3d7ab Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 16 Aug 2024 10:46:06 +0100 Subject: [PATCH 2/5] Add type hint for a use of Pytest's ``tmp_path_factory`` (#12791) --- sphinx/testing/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py index 899fea6b613..03e38e85e86 100644 --- a/sphinx/testing/fixtures.py +++ b/sphinx/testing/fixtures.py @@ -239,7 +239,7 @@ def if_graphviz_found(app: SphinxTestApp) -> None: # NoQA: PT004 @pytest.fixture(scope='session') -def sphinx_test_tempdir(tmp_path_factory: Any) -> Path: +def sphinx_test_tempdir(tmp_path_factory: pytest.TempPathFactory) -> Path: """Temporary directory.""" return tmp_path_factory.getbasetemp() From d56cf30ecb2d68651c75b454f0aeae74304285dd Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Fri, 16 Aug 2024 10:46:54 +0100 Subject: [PATCH 3/5] Fix ``sphinx.testing.path.path.write_bytes`` bytes type (#12790) --- sphinx/testing/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py index 3a9ee8ddb20..49f0ffa6005 100644 --- a/sphinx/testing/path.py +++ b/sphinx/testing/path.py @@ -178,7 +178,7 @@ def read_bytes(self) -> builtins.bytes: with open(self, mode='rb') as f: return f.read() - def write_bytes(self, bytes: str, append: bool = False) -> None: + def write_bytes(self, bytes: bytes, append: bool = False) -> None: """ Writes the given `bytes` to the file. From f9b3355642acadbb45aaaf68012b2097aa094694 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 14 Aug 2024 21:16:19 +0100 Subject: [PATCH 4/5] Split ``Index{Entry}`` --- sphinx/domains/__init__.py | 124 ++++----------------------------- sphinx/domains/_index.py | 113 ++++++++++++++++++++++++++++++ sphinx/domains/std/__init__.py | 9 ++- 3 files changed, 133 insertions(+), 113 deletions(-) create mode 100644 sphinx/domains/_index.py diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index b05643e1436..5f716ab61b7 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -7,20 +7,16 @@ from __future__ import annotations import copy -from abc import ABC, abstractmethod -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, NamedTuple, cast +from typing import TYPE_CHECKING, Any, cast -from docutils.nodes import Element, Node, system_message - -from sphinx.errors import SphinxError +from sphinx.domains._index import Index, IndexEntry from sphinx.locale import _ if TYPE_CHECKING: - from collections.abc import Iterable, Sequence - from typing import TypeAlias + from collections.abc import Callable, Iterable, Sequence from docutils import nodes + from docutils.nodes import Element, Node from docutils.parsers.rst import Directive from docutils.parsers.rst.states import Inliner @@ -28,7 +24,14 @@ from sphinx.builders import Builder from sphinx.environment import BuildEnvironment from sphinx.roles import XRefRole - from sphinx.util.typing import RoleFunction + from sphinx.util.typing import RoleFunction, TitleGetter + +__all__ = ( + 'Domain', + 'Index', + 'IndexEntry', + 'ObjType', +) class ObjType: @@ -57,107 +60,6 @@ def __init__(self, lname: str, *roles: Any, **attrs: Any) -> None: self.attrs.update(attrs) -class IndexEntry(NamedTuple): - name: str - subtype: int - docname: str - anchor: str - extra: str - qualifier: str - descr: str - - -class Index(ABC): - """ - An Index is the description for a domain-specific index. To add an index to - a domain, subclass Index, overriding the three name attributes: - - * `name` is an identifier used for generating file names. - It is also used for a hyperlink target for the index. Therefore, users can - refer the index page using ``ref`` role and a string which is combined - domain name and ``name`` attribute (ex. ``:ref:`py-modindex```). - * `localname` is the section title for the index. - * `shortname` is a short name for the index, for use in the relation bar in - HTML output. Can be empty to disable entries in the relation bar. - - and providing a :meth:`generate()` method. Then, add the index class to - your domain's `indices` list. Extensions can add indices to existing - domains using :meth:`~sphinx.application.Sphinx.add_index_to_domain()`. - - .. versionchanged:: 3.0 - - Index pages can be referred by domain name and index name via - :rst:role:`ref` role. - """ - - name: str - localname: str - shortname: str | None = None - - def __init__(self, domain: Domain) -> None: - if not self.name or self.localname is None: - raise SphinxError('Index subclass %s has no valid name or localname' - % self.__class__.__name__) - self.domain = domain - - @abstractmethod - def generate(self, docnames: Iterable[str] | None = None, - ) -> tuple[list[tuple[str, list[IndexEntry]]], bool]: - """Get entries for the index. - - If ``docnames`` is given, restrict to entries referring to these - docnames. - - The return value is a tuple of ``(content, collapse)``: - - ``collapse`` - A boolean that determines if sub-entries should start collapsed (for - output formats that support collapsing sub-entries). - - ``content``: - A sequence of ``(letter, entries)`` tuples, where ``letter`` is the - "heading" for the given ``entries``, usually the starting letter, and - ``entries`` is a sequence of single entries. Each entry is a sequence - ``[name, subtype, docname, anchor, extra, qualifier, descr]``. The - items in this sequence have the following meaning: - - ``name`` - The name of the index entry to be displayed. - - ``subtype`` - The sub-entry related type. One of: - - ``0`` - A normal entry. - ``1`` - An entry with sub-entries. - ``2`` - A sub-entry. - - ``docname`` - *docname* where the entry is located. - - ``anchor`` - Anchor for the entry within ``docname`` - - ``extra`` - Extra info for the entry. - - ``qualifier`` - Qualifier for the description. - - ``descr`` - Description for the entry. - - Qualifier and description are not rendered for some output formats such - as LaTeX. - """ - raise NotImplementedError - - -TitleGetter: TypeAlias = Callable[[Node], str | None] - - class Domain: """ A Domain is meant to be a group of "object" description directives for @@ -268,7 +170,7 @@ def role(self, name: str) -> RoleFunction | None: def role_adapter(typ: str, rawtext: str, text: str, lineno: int, inliner: Inliner, options: dict | None = None, content: Sequence[str] = (), - ) -> tuple[list[Node], list[system_message]]: + ) -> tuple[list[Node], list[nodes.system_message]]: return self.roles[name](fullname, rawtext, text, lineno, inliner, options or {}, content) self._role_cache[name] = role_adapter diff --git a/sphinx/domains/_index.py b/sphinx/domains/_index.py new file mode 100644 index 00000000000..1598d2f2026 --- /dev/null +++ b/sphinx/domains/_index.py @@ -0,0 +1,113 @@ +"""Domain indices.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, NamedTuple + +from sphinx.errors import SphinxError + +if TYPE_CHECKING: + from collections.abc import Iterable + + from sphinx.domains import Domain + + +class IndexEntry(NamedTuple): + name: str + subtype: int + docname: str + anchor: str + extra: str + qualifier: str + descr: str + + +class Index(ABC): + """ + An Index is the description for a domain-specific index. To add an index to + a domain, subclass Index, overriding the three name attributes: + + * `name` is an identifier used for generating file names. + It is also used for a hyperlink target for the index. Therefore, users can + refer the index page using ``ref`` role and a string which is combined + domain name and ``name`` attribute (ex. ``:ref:`py-modindex```). + * `localname` is the section title for the index. + * `shortname` is a short name for the index, for use in the relation bar in + HTML output. Can be empty to disable entries in the relation bar. + + and providing a :meth:`generate()` method. Then, add the index class to + your domain's `indices` list. Extensions can add indices to existing + domains using :meth:`~sphinx.application.Sphinx.add_index_to_domain()`. + + .. versionchanged:: 3.0 + + Index pages can be referred by domain name and index name via + :rst:role:`ref` role. + """ + + name: str + localname: str + shortname: str | None = None + + def __init__(self, domain: Domain) -> None: + if not self.name or self.localname is None: + msg = f'Index subclass {self.__class__.__name__} has no valid name or localname' + raise SphinxError(msg) + self.domain = domain + + @abstractmethod + def generate( + self, + docnames: Iterable[str] | None = None, + ) -> tuple[list[tuple[str, list[IndexEntry]]], bool]: + """Get entries for the index. + + If ``docnames`` is given, restrict to entries referring to these + docnames. + + The return value is a tuple of ``(content, collapse)``: + + ``collapse`` + A boolean that determines if sub-entries should start collapsed (for + output formats that support collapsing sub-entries). + + ``content``: + A sequence of ``(letter, entries)`` tuples, where ``letter`` is the + "heading" for the given ``entries``, usually the starting letter, and + ``entries`` is a sequence of single entries. Each entry is a sequence + ``[name, subtype, docname, anchor, extra, qualifier, descr]``. The + items in this sequence have the following meaning: + + ``name`` + The name of the index entry to be displayed. + + ``subtype`` + The sub-entry related type. One of: + + ``0`` + A normal entry. + ``1`` + An entry with sub-entries. + ``2`` + A sub-entry. + + ``docname`` + *docname* where the entry is located. + + ``anchor`` + Anchor for the entry within ``docname`` + + ``extra`` + Extra info for the entry. + + ``qualifier`` + Qualifier for the description. + + ``descr`` + Description for the entry. + + Qualifier and description are not rendered for some output formats such + as LaTeX. + """ + raise NotImplementedError diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py index 629cad3a5cd..882a8a7bf1b 100644 --- a/sphinx/domains/std/__init__.py +++ b/sphinx/domains/std/__init__.py @@ -14,7 +14,7 @@ from sphinx import addnodes from sphinx.addnodes import desc_signature, pending_xref from sphinx.directives import ObjectDescription -from sphinx.domains import Domain, ObjType, TitleGetter +from sphinx.domains import Domain, ObjType from sphinx.locale import _, __ from sphinx.roles import EmphasizedLiteral, XRefRole from sphinx.util import docname_join, logging, ws_re @@ -28,7 +28,12 @@ from sphinx.application import Sphinx from sphinx.builders import Builder from sphinx.environment import BuildEnvironment - from sphinx.util.typing import ExtensionMetadata, OptionSpec, RoleFunction + from sphinx.util.typing import ( + ExtensionMetadata, + OptionSpec, + RoleFunction, + TitleGetter, + ) logger = logging.getLogger(__name__) From c9d3414be7652c8b75ec8a5a037760950557b1bc Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 14 Aug 2024 21:22:49 +0100 Subject: [PATCH 5/5] Better typing for `ObjType` --- sphinx/domains/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index 5f716ab61b7..ef3b5e90ade 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -53,11 +53,10 @@ class ObjType: 'searchprio': 1, } - def __init__(self, lname: str, *roles: Any, **attrs: Any) -> None: - self.lname = lname - self.roles: tuple = roles - self.attrs: dict = self.known_attrs.copy() - self.attrs.update(attrs) + def __init__(self, lname: str, /, *roles: Any, **attrs: Any) -> None: + self.lname: str = lname + self.roles: tuple[Any, ...] = roles + self.attrs: dict[str, Any] = self.known_attrs | attrs class Domain: