Add per-event overloads to `Sphinx.connect()` (#12784)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Adam Dangoor 2024-08-15 21:49:50 +01:00 committed by GitHub
parent 76ead8737d
commit cb455ddada
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 350 additions and 17 deletions

View File

@ -186,12 +186,15 @@ nitpick_ignore = {
('js:func', 'number'),
('js:func', 'string'),
('py:attr', 'srcline'),
('py:class', '_ConfigRebuild'), # sphinx.application.Sphinx.add_config_value
('py:class', '_StrPath'), # sphinx.environment.BuildEnvironment.doc2path
('py:class', 'Element'), # sphinx.domains.Domain
('py:class', 'Documenter'), # sphinx.application.Sphinx.add_autodocumenter
('py:class', 'IndexEntry'), # sphinx.domains.IndexEntry
('py:class', 'Lexer'), # sphinx.application.Sphinx.add_lexer
('py:class', 'Node'), # sphinx.domains.Domain
('py:class', 'NullTranslations'), # gettext.NullTranslations
('py:class', 'Path'), # sphinx.application.Sphinx.connect
('py:class', 'RoleFunction'), # sphinx.domains.Domain
('py:class', 'RSTState'), # sphinx.utils.parsing.nested_parse_to_nodes
('py:class', 'Theme'), # sphinx.application.TemplateBridge
@ -199,6 +202,8 @@ nitpick_ignore = {
('py:class', 'StringList'), # sphinx.utils.parsing.nested_parse_to_nodes
('py:class', 'system_message'), # sphinx.utils.docutils.SphinxDirective
('py:class', 'TitleGetter'), # sphinx.domains.Domain
('py:class', 'todo_node'), # sphinx.application.Sphinx.connect
('py:class', 'Transform'), # sphinx.application.Sphinx.add_transform
('py:class', 'XRefRole'), # sphinx.domains.Domain
('py:class', 'docutils.nodes.Element'),
('py:class', 'docutils.nodes.Node'),
@ -210,6 +215,7 @@ nitpick_ignore = {
('py:class', 'docutils.parsers.rst.states.Inliner'),
('py:class', 'docutils.transforms.Transform'),
('py:class', 'nodes.NodeVisitor'),
('py:class', 'nodes.TextElement'), # sphinx.application.Sphinx.connect
('py:class', 'nodes.document'),
('py:class', 'nodes.reference'),
('py:class', 'pygments.lexer.Lexer'),

View File

@ -107,10 +107,10 @@ Here is a more detailed list of these events.
:param app: :class:`.Sphinx`
:param env: :class:`.BuildEnvironment`
:param added: ``set[str]``
:param changed: ``set[str]``
:param removed: ``set[str]``
:returns: ``list[str]`` of additional docnames to re-read
:param added: ``Set[str]``
:param changed: ``Set[str]``
:param removed: ``Set[str]``
:returns: ``Sequence[str]`` of additional docnames to re-read
Emitted when the environment determines which source files have changed and
should be re-read.

View File

@ -10,15 +10,11 @@ import os
import pickle
import sys
from collections import deque
from collections.abc import Callable, Collection, Sequence # NoQA: TCH003
from io import StringIO
from os import path
from typing import IO, TYPE_CHECKING, Any, Literal
from typing import TYPE_CHECKING, overload
from docutils.nodes import TextElement # NoQA: TCH002
from docutils.parsers.rst import Directive, roles
from docutils.transforms import Transform # NoQA: TCH002
from pygments.lexer import Lexer # NoQA: TCH002
import sphinx
from sphinx import locale, package_dir
@ -41,16 +37,22 @@ from sphinx.util.osutil import ensuredir, relpath
from sphinx.util.tags import Tags
if TYPE_CHECKING:
from typing import Final
from collections.abc import Callable, Collection, Iterable, Sequence, Set
from pathlib import Path
from typing import IO, Any, Final, Literal
from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers import Parser
from docutils.transforms import Transform
from pygments.lexer import Lexer
from sphinx import addnodes
from sphinx.builders import Builder
from sphinx.domains import Domain, Index
from sphinx.environment.collectors import EnvironmentCollector
from sphinx.ext.autodoc import Documenter
from sphinx.ext.todo import todo_node
from sphinx.extension import Extension
from sphinx.roles import XRefRole
from sphinx.search import SearchLanguage
@ -456,6 +458,329 @@ class Sphinx:
req = f'{major}.{minor}'
raise VersionRequirementError(req)
# ---- Core events -------------------------------------------------------
@overload
def connect(
self,
event: Literal['config-inited'],
callback: Callable[[Sphinx, Config], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['builder-inited'],
callback: Callable[[Sphinx], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-get-outdated'],
callback: Callable[
[Sphinx, BuildEnvironment, Set[str], Set[str], Set[str]], Sequence[str]
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-before-read-docs'],
callback: Callable[[Sphinx, BuildEnvironment, list[str]], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-purge-doc'],
callback: Callable[[Sphinx, BuildEnvironment, str], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['source-read'],
callback: Callable[[Sphinx, str, list[str]], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['include-read'],
callback: Callable[[Sphinx, Path, str, list[str]], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['doctree-read'],
callback: Callable[[Sphinx, nodes.document], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-merge-info'],
callback: Callable[
[Sphinx, BuildEnvironment, list[str], BuildEnvironment], None
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-updated'],
callback: Callable[[Sphinx, BuildEnvironment], str],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-get-updated'],
callback: Callable[[Sphinx, BuildEnvironment], Iterable[str]],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['env-check-consistency'],
callback: Callable[[Sphinx, BuildEnvironment], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['write-started'],
callback: Callable[[Sphinx, Builder], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['doctree-resolved'],
callback: Callable[[Sphinx, nodes.document, str], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['missing-reference'],
callback: Callable[
[Sphinx, BuildEnvironment, addnodes.pending_xref, nodes.TextElement],
nodes.reference | None,
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['warn-missing-reference'],
callback: Callable[[Sphinx, Domain, addnodes.pending_xref], bool | None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['build-finished'],
callback: Callable[[Sphinx, Exception | None], None],
priority: int = 500
) -> int:
...
# ---- Events from builtin builders --------------------------------------
@overload
def connect(
self,
event: Literal['html-collect-pages'],
callback: Callable[[Sphinx], Iterable[tuple[str, dict[str, Any], str]]],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['html-page-context'],
callback: Callable[
[Sphinx, str, str, dict[str, Any], nodes.document], str | None
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['linkcheck-process-uri'],
callback: Callable[[Sphinx, str], str | None],
priority: int = 500
) -> int:
...
# ---- Events from builtin extensions-- ----------------------------------
@overload
def connect(
self,
event: Literal['object-description-transform'],
callback: Callable[[Sphinx, str, str, addnodes.desc_content], None],
priority: int = 500
) -> int:
...
# ---- Events from first-party extensions --------------------------------
@overload
def connect(
self,
event: Literal['autodoc-process-docstring'],
callback: Callable[
[
Sphinx,
Literal['module', 'class', 'exception', 'function', 'method', 'attribute'],
str,
Any,
dict[str, bool],
Sequence[str],
],
None,
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['autodoc-before-process-signature'],
callback: Callable[[Sphinx, Any, bool], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['autodoc-process-signature'],
callback: Callable[
[
Sphinx,
Literal['module', 'class', 'exception', 'function', 'method', 'attribute'],
str,
Any,
dict[str, bool],
str | None,
str | None,
],
tuple[str | None, str | None] | None,
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['autodoc-process-bases'],
callback: Callable[[Sphinx, str, Any, dict[str, bool], list[str]], None],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['autodoc-skip-member'],
callback: Callable[
[
Sphinx,
Literal['module', 'class', 'exception', 'function', 'method', 'attribute'],
str,
Any,
bool,
dict[str, bool],
],
bool,
],
priority: int = 500
) -> int:
...
@overload
def connect(
self,
event: Literal['todo-defined'],
callback: Callable[[Sphinx, todo_node], None],
priority: int = 500,
) -> int:
...
@overload
def connect(
self,
event: Literal['viewcode-find-source'],
callback: Callable[
[Sphinx, str],
tuple[str, dict[str, tuple[Literal['class', 'def', 'other'], int, int]]],
],
priority: int = 500,
) -> int:
...
@overload
def connect(
self,
event: Literal['viewcode-follow-imported'],
callback: Callable[[Sphinx, str, str], str | None],
priority: int = 500,
) -> int:
...
# ---- Catch-all ---------------------------------------------------------
@overload
def connect(
self,
event: str,
callback: Callable[..., Any],
priority: int = 500
) -> int:
...
# event interface
def connect(self, event: str, callback: Callable, priority: int = 500) -> int:
"""Register *callback* to be called when *event* is emitted.
@ -851,7 +1176,7 @@ class Sphinx:
def add_object_type(self, directivename: str, rolename: str, indextemplate: str = '',
parse_node: Callable | None = None,
ref_nodeclass: type[TextElement] | None = None,
ref_nodeclass: type[nodes.TextElement] | None = None,
objname: str = '', doc_field_types: Sequence = (),
override: bool = False,
) -> None:
@ -918,9 +1243,11 @@ class Sphinx:
ref_nodeclass, objname, doc_field_types,
override=override)
def add_crossref_type(self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: type[TextElement] | None = None, objname: str = '',
override: bool = False) -> None:
def add_crossref_type(
self, directivename: str, rolename: str, indextemplate: str = '',
ref_nodeclass: type[nodes.TextElement] | None = None, objname: str = '',
override: bool = False,
) -> None:
"""Register a new crossref object type.
This method is very similar to :meth:`~Sphinx.add_object_type` except that the

View File

@ -23,7 +23,7 @@ else:
if TYPE_CHECKING:
import os
from collections.abc import Collection, Iterator, Sequence, Set
from collections.abc import Collection, Iterable, Iterator, Sequence, Set
from typing import TypeAlias
from sphinx.application import Sphinx
@ -739,8 +739,8 @@ def check_primary_domain(app: Sphinx, config: Config) -> None:
config.primary_domain = None
def check_root_doc(app: Sphinx, env: BuildEnvironment, added: set[str],
changed: set[str], removed: set[str]) -> set[str]:
def check_root_doc(app: Sphinx, env: BuildEnvironment, added: Set[str],
changed: Set[str], removed: Set[str]) -> Iterable[str]:
"""Adjust root_doc to 'contents' to support an old project which does not have
any root_doc setting.
"""