diff --git a/pyproject.toml b/pyproject.toml index 17cb0463ae5..b432c46be32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ lint = [ "sphinx-lint>=0.9", "types-colorama==0.4.15.20240311", "types-defusedxml==0.7.0.20250516", - "types-docutils==0.21.0.20250514", + "types-docutils==0.21.0.20250525", "types-Pillow==10.2.0.20240822", "types-Pygments==2.19.0.20250516", "types-requests==2.32.0.20250602", # align with requests @@ -166,7 +166,7 @@ type-stubs = [ # align with versions used elsewhere "types-colorama==0.4.15.20240311", "types-defusedxml==0.7.0.20250516", - "types-docutils==0.21.0.20250514", + "types-docutils==0.21.0.20250525", "types-Pillow==10.2.0.20240822", "types-Pygments==2.19.0.20250516", "types-requests==2.32.0.20250602", diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index 10ae0820c5b..28a7553da51 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -114,8 +114,8 @@ class NavPoint(NamedTuple): def sphinx_smarty_pants(t: str, language: str = 'en') -> str: t = t.replace('"', '"') - t = smartquotes.educateDashesOldSchool(t) # type: ignore[no-untyped-call] - t = smartquotes.educateQuotes(t, language) # type: ignore[no-untyped-call] + t = smartquotes.educateDashesOldSchool(t) + t = smartquotes.educateQuotes(t, language) t = t.replace('"', '"') return t diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index c848a9b3657..7e6853a81ef 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -9,7 +9,7 @@ from unicodedata import east_asian_width from docutils.parsers.rst import roles -from docutils.parsers.rst.languages import en as english # type: ignore[attr-defined] +from docutils.parsers.rst.languages import en as english from docutils.parsers.rst.states import Body from docutils.utils import Reporter from jinja2 import pass_environment @@ -66,7 +66,7 @@ def heading(env: Environment, text: str, level: int = 1) -> str: def default_role(docname: str, name: str) -> Iterator[None]: if name: dummy_reporter = Reporter('', 4, 4) - role_fn, _ = roles.role(name, english, 0, dummy_reporter) + role_fn, _ = roles.role(name, english, 0, dummy_reporter) # type: ignore[arg-type] if role_fn: docutils.register_role('', role_fn) # type: ignore[arg-type] else: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index b39b463d6db..464da088184 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -17,7 +17,22 @@ from sphinx.util.images import get_image_size if TYPE_CHECKING: - from docutils.nodes import Element, Node, Text + from docutils.nodes import ( + Element, + Node, + Text, + bullet_list, + caption, + emphasis, + field_list, + figure, + image, + literal_block, + reference, + strong, + table, + title, + ) from sphinx.builders import Builder from sphinx.builders.html import StandaloneHTMLBuilder @@ -357,7 +372,7 @@ def visit_reference(self, node: Element) -> None: def visit_number_reference(self, node: Element) -> None: self.visit_reference(node) - def depart_number_reference(self, node: Element) -> None: + def depart_number_reference(self, node: reference) -> None: self.depart_reference(node) # overwritten -- we don't want source comments to show up in the HTML @@ -451,7 +466,7 @@ def add_permalink_ref(self, node: Element, title: str) -> None: ) # overwritten - def visit_bullet_list(self, node: Element) -> None: + def visit_bullet_list(self, node: bullet_list) -> None: if len(node) == 1 and isinstance(node[0], addnodes.toctree): # avoid emitting empty raise nodes.SkipNode @@ -498,7 +513,7 @@ def depart_term(self, node: Element) -> None: self.body.append('') # overwritten - def visit_title(self, node: Element) -> None: + def visit_title(self, node: title) -> None: if ( isinstance(node.parent, addnodes.compact_paragraph) and node.parent.get('toctree') @@ -535,7 +550,7 @@ def visit_title(self, node: Element) -> None: self.body.pop() self.context[-1] = '

\n' - def depart_title(self, node: Element) -> None: + def depart_title(self, node: title) -> None: close_tag = self.context[-1] if ( self.config.html_permalinks @@ -586,7 +601,7 @@ def depart_rubric(self, node: nodes.rubric) -> None: super().depart_rubric(node) # overwritten - def visit_literal_block(self, node: Element) -> None: + def visit_literal_block(self, node: literal_block) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight return super().visit_literal_block(node) @@ -614,7 +629,7 @@ def visit_literal_block(self, node: Element) -> None: self.body.append(starttag + highlighted + '\n') raise nodes.SkipNode - def visit_caption(self, node: Element) -> None: + def visit_caption(self, node: caption) -> None: if ( isinstance(node.parent, nodes.container) and node.parent.get('literal_block') @@ -625,7 +640,7 @@ def visit_caption(self, node: Element) -> None: self.add_fignumber(node.parent) self.body.append(self.starttag(node, 'span', '', CLASS='caption-text')) - def depart_caption(self, node: Element) -> None: + def depart_caption(self, node: caption) -> None: self.body.append('') # append permalink if available @@ -648,7 +663,7 @@ def depart_caption(self, node: Element) -> None: super().depart_caption(node) def visit_doctest_block(self, node: Element) -> None: - self.visit_literal_block(node) + self.visit_literal_block(node) # type: ignore[arg-type] # overwritten to add the
(for XHTML compliance) def visit_block_quote(self, node: Element) -> None: @@ -740,14 +755,14 @@ def depart_download_reference(self, node: Element) -> None: self.body.append(self.context.pop()) # overwritten - def visit_figure(self, node: Element) -> None: + def visit_figure(self, node: figure) -> None: # set align=default if align not specified to give a default style node.setdefault('align', 'default') return super().visit_figure(node) # overwritten - def visit_image(self, node: Element) -> None: + def visit_image(self, node: image) -> None: olduri = node['uri'] # rewrite the URI if the environment knows about it if olduri in self.builder.images: @@ -775,7 +790,7 @@ def visit_image(self, node: Element) -> None: super().visit_image(node) # overwritten - def depart_image(self, node: Element) -> None: + def depart_image(self, node: image) -> None: if node['uri'].lower().endswith(('svg', 'svgz')): pass else: @@ -892,16 +907,16 @@ def visit_tip(self, node: Element) -> None: def depart_tip(self, node: Element) -> None: self.depart_admonition(node) - def visit_literal_emphasis(self, node: Element) -> None: + def visit_literal_emphasis(self, node: emphasis) -> None: return self.visit_emphasis(node) - def depart_literal_emphasis(self, node: Element) -> None: + def depart_literal_emphasis(self, node: emphasis) -> None: return self.depart_emphasis(node) - def visit_literal_strong(self, node: Element) -> None: + def visit_literal_strong(self, node: strong) -> None: return self.visit_strong(node) - def depart_literal_strong(self, node: Element) -> None: + def depart_literal_strong(self, node: strong) -> None: return self.depart_strong(node) def visit_abbreviation(self, node: Element) -> None: @@ -913,15 +928,15 @@ def visit_abbreviation(self, node: Element) -> None: def depart_abbreviation(self, node: Element) -> None: self.body.append('') - def visit_manpage(self, node: Element) -> None: + def visit_manpage(self, node: emphasis) -> None: self.visit_literal_emphasis(node) - def depart_manpage(self, node: Element) -> None: + def depart_manpage(self, node: emphasis) -> None: self.depart_literal_emphasis(node) # overwritten to add even/odd classes - def visit_table(self, node: Element) -> None: + def visit_table(self, node: table) -> None: self._table_row_indices.append(0) atts = {} @@ -936,7 +951,7 @@ def visit_table(self, node: Element) -> None: tag = self.starttag(node, 'table', CLASS=' '.join(classes), **atts) self.body.append(tag) - def depart_table(self, node: Element) -> None: + def depart_table(self, node: table) -> None: self._table_row_indices.pop() super().depart_table(node) @@ -949,11 +964,11 @@ def visit_row(self, node: Element) -> None: self.body.append(self.starttag(node, 'tr', '')) node.column = 0 # type: ignore[attr-defined] - def visit_field_list(self, node: Element) -> None: + def visit_field_list(self, node: field_list) -> None: self._fieldlist_row_indices.append(0) return super().visit_field_list(node) - def depart_field_list(self, node: Element) -> None: + def depart_field_list(self, node: field_list) -> None: self._fieldlist_row_indices.pop() return super().depart_field_list(node) diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 171761fa2b0..e434b5f4c50 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -19,7 +19,21 @@ from collections.abc import Iterable from typing import Any - from docutils.nodes import Element + from docutils.nodes import ( + Element, + admonition, + bullet_list, + caption, + definition, + definition_list, + emphasis, + footnote, + paragraph, + reference, + strong, + term, + title, + ) from sphinx.builders import Builder @@ -71,7 +85,7 @@ def apply(self, **kwargs: Any) -> None: node.parent.remove(node) -class ManualPageTranslator(SphinxTranslator, BaseTranslator): # type: ignore[misc] +class ManualPageTranslator(SphinxTranslator, BaseTranslator): """Custom man page translator.""" _docinfo: dict[str, Any] = {} @@ -130,17 +144,17 @@ def depart_start_of_file(self, node: Element) -> None: # Top-level nodes for descriptions ################################## - def visit_desc(self, node: Element) -> None: + def visit_desc(self, node: definition_list) -> None: self.visit_definition_list(node) - def depart_desc(self, node: Element) -> None: + def depart_desc(self, node: definition_list) -> None: self.depart_definition_list(node) - def visit_desc_signature(self, node: Element) -> None: - self.visit_definition_list_item(node) + def visit_desc_signature(self, node: term) -> None: + self.visit_definition_list_item(node) # type: ignore[arg-type] self.visit_term(node) - def depart_desc_signature(self, node: Element) -> None: + def depart_desc_signature(self, node: term) -> None: self.depart_term(node) def visit_desc_signature_line(self, node: Element) -> None: @@ -149,10 +163,10 @@ def visit_desc_signature_line(self, node: Element) -> None: def depart_desc_signature_line(self, node: Element) -> None: self.body.append(' ') - def visit_desc_content(self, node: Element) -> None: + def visit_desc_content(self, node: definition) -> None: self.visit_definition(node) - def depart_desc_content(self, node: Element) -> None: + def depart_desc_content(self, node: definition) -> None: self.depart_definition(node) def visit_desc_inline(self, node: Element) -> None: @@ -231,25 +245,25 @@ def depart_desc_annotation(self, node: Element) -> None: ############################################## - def visit_versionmodified(self, node: Element) -> None: + def visit_versionmodified(self, node: paragraph) -> None: self.visit_paragraph(node) - def depart_versionmodified(self, node: Element) -> None: + def depart_versionmodified(self, node: paragraph) -> None: self.depart_paragraph(node) # overwritten -- don't make whole of term bold if it includes strong node - def visit_term(self, node: Element) -> None: + def visit_term(self, node: term) -> None: if any(node.findall(nodes.strong)): self.body.append('\n') else: super().visit_term(node) # overwritten -- we don't want source comments to show up - def visit_comment(self, node: Element) -> None: + def visit_comment(self, node: Element) -> None: # type: ignore[override] raise nodes.SkipNode # overwritten -- added ensure_eol() - def visit_footnote(self, node: Element) -> None: + def visit_footnote(self, node: footnote) -> None: self.ensure_eol() super().visit_footnote(node) @@ -264,10 +278,10 @@ def visit_rubric(self, node: Element) -> None: def depart_rubric(self, node: Element) -> None: self.body.append('\n') - def visit_seealso(self, node: Element) -> None: + def visit_seealso(self, node: admonition) -> None: self.visit_admonition(node, 'seealso') - def depart_seealso(self, node: Element) -> None: + def depart_seealso(self, node: admonition) -> None: self.depart_admonition(node) def visit_productionlist(self, node: Element) -> None: @@ -291,7 +305,7 @@ def visit_image(self, node: Element) -> None: raise nodes.SkipNode # overwritten -- don't visit inner marked up nodes - def visit_reference(self, node: Element) -> None: + def visit_reference(self, node: reference) -> None: uri = node.get('refuri', '') is_safe_to_click = uri.startswith(('mailto:', 'http:', 'https:', 'ftp:')) if is_safe_to_click: @@ -301,7 +315,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) + self.visit_Text(node) # type: ignore[arg-type] self.body.append(self.defs['reference'][1]) if uri and not uri.startswith('#'): @@ -369,10 +383,10 @@ def visit_acks(self, node: Element) -> None: self.body.append('\n') raise nodes.SkipNode - def visit_hlist(self, node: Element) -> None: + def visit_hlist(self, node: bullet_list) -> None: self.visit_bullet_list(node) - def depart_hlist(self, node: Element) -> None: + def depart_hlist(self, node: bullet_list) -> None: self.depart_bullet_list(node) def visit_hlistcol(self, node: Element) -> None: @@ -381,16 +395,16 @@ def visit_hlistcol(self, node: Element) -> None: def depart_hlistcol(self, node: Element) -> None: pass - def visit_literal_emphasis(self, node: Element) -> None: + def visit_literal_emphasis(self, node: emphasis) -> None: return self.visit_emphasis(node) - def depart_literal_emphasis(self, node: Element) -> None: + def depart_literal_emphasis(self, node: emphasis) -> None: return self.depart_emphasis(node) - def visit_literal_strong(self, node: Element) -> None: + def visit_literal_strong(self, node: strong) -> None: return self.visit_strong(node) - def depart_literal_strong(self, node: Element) -> None: + def depart_literal_strong(self, node: strong) -> None: return self.depart_strong(node) def visit_abbreviation(self, node: Element) -> None: @@ -399,14 +413,14 @@ def visit_abbreviation(self, node: Element) -> None: def depart_abbreviation(self, node: Element) -> None: pass - def visit_manpage(self, node: Element) -> None: + def visit_manpage(self, node: strong) -> None: return self.visit_strong(node) - def depart_manpage(self, node: Element) -> None: + def depart_manpage(self, node: strong) -> None: return self.depart_strong(node) # overwritten: handle section titles better than in 0.6 release - def visit_caption(self, node: Element) -> None: + def visit_caption(self, node: caption) -> None: if ( isinstance(node.parent, nodes.container) and node.parent.get('literal_block') @@ -415,7 +429,7 @@ def visit_caption(self, node: Element) -> None: else: super().visit_caption(node) - def depart_caption(self, node: Element) -> None: + def depart_caption(self, node: caption) -> None: if ( isinstance(node.parent, nodes.container) and node.parent.get('literal_block') @@ -425,7 +439,7 @@ def depart_caption(self, node: Element) -> None: super().depart_caption(node) # overwritten: handle section titles better than in 0.6 release - def visit_title(self, node: Element) -> None: + def visit_title(self, node: title) -> None: if isinstance(node.parent, addnodes.seealso): self.body.append('.IP "') return None @@ -438,7 +452,7 @@ def visit_title(self, node: Element) -> None: raise nodes.SkipNode return super().visit_title(node) - def depart_title(self, node: Element) -> None: + def depart_title(self, node: title) -> None: if isinstance(node.parent, addnodes.seealso): self.body.append('"\n') return None diff --git a/tests/test_util/test_util_docutils_sphinx_directive.py b/tests/test_util/test_util_docutils_sphinx_directive.py index 8c24a3c4a83..eb1e4aea16a 100644 --- a/tests/test_util/test_util_docutils_sphinx_directive.py +++ b/tests/test_util/test_util_docutils_sphinx_directive.py @@ -3,7 +3,7 @@ from types import SimpleNamespace from docutils import nodes -from docutils.parsers.rst.languages import en as english # type: ignore[attr-defined] +from docutils.parsers.rst.languages import en as english from docutils.parsers.rst.states import ( Inliner, RSTState,