diff --git a/pyproject.toml b/pyproject.toml index 3327667e392..d5843e63563 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ lint = [ "ruff==0.3.4", "mypy==1.9.0", "sphinx-lint", - "docutils-stubs", + "types-docutils", "types-requests", "pytest>=6.0", ] diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 7896fbaef05..1d637fc5f2f 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -16,8 +16,8 @@ # deprecated name -> (object to return, canonical path or empty string) _DEPRECATED_OBJECTS = { - 'meta': (nodes.meta, 'docutils.nodes.meta'), # type: ignore[attr-defined] - 'docutils_meta': (nodes.meta, 'docutils.nodes.meta'), # type: ignore[attr-defined] + 'meta': (nodes.meta, 'docutils.nodes.meta'), + 'docutils_meta': (nodes.meta, 'docutils.nodes.meta'), } @@ -45,7 +45,7 @@ class document(nodes.document): def set_id(self, node: Element, msgnode: Element | None = None, suggested_prefix: str = '') -> str: - return super().set_id(node, msgnode, suggested_prefix) # type: ignore[call-arg] + return super().set_id(node, msgnode, suggested_prefix) class translatable(nodes.Node): @@ -89,7 +89,7 @@ class toctree(nodes.General, nodes.Element, translatable): def preserve_original_messages(self) -> None: # toctree entries - rawentries = self.setdefault('rawentries', []) + rawentries: list[str] = self.setdefault('rawentries', []) for title, _docname in self['entries']: if title: rawentries.append(title) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 6afb5d4cc44..878f5d6acfb 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -520,7 +520,7 @@ def write_doctree( doctree.settings = doctree.settings.copy() doctree.settings.warning_stream = None doctree.settings.env = None - doctree.settings.record_dependencies = None # type: ignore[assignment] + doctree.settings.record_dependencies = None doctree_filename = path.join(self.doctreedir, docname + '.doctree') ensuredir(path.dirname(doctree_filename)) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 572aa8c6e23..3928f9f9308 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -67,7 +67,7 @@ def add(self, msg: str, origin: Element | MsgOrigin) -> None: line = origin.line if line is None: line = -1 - self.metadata[msg].append((origin.source, line, origin.uid)) + self.metadata[msg].append((origin.source, line, origin.uid)) # type: ignore[arg-type] def __iter__(self) -> Generator[Message, None, None]: for message in self.messages: diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index bc75527cebd..9178ada7f0b 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -53,6 +53,7 @@ from collections.abc import Iterable, Iterator, Set from docutils.nodes import Node + from docutils.readers import Reader from sphinx.application import Sphinx from sphinx.config import _ConfigRebuild @@ -200,7 +201,7 @@ def __init__(self, app: Sphinx, env: BuildEnvironment) -> None: self._js_files: list[_JavaScript] = [] # Cached Publisher for writing doctrees to HTML - reader = docutils.readers.doctree.Reader(parser_name='restructuredtext') + reader: Reader = docutils.readers.doctree.Reader(parser_name='restructuredtext') pub = Publisher( reader=reader, parser=reader.parser, @@ -437,7 +438,7 @@ def render_partial(self, node: Node | None) -> dict[str, str]: doc.append(node) self._publisher.set_source(doc) self._publisher.publish() - return self._publisher.writer.parts # type: ignore[union-attr] + return self._publisher.writer.parts def prepare_writing(self, docnames: set[str]) -> None: # create the search indexer @@ -767,7 +768,7 @@ def copy_image_files(self) -> None: def copy_download_files(self) -> None: def to_relpath(f: str) -> str: - return relative_path(self.srcdir, f) # type: ignore[arg-type] + return relative_path(self.srcdir, f) # copy downloadable files if self.env.dlfiles: diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py index f1807f5ad38..83599d8261d 100644 --- a/sphinx/builders/latex/transforms.py +++ b/sphinx/builders/latex/transforms.py @@ -115,7 +115,7 @@ def get_docname_for_node(self, node: Node) -> str: node = node.parent try: - source = node['source'] # type: ignore[index] + source = node['source'] except TypeError: raise ValueError(__('Failed to get a docname!')) from None raise ValueError(__('Failed to get a docname ' @@ -523,7 +523,7 @@ def run(self, **kwargs: Any) -> None: citations += node if len(citations) > 0: - self.document += citations + self.document += citations # type: ignore[attr-defined] class CitationReferenceTransform(SphinxPostTransform): diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index cac6c031f4e..9e06a7a64b8 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -3,7 +3,7 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Generic, TypeVar, cast +from typing import TYPE_CHECKING, ClassVar, Generic, TypeVar, cast from docutils import nodes from docutils.parsers.rst import directives, roles @@ -55,7 +55,7 @@ class ObjectDescription(SphinxDirective, Generic[ObjDescT]): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, @@ -296,7 +296,7 @@ def run(self) -> list[Node]: # If ``:no-index:`` is set, or there are no ids on the node # or any of its children, then just return the index node, # as Docutils expects a target node to have at least one id. - if node_ids := [node_id for el in node.findall(nodes.Element) + if node_ids := [node_id for el in node.findall(nodes.Element) # type: ignore[var-annotated] for node_id in el.get('ids', ())]: target_node = nodes.target(ids=node_ids) self.set_source_info(target_node) @@ -320,7 +320,7 @@ def run(self) -> list[Node]: role_name = self.arguments[0] role, messages = roles.role(role_name, self.state_machine.language, self.lineno, self.state.reporter) - if role: # type: ignore[truthy-function] + if role: docutils.register_role('', role) # type: ignore[arg-type] self.env.temp_data['default_role'] = role_name else: @@ -342,7 +342,7 @@ class DefaultDomain(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: domain_name = self.arguments[0].lower() diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index a0629cec650..da6c3c9f4a6 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -3,7 +3,7 @@ import sys import textwrap from difflib import unified_diff -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -35,7 +35,7 @@ class Highlight(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'force': directives.flag, 'linenothreshold': directives.positive_int, } @@ -102,7 +102,7 @@ class CodeBlock(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'force': directives.flag, 'linenos': directives.flag, 'dedent': optional_int, @@ -393,7 +393,7 @@ class LiteralInclude(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'dedent': optional_int, 'linenos': directives.flag, 'lineno-start': int, diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 96ba93b7a5b..286db295579 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -3,7 +3,7 @@ import re from os.path import abspath, relpath from pathlib import Path -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils import nodes from docutils.parsers.rst import directives @@ -22,6 +22,8 @@ from sphinx.util.nodes import explicit_title_re if TYPE_CHECKING: + from collections.abc import Sequence + from docutils.nodes import Element, Node from sphinx.application import Sphinx @@ -179,7 +181,7 @@ class Author(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: if not self.config.show_authors: @@ -221,7 +223,7 @@ class TabularColumns(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: node = addnodes.tabular_col_spec() @@ -239,7 +241,7 @@ class Centered(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: if not self.arguments: @@ -262,7 +264,7 @@ class Acks(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: node = addnodes.acks() @@ -285,7 +287,7 @@ class HList(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'columns': int, } @@ -323,7 +325,7 @@ class Only(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: node = addnodes.only() @@ -379,7 +381,7 @@ class Include(BaseInclude, SphinxDirective): "correctly", i.e. relative to source directory. """ - def run(self) -> list[Node]: + def run(self) -> Sequence[Node]: # To properly emit "include-read" events from included RST text, # we must patch the ``StateMachine.insert_input()`` method. @@ -413,7 +415,7 @@ def _insert_input(include_lines: list[str], source: str) -> None: # Only enable this patch if there are listeners for 'include-read'. if self.env.app.events.listeners.get('include-read'): # See https://github.com/python/mypy/issues/2427 for details on the mypy issue - self.state_machine.insert_input = _insert_input # type: ignore[assignment] + self.state_machine.insert_input = _insert_input if self.arguments[0].startswith('<') and \ self.arguments[0].endswith('>'): diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index b46713f63d3..145f1f5d9ff 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -2,13 +2,13 @@ import os from os import path -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, ClassVar, cast from docutils import nodes from docutils.nodes import Node, make_id from docutils.parsers.rst import directives from docutils.parsers.rst.directives import images, tables -from docutils.parsers.rst.directives.misc import Meta # type: ignore[attr-defined] +from docutils.parsers.rst.directives.misc import Meta from docutils.parsers.rst.roles import set_classes from sphinx.directives import optional_int @@ -82,7 +82,7 @@ class Code(SphinxDirective): """ optional_arguments = 1 - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'class': directives.class_option, 'force': directives.flag, 'name': directives.unchanged, @@ -127,7 +127,7 @@ class MathDirective(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'label': directives.unchanged, 'name': directives.unchanged, 'class': directives.class_option, diff --git a/sphinx/domains/c/__init__.py b/sphinx/domains/c/__init__.py index c82835100e0..7a68606bcc8 100644 --- a/sphinx/domains/c/__init__.py +++ b/sphinx/domains/c/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -56,7 +56,7 @@ class CObject(ObjectDescription[ASTDeclaration]): Description of a C language object. """ - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, 'no-typesetting': directives.flag, @@ -297,7 +297,7 @@ class CNamespaceObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: rootSymbol = self.env.domaindata['c']['root_symbol'] @@ -327,7 +327,7 @@ class CNamespacePushObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): @@ -358,7 +358,7 @@ class CNamespacePopObject(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: stack = self.env.temp_data.get('c:namespace_stack', None) @@ -517,7 +517,7 @@ def apply(self, **kwargs: Any) -> None: class CAliasObject(ObjectDescription): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'maxdepth': directives.nonnegative_int, 'noroot': directives.flag, } diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py index 9a06287453f..c0550f5b478 100644 --- a/sphinx/domains/changeset.py +++ b/sphinx/domains/changeset.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, NamedTuple, cast +from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast from docutils import nodes @@ -52,7 +52,7 @@ class VersionChange(SphinxDirective): required_arguments = 1 optional_arguments = 1 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: node = addnodes.versionmodified() @@ -123,7 +123,7 @@ def note_changeset(self, node: addnodes.versionmodified) -> None: version = node['version'] module = self.env.ref_context.get('py:module') objname = self.env.temp_data.get('object') - changeset = ChangeSet(node['type'], self.env.docname, node.line, + changeset = ChangeSet(node['type'], self.env.docname, node.line, # type: ignore[arg-type] module, objname, node.astext()) self.changesets.setdefault(version, []).append(changeset) diff --git a/sphinx/domains/citation.py b/sphinx/domains/citation.py index fc0990789f4..4f00feb81e7 100644 --- a/sphinx/domains/citation.py +++ b/sphinx/domains/citation.py @@ -70,7 +70,7 @@ def note_citation(self, node: nodes.citation) -> None: path = self.env.doc2path(self.citations[label][0]) logger.warning(__('duplicate citation %s, other instance in %s'), label, path, location=node, type='ref', subtype='citation') - self.citations[label] = (node['docname'], node['ids'][0], node.line) + self.citations[label] = (node['docname'], node['ids'][0], node.line) # type: ignore[assignment] def note_citation_reference(self, node: pending_xref) -> None: docnames = self.citation_refs.setdefault(node['reftarget'], set()) diff --git a/sphinx/domains/cpp/__init__.py b/sphinx/domains/cpp/__init__.py index 117d9fc4ce5..872b09ec93e 100644 --- a/sphinx/domains/cpp/__init__.py +++ b/sphinx/domains/cpp/__init__.py @@ -3,7 +3,7 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -65,7 +65,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]): can_collapse=True), ] - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, 'no-typesetting': directives.flag, @@ -394,7 +394,7 @@ class CPPNamespaceObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: rootSymbol = self.env.domaindata['cpp']['root_symbol'] @@ -425,7 +425,7 @@ class CPPNamespacePushObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): @@ -457,7 +457,7 @@ class CPPNamespacePopObject(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: stack = self.env.temp_data.get('cpp:namespace_stack', None) @@ -634,7 +634,7 @@ def apply(self, **kwargs: Any) -> None: class CPPAliasObject(ObjectDescription): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'maxdepth': directives.nonnegative_int, 'noroot': directives.flag, } diff --git a/sphinx/domains/index.py b/sphinx/domains/index.py index 115277bf76c..87d1cacbd6b 100644 --- a/sphinx/domains/index.py +++ b/sphinx/domains/index.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -68,7 +68,7 @@ class IndexDirective(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'name': directives.unchanged, } diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index de6c3507c32..9b881f87f92 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -3,7 +3,7 @@ from __future__ import annotations import contextlib -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils import nodes from docutils.parsers.rst import directives @@ -46,7 +46,7 @@ class JSObject(ObjectDescription[tuple[str, str]]): #: based on directive nesting allow_nesting = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, @@ -298,7 +298,7 @@ class JSModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, 'no-contents-entry': directives.flag, 'no-typesetting': directives.flag, diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index 5b574ed6ef7..542911f8706 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -5,7 +5,7 @@ import builtins import inspect import typing -from typing import TYPE_CHECKING, Any, NamedTuple, cast +from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast from docutils import nodes from docutils.parsers.rst import directives @@ -67,7 +67,7 @@ class ModuleEntry(NamedTuple): class PyFunction(PyObject): """Description of a function.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() # noqa: F821 option_spec.update({ 'async': directives.flag, }) @@ -122,7 +122,7 @@ def needs_arglist(self) -> bool: class PyVariable(PyObject): """Description of a variable.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() option_spec.update({ 'type': directives.unchanged, 'value': directives.unchanged, @@ -161,7 +161,7 @@ class PyClasslike(PyObject): Description of a class-like object (classes, interfaces, exceptions). """ - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() option_spec.update({ 'final': directives.flag, }) @@ -189,7 +189,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: class PyMethod(PyObject): """Description of a method.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() option_spec.update({ 'abstractmethod': directives.flag, 'async': directives.flag, @@ -243,7 +243,7 @@ def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str: class PyClassMethod(PyMethod): """Description of a classmethod.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() def run(self) -> list[Node]: self.name = 'py:method' @@ -255,7 +255,7 @@ def run(self) -> list[Node]: class PyStaticMethod(PyMethod): """Description of a staticmethod.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() def run(self) -> list[Node]: self.name = 'py:method' @@ -283,7 +283,7 @@ def needs_arglist(self) -> bool: class PyAttribute(PyObject): """Description of an attribute.""" - option_spec: OptionSpec = PyObject.option_spec.copy() + option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy() option_spec.update({ 'type': directives.unchanged, 'value': directives.unchanged, @@ -385,7 +385,7 @@ class PyModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'platform': lambda x: x, 'synopsis': lambda x: x, 'no-index': directives.flag, @@ -444,7 +444,7 @@ class PyCurrentModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: modname = self.arguments[0].strip() diff --git a/sphinx/domains/python/_object.py b/sphinx/domains/python/_object.py index 3bd06a3f8bd..41f9df1986c 100644 --- a/sphinx/domains/python/_object.py +++ b/sphinx/domains/python/_object.py @@ -2,7 +2,7 @@ import contextlib import re -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -76,13 +76,13 @@ def make_xref( result['reftarget'] = reftarget result.clear() - result += innernode(reftitle, reftitle) + result += innernode(reftitle, reftitle) # type: ignore[call-arg] elif env.config.python_use_unqualified_type_names: children = result.children result.clear() shortname = target.split('.')[-1] - textnode = innernode('', shortname) + textnode = innernode('', shortname) # type: ignore[call-arg] contnodes = [pending_xref_condition('', '', textnode, condition='resolved'), pending_xref_condition('', '', *children, condition='*')] result.extend(contnodes) @@ -113,7 +113,7 @@ def make_xrefs( contnode = nodes.Text(sub_target) if in_literal or delims_re.match(sub_target): - results.append(contnode or innernode(sub_target, sub_target)) + results.append(contnode or innernode(sub_target, sub_target)) # type: ignore[call-arg] else: results.append(self.make_xref(rolename, domain, sub_target, innernode, contnode, env, inliner, location)) @@ -144,7 +144,7 @@ class PyObject(ObjectDescription[tuple[str, str]]): :vartype allow_nesting: bool """ - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 1ff9d4247e4..5ae267a212f 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -3,7 +3,7 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils.parsers.rst import directives @@ -36,7 +36,7 @@ class ReSTMarkup(ObjectDescription[str]): Description of generic reST markup. """ - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, @@ -142,7 +142,7 @@ class ReSTDirectiveOption(ReSTMarkup): Description of an option for reST directive. """ - option_spec: OptionSpec = ReSTMarkup.option_spec.copy() + option_spec: ClassVar[OptionSpec] = ReSTMarkup.option_spec.copy() option_spec.update({ 'type': directives.unchanged, }) diff --git a/sphinx/domains/std/__init__.py b/sphinx/domains/std/__init__.py index ca879378c05..30d0977a2f4 100644 --- a/sphinx/domains/std/__init__.py +++ b/sphinx/domains/std/__init__.py @@ -4,7 +4,7 @@ import re from copy import copy -from typing import TYPE_CHECKING, Any, Callable, Final, cast +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Final, cast from docutils import nodes from docutils.nodes import Element, Node, system_message @@ -112,7 +112,7 @@ class Target(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: # normalize whitespace in fullname like XRefRole does @@ -206,7 +206,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> str: def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature) -> None: currprogram = self.env.ref_context.get('std:program') - for optname in signode.get('allnames', []): + for optname in signode.get('allnames', []): # type: ignore[var-annotated] prefixes = ['cmdoption'] if currprogram: prefixes.append(currprogram) @@ -228,7 +228,7 @@ def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature descr = _('%s command line option') % currprogram else: descr = _('command line option') - for option in signode.get('allnames', []): + for option in signode.get('allnames', []): # type: ignore[var-annotated] entry = f'{descr}; {option}' self.indexnode['entries'].append(('pair', entry, signode['ids'][0], '', None)) @@ -242,7 +242,7 @@ class Program(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: program = ws_re.sub('-', self.arguments[0].strip()) @@ -306,7 +306,7 @@ class Glossary(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'sorted': directives.flag, } @@ -385,7 +385,7 @@ def run(self) -> list[Node]: parts = split_term_classifiers(line) # parse the term with inline markup # classifiers (parts[1:]) will not be shown on doctree - textnodes, sysmsg = self.state.inline_text(parts[0], # type: ignore[arg-type] + textnodes, sysmsg = self.state.inline_text(parts[0], lineno) # use first classifier as a index key @@ -453,7 +453,7 @@ class ProductionList(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: domain = cast(StandardDomain, self.env.get_domain('std')) diff --git a/sphinx/environment/collectors/metadata.py b/sphinx/environment/collectors/metadata.py index b64a9fa0836..bef35119e3a 100644 --- a/sphinx/environment/collectors/metadata.py +++ b/sphinx/environment/collectors/metadata.py @@ -30,7 +30,7 @@ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: Keep processing minimal -- just return what docutils says. """ - index = doctree.first_child_not_matching_class(nodes.PreBibliographic) + index = doctree.first_child_not_matching_class(nodes.PreBibliographic) # type: ignore[arg-type] if index is None: return elif isinstance(doctree[index], nodes.docinfo): diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 9cf7e81b44c..b5cae374b0e 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -13,7 +13,7 @@ import sys import warnings from inspect import Parameter, Signature -from typing import TYPE_CHECKING, Any, Callable, TypeVar +from typing import TYPE_CHECKING, Any, Callable, ClassVar, TypeVar from docutils.statemachine import StringList @@ -319,7 +319,7 @@ class Documenter: #: true if the generated content may contain titles titles_allowed = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'no-index': bool_option, 'noindex': bool_option, } @@ -980,7 +980,7 @@ class ModuleDocumenter(Documenter): content_indent = '' _extra_indent = ' ' - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'members': members_option, 'undoc-members': bool_option, 'no-index': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'synopsis': identity, @@ -1466,7 +1466,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: objtype = 'class' member_order = 20 - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'members': members_option, 'undoc-members': bool_option, 'no-index': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'member-order': member_order_option, @@ -2042,7 +2042,7 @@ class DataDocumenter(GenericAliasMixin, objtype = 'data' member_order = 40 priority = -10 - option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) + option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option @@ -2589,7 +2589,7 @@ class AttributeDocumenter(GenericAliasMixin, SlotsMixin, # type: ignore[misc] objtype = 'attribute' member_order = 60 - option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) + option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index d7fb8c7551d..130e347ab4e 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -115,7 +115,7 @@ def run(self) -> list[Node]: reporter = self.state.document.reporter try: - source, lineno = reporter.get_source_and_line( # type: ignore[attr-defined] + source, lineno = reporter.get_source_and_line( self.lineno) except AttributeError: source, lineno = (None, None) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 4d8618444d1..7057f439b63 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -58,7 +58,7 @@ from inspect import Parameter from os import path from types import ModuleType -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils import nodes from docutils.parsers.rst import directives @@ -218,7 +218,7 @@ class Autosummary(SphinxDirective): optional_arguments = 0 final_argument_whitespace = False has_content = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'caption': directives.unchanged_required, 'toctree': directives.unchanged, 'nosignatures': directives.flag, diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 868600de1b0..fe133900c05 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -11,7 +11,7 @@ import time from io import StringIO from os import path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any, Callable, ClassVar from docutils import nodes from docutils.parsers.rst import directives @@ -143,19 +143,19 @@ def run(self) -> list[Node]: class TestsetupDirective(TestDirective): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'skipif': directives.unchanged_required, } class TestcleanupDirective(TestDirective): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'skipif': directives.unchanged_required, } class DoctestDirective(TestDirective): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'options': directives.unchanged, @@ -166,7 +166,7 @@ class DoctestDirective(TestDirective): class TestcodeDirective(TestDirective): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'pyversion': directives.unchanged_required, @@ -176,7 +176,7 @@ class TestcodeDirective(TestDirective): class TestoutputDirective(TestDirective): - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'options': directives.unchanged, @@ -371,14 +371,13 @@ def get_filename_for_node(self, node: Node, docname: str) -> str: filename of the document it's included in. """ try: - filename = relpath(node.source, self.env.srcdir)\ - .rsplit(':docstring of ', maxsplit=1)[0] + filename = relpath(node.source, self.env.srcdir).rsplit(':docstring of ', maxsplit=1)[0] # type: ignore[arg-type] # noqa: E501 except Exception: filename = self.env.doc2path(docname, False) return filename @staticmethod - def get_line_number(node: Node) -> int: + def get_line_number(node: Node) -> int | None: """Get the real line number or admit we don't know.""" # TODO: Work out how to store or calculate real (file-relative) # line numbers for doctest blocks in docstrings. @@ -387,7 +386,7 @@ def get_line_number(node: Node) -> int: # not the file. This is correct where it is set, in # `docutils.nodes.Node.setup_child`, but Sphinx should report # relative to the file, not the docstring. - return None # type: ignore[return-value] + return None if node.line is not None: # TODO: find the root cause of this off by one error. return node.line - 1 @@ -428,21 +427,21 @@ def condition(node: Node) -> bool: def condition(node: Node) -> bool: return isinstance(node, (nodes.literal_block, nodes.comment)) \ and 'testnodetype' in node - for node in doctree.findall(condition): # type: Element - if self.skipped(node): + for node in doctree.findall(condition): + if self.skipped(node): # type: ignore[arg-type] continue - source = node['test'] if 'test' in node else node.astext() + source = node['test'] if 'test' in node else node.astext() # type: ignore[index, operator] filename = self.get_filename_for_node(node, docname) line_number = self.get_line_number(node) if not source: logger.warning(__('no code/output in %s block at %s:%s'), - node.get('testnodetype', 'doctest'), + node.get('testnodetype', 'doctest'), # type: ignore[attr-defined] filename, line_number) - code = TestCode(source, type=node.get('testnodetype', 'doctest'), - filename=filename, lineno=line_number, - options=node.get('options')) - node_groups = node.get('groups', ['default']) + code = TestCode(source, type=node.get('testnodetype', 'doctest'), # type: ignore[attr-defined] + filename=filename, lineno=line_number, # type: ignore[arg-type] + options=node.get('options')) # type: ignore[attr-defined] + node_groups = node.get('groups', ['default']) # type: ignore[attr-defined] if '*' in node_groups: add_to_all_groups.append(code) continue diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 50c60596b2c..9e6ce11a6b9 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -11,7 +11,7 @@ from itertools import chain from os import path from subprocess import CalledProcessError -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, ClassVar from urllib.parse import urlsplit, urlunsplit from docutils import nodes @@ -117,7 +117,7 @@ class Graphviz(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'alt': directives.unchanged, 'align': align_spec, 'caption': directives.unchanged, @@ -186,7 +186,7 @@ class GraphvizSimple(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'alt': directives.unchanged, 'align': align_spec, 'caption': directives.unchanged, diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py index b1adff3f4bd..398d6699ff9 100644 --- a/sphinx/ext/ifconfig.py +++ b/sphinx/ext/ifconfig.py @@ -16,7 +16,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, ClassVar from docutils import nodes @@ -41,7 +41,7 @@ class IfConfig(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: node = ifconfig() diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index a1933fdfd79..b9e51378099 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -37,7 +37,7 @@ class E(B): pass from collections.abc import Iterable, Sequence from importlib import import_module from os import path -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils import nodes from docutils.parsers.rst import directives @@ -348,7 +348,7 @@ class InheritanceDiagram(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'parts': int, 'private-bases': directives.flag, 'caption': directives.unchanged, @@ -378,7 +378,7 @@ def run(self) -> list[Node]: aliases=self.config.inheritance_alias, top_classes=node['top-classes']) except InheritanceException as err: - return [node.document.reporter.warning(err, line=self.lineno)] + return [node.document.reporter.warning(err, line=self.lineno)] # type: ignore[union-attr] # Create xref nodes for each target of the graph's image map and # add them to the doc tree so that Sphinx can resolve the @@ -386,7 +386,7 @@ def run(self) -> list[Node]: # removed from the doctree after we're done with them. for name in graph.get_all_class_names(): refnodes, x = class_role( # type: ignore[call-arg,misc] - 'class', ':class:`%s`' % name, name, 0, self.state) # type: ignore[arg-type] + 'class', ':class:`%s`' % name, name, 0, self.state) node.extend(refnodes) # Store the graph object so we can use it to generate the # dot file later diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index d206866830b..1962328d766 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -9,7 +9,7 @@ import functools import operator -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast from docutils import nodes from docutils.parsers.rst import directives @@ -53,7 +53,7 @@ class Todo(BaseAdmonition, SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = { + option_spec: ClassVar[OptionSpec] = { 'class': directives.class_option, 'name': directives.unchanged, } @@ -112,7 +112,7 @@ class TodoList(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = {} + option_spec: ClassVar[OptionSpec] = {} def run(self) -> list[Node]: # Simply insert an empty todolist node which will be replaced later diff --git a/sphinx/io.py b/sphinx/io.py index 4f6cc75e7ec..459d250e45f 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -98,9 +98,9 @@ def setup(self, app: Sphinx) -> None: self.transforms = self.transforms + app.registry.get_transforms() super().setup(app) - def read(self, source: Input, parser: Parser, settings: Values) -> nodes.document: + def read(self, source: Input, parser: Parser, settings: Values) -> nodes.document: # type: ignore[type-arg] self.source = source - if not self.parser: + if not self.parser: # type: ignore[has-type] self.parser = parser self.settings = settings self.input = self.read_source(settings.env) @@ -179,7 +179,7 @@ def create_publisher(app: Sphinx, filetype: str) -> Publisher: # CommonMarkParser. from docutils.parsers.rst import Parser as RSTParser - parser.settings_spec = RSTParser.settings_spec + parser.settings_spec = RSTParser.settings_spec # type: ignore[misc] pub = Publisher( reader=reader, diff --git a/sphinx/roles.py b/sphinx/roles.py index 830fe04994f..2fa242f0190 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -462,15 +462,15 @@ def setup(app: Sphinx) -> ExtensionMetadata: for rolename, nodeclass in generic_docroles.items(): generic = roles.GenericRole(rolename, nodeclass) - role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) - roles.register_local_role(rolename, role) + role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) # type: ignore[arg-type] + roles.register_local_role(rolename, role) # type: ignore[arg-type] for rolename, func in specific_docroles.items(): - roles.register_local_role(rolename, func) + roles.register_local_role(rolename, func) # type: ignore[arg-type] # Since docutils registers it as a canonical role, override it as a # canonical role as well. - roles.register_canonical_role('code', code_role) + roles.register_canonical_role('code', code_role) # type: ignore[arg-type] return { 'version': 'builtin', diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 9e57fdbba0c..2638f92ffb4 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -182,7 +182,7 @@ def load(self, f: IO[str]) -> Any: def _is_meta_keywords( - node: nodes.meta, # type: ignore[name-defined] + node: nodes.meta, lang: str | None, ) -> bool: if node.get('name') == 'keywords': @@ -234,7 +234,7 @@ def dispatch_visit(self, node: Node) -> None: ids = node.parent['ids'] self.found_titles.append((title, ids[0] if ids else None)) self.found_title_words.extend(self.lang.split(title)) - elif isinstance(node, Element) and _is_meta_keywords(node, self.lang.lang): + elif isinstance(node, Element) and _is_meta_keywords(node, self.lang.lang): # type: ignore[arg-type] keywords = node['content'] keywords = [keyword.strip() for keyword in keywords.split(',')] self.found_words.extend(keywords) @@ -495,7 +495,7 @@ def _visit_nodes(node): nodetext = re.sub(r'<[^<]+?>', '', nodetext) word_store.words.extend(split(nodetext)) return - elif (isinstance(node, nodes.meta) # type: ignore[attr-defined] + elif (isinstance(node, nodes.meta) and _is_meta_keywords(node, language)): keywords = [keyword.strip() for keyword in node['content'].split(',')] word_store.words.extend(keywords) diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index 224b0e3bfd3..f8923396b40 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -81,7 +81,7 @@ def apply_transforms(self) -> None: if not hasattr(self.document.settings, 'env') and self.env: self.document.settings.env = self.env - super().apply_transforms() + super().apply_transforms() # type: ignore[misc] else: # wrap the target node by document node during transforming try: diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 3239dcd73a2..4f8c3530d75 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -78,7 +78,7 @@ def publish_msgstr(app: Sphinx, source: str, source_path: str, source_line: int, settings=settings, ) with contextlib.suppress(IndexError): # empty node - return doc[0] # type: ignore[return-value] + return doc[0] return doc finally: config.rst_prolog = rst_prolog # type: ignore[attr-defined] @@ -138,7 +138,7 @@ def update_title_mapping(self) -> bool: if old_name != new_name: # if name would be changed, replace node names and # document nameids mapping with new name. - names = section_node.setdefault('names', []) + names: list[str] = section_node.setdefault('names', []) names.append(new_name) # Original section name (reference target name) should be kept to refer # from other nodes which is still not translated or uses explicit target @@ -394,7 +394,7 @@ def apply(self, **kwargs: Any) -> None: msgstr = '::\n\n' + indent(msgstr, ' ' * 3) patch = publish_msgstr(self.app, msgstr, source, - node.line, self.config, settings) + node.line, self.config, settings) # type: ignore[arg-type] # FIXME: no warnings about inconsistent references in this part # XXX doctest and other block markup if not isinstance(patch, nodes.paragraph): @@ -408,10 +408,10 @@ def apply(self, **kwargs: Any) -> None: for _id in node['ids']: parts = split_term_classifiers(msgstr) patch = publish_msgstr( - self.app, parts[0] or '', source, node.line, self.config, settings, + self.app, parts[0] or '', source, node.line, self.config, settings, # type: ignore[arg-type] ) updater.patch = make_glossary_term( - self.env, patch, parts[1] or '', source, node.line, _id, self.document, + self.env, patch, parts[1] or '', source, node.line, _id, self.document, # type: ignore[arg-type] ) processed = True @@ -440,11 +440,11 @@ def apply(self, **kwargs: Any) -> None: # update translatable nodes if isinstance(node, addnodes.translatable): - node.apply_translated_message(msg, msgstr) # type: ignore[attr-defined] + node.apply_translated_message(msg, msgstr) continue # update meta nodes - if isinstance(node, nodes.meta): # type: ignore[attr-defined] + if isinstance(node, nodes.meta): node['content'] = msgstr node['translated'] = True continue @@ -474,11 +474,11 @@ def apply(self, **kwargs: Any) -> None: msgstr = msgstr + '\n' + '=' * len(msgstr) * 2 patch = publish_msgstr(self.app, msgstr, source, - node.line, self.config, settings) + node.line, self.config, settings) # type: ignore[arg-type] # Structural Subelements phase2 if isinstance(node, nodes.title): # get node that placed as a first child - patch = patch.next_node() + patch = patch.next_node() # type: ignore[assignment] # ignore unexpected markups in translation message unexpected: tuple[type[nodes.Element], ...] = ( @@ -588,10 +588,10 @@ def apply(self, **kwargs: Any) -> None: for node in NodeMatcher(nodes.Element, translated=Any).findall(self.document): if node['translated']: if add_translated: - node.setdefault('classes', []).append('translated') + node.setdefault('classes', []).append('translated') # type: ignore[arg-type] else: if add_untranslated: - node.setdefault('classes', []).append('untranslated') + node.setdefault('classes', []).append('untranslated') # type: ignore[arg-type] class RemoveTranslatableInline(SphinxTransform): diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index f73999e5b88..c277a59c51b 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -78,7 +78,7 @@ def make_xref(self, rolename: str, domain: str, target: str, assert env is not None assert (inliner is None) == (location is None), (inliner, location) if not rolename: - return contnode or innernode(target, target) + return contnode or innernode(target, target) # type: ignore[call-arg] # The domain is passed from DocFieldTransformer. So it surely exists. # So we don't need to take care the env.get_domain() raises an exception. role = env.get_domain(domain).role(rolename) @@ -89,7 +89,7 @@ def make_xref(self, rolename: str, domain: str, target: str, logger.warning(__(msg), domain, rolename, location=location) refnode = addnodes.pending_xref('', refdomain=domain, refexplicit=False, reftype=rolename, reftarget=target) - refnode += contnode or innernode(target, target) + refnode += contnode or innernode(target, target) # type: ignore[call-arg] env.get_domain(domain).process_field_xref(refnode) return refnode lineno = -1 diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 68e6d0fc73f..9774055fd31 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -101,7 +101,7 @@ def register_role(name: str, role: RoleFunction) -> None: This modifies global state of docutils. So it is better to use this inside ``docutils_namespace()`` to prevent side-effects. """ - roles.register_local_role(name, role) + roles.register_local_role(name, role) # type: ignore[arg-type] def unregister_role(name: str) -> None: @@ -150,7 +150,7 @@ def patched_get_language(language_code: str, reporter: Reporter | None = None) - return get_language(language_code) try: - docutils.languages.get_language = patched_get_language + docutils.languages.get_language = patched_get_language # type: ignore[assignment] yield finally: # restore original implementations @@ -174,7 +174,7 @@ def patched_get_language(language_code: str, reporter: Reporter | None = None) - return get_language(language_code) try: - docutils.parsers.rst.languages.get_language = patched_get_language + docutils.parsers.rst.languages.get_language = patched_get_language # type: ignore[assignment] yield finally: # restore original implementations @@ -201,7 +201,7 @@ def using_user_docutils_conf(confdir: str | None) -> Generator[None, None, None] def du19_footnotes() -> Generator[None, None, None]: def visit_footnote(self: HTMLTranslator, node: Element) -> None: label_style = self.settings.footnote_references - if not isinstance(node.previous_sibling(), type(node)): # type: ignore[attr-defined] + if not isinstance(node.previous_sibling(), type(node)): self.body.append(f'<aside class="footnote-list {label_style}">\n') self.body.append(self.starttag(node, 'aside', classes=[node.tagname, label_style], @@ -263,8 +263,8 @@ def enable(self) -> None: self.directive_func = directives.directive self.role_func = roles.role - directives.directive = self.directive - roles.role = self.role + directives.directive = self.directive # type: ignore[assignment] + roles.role = self.role # type: ignore[assignment] def disable(self) -> None: directives.directive = self.directive_func @@ -383,7 +383,7 @@ def switch_source_input(state: State, content: StringList) -> Generator[None, No gsal = state.memo.reporter.get_source_and_line # type: ignore[attr-defined] # replace it by new one - state_machine = StateMachine([], None) # type: ignore[arg-type] + state_machine: StateMachine[None] = StateMachine([], None) # type: ignore[arg-type] state_machine.input_lines = content state.memo.reporter.get_source_and_line = state_machine.get_source_and_line # type: ignore[attr-defined] # NoQA: E501 diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py index 7a06c9850c8..e621f559abc 100644 --- a/sphinx/util/fileutil.py +++ b/sphinx/util/fileutil.py @@ -80,7 +80,7 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st return for root, dirs, files in os.walk(source, followlinks=True): - reldir = relative_path(source, root) # type: ignore[arg-type] + reldir = relative_path(source, root) for dir in dirs.copy(): if excluded(posixpath.join(reldir, dir)): dirs.remove(dir) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 37e6108be0a..3cf199a8f7f 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -5,7 +5,7 @@ import contextlib import re import unicodedata -from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, cast from docutils import nodes from docutils.nodes import Node @@ -93,7 +93,8 @@ def findall(self, node: Node) -> Iterator[N]: While the `NodeMatcher` object can be used as an argument to `Node.findall`, doing so confounds type checkers' ability to determine the return type of the iterator. """ - return node.findall(self) + for found in node.findall(self): + yield cast(N, found) def get_full_module_name(node: Node) -> str: @@ -137,7 +138,7 @@ def apply_source_workaround(node: Element) -> None: get_full_module_name(node), repr_domxml(node)) definition_list_item = node.parent node.source = definition_list_item.source - node.line = definition_list_item.line - 1 + node.line = definition_list_item.line - 1 # type: ignore[operator] node.rawsource = node.astext() # set 'classifier1' (or 'classifier2') elif isinstance(node, nodes.classifier) and not node.source: # docutils-0.15 fills in rawsource attribute, but not in source. @@ -236,7 +237,7 @@ def is_translatable(node: Node) -> bool: return False return True - return isinstance(node, nodes.meta) # type: ignore[attr-defined] + return isinstance(node, nodes.meta) LITERAL_TYPE_NODES = ( @@ -252,10 +253,10 @@ def is_translatable(node: Node) -> bool: def extract_messages(doctree: Element) -> Iterable[tuple[Element, str]]: """Extract translatable messages from a document tree.""" - for node in doctree.findall(is_translatable): # type: Element + for node in doctree.findall(is_translatable): if isinstance(node, addnodes.translatable): for msg in node.extract_original_messages(): - yield node, msg + yield node, msg # type: ignore[misc] continue if isinstance(node, LITERAL_TYPE_NODES): msg = node.rawsource @@ -269,14 +270,14 @@ def extract_messages(doctree: Element) -> Iterable[tuple[Element, str]]: msg = f'.. image:: {image_uri}' else: msg = '' - elif isinstance(node, nodes.meta): # type: ignore[attr-defined] + elif isinstance(node, nodes.meta): msg = node["content"] else: - msg = node.rawsource.replace('\n', ' ').strip() + msg = node.rawsource.replace('\n', ' ').strip() # type: ignore[attr-defined] # XXX nodes rendering empty are likely a bug in sphinx.addnodes if msg: - yield node, msg + yield node, msg # type: ignore[misc] def get_node_source(node: Element) -> str: diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 9f9c3d7cb20..9d0e8307138 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -5,11 +5,11 @@ import re from collections import defaultdict from contextlib import contextmanager -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from unicodedata import east_asian_width from docutils.parsers.rst import roles -from docutils.parsers.rst.languages import en as english +from docutils.parsers.rst.languages import en as english # type: ignore[attr-defined] from docutils.parsers.rst.states import Body from docutils.utils import Reporter from jinja2 import Environment, pass_environment @@ -65,7 +65,7 @@ def default_role(docname: str, name: str) -> Generator[None, None, None]: if name: dummy_reporter = Reporter('', 4, 4) role_fn, _ = roles.role(name, english, 0, dummy_reporter) - if role_fn: # type: ignore[truthy-function] + if role_fn: docutils.register_role('', role_fn) # type: ignore[arg-type] else: logger.warning(__('default role %s not found'), name, location=docname) @@ -103,6 +103,7 @@ def append_epilog(content: StringList, epilog: str) -> None: if epilog: if len(content) > 0: source, lineno = content.info(-1) + lineno = cast(int, lineno) # lineno will never be None, since len(content) > 0 else: source = '<generated>' lineno = 0 diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index f43ea24e5f4..40ea1f8883f 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -338,7 +338,7 @@ def depart_number_reference(self, node: Element) -> None: self.depart_reference(node) # overwritten -- we don't want source comments to show up in the HTML - def visit_comment(self, node: Element) -> None: # type: ignore[override] + def visit_comment(self, node: Element) -> None: raise nodes.SkipNode # overwritten diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5a4778e48bc..606225a0bd4 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -2121,7 +2121,7 @@ def depart_subscript(self, node: Element) -> None: self.body.append('}}$') def visit_inline(self, node: Element) -> None: - classes = node.get('classes', []) + classes = node.get('classes', []) # type: ignore[var-annotated] if classes == ['menuselection']: self.body.append(r'\sphinxmenuselection{') self.context.append('}') @@ -2153,12 +2153,12 @@ def depart_compound(self, node: Element) -> None: pass def visit_container(self, node: Element) -> None: - classes = node.get('classes', []) + classes = node.get('classes', []) # type: ignore[var-annotated] for c in classes: self.body.append('\n\\begin{sphinxuseclass}{%s}' % c) def depart_container(self, node: Element) -> None: - classes = node.get('classes', []) + classes = node.get('classes', []) # type: ignore[var-annotated] for _c in classes: self.body.append('\n\\end{sphinxuseclass}') diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index e3dca81ed0c..d2d6dc5bf37 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -245,7 +245,7 @@ def visit_term(self, node: Element) -> None: super().visit_term(node) # overwritten -- we don't want source comments to show up - def visit_comment(self, node: Element) -> None: # type: ignore[override] + def visit_comment(self, node: Element) -> None: raise nodes.SkipNode # overwritten -- added ensure_eol() @@ -316,7 +316,7 @@ def visit_reference(self, node: Element) -> None: self.body.append(self.defs['reference'][0]) # avoid repeating escaping code... fine since # visit_Text calls astext() and only works on that afterwards - self.visit_Text(node) # type: ignore[arg-type] + self.visit_Text(node) self.body.append(self.defs['reference'][1]) if uri.startswith(('mailto:', 'http:', 'https:', 'ftp:')): diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index dd19d040c79..6e0a7de9ff0 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -281,7 +281,7 @@ def add_node_name(name: str) -> str: for name, content in self.indices] # each section is also a node for section in self.document.findall(nodes.section): - title = cast(nodes.TextElement, section.next_node(nodes.Titular)) + title = cast(nodes.TextElement, section.next_node(nodes.Titular)) # type: ignore[type-var] name = title.astext() if title else '<untitled>' section['node_name'] = add_node_name(name)