aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Doc/tools/extensions/pydoc_topics.py
diff options
context:
space:
mode:
authorAdam Turner <9087854+AA-Turner@users.noreply.github.com>2025-03-19 18:35:11 +0000
committerGitHub <noreply@github.com>2025-03-19 18:35:11 +0000
commitc1a02f9101f0b2d9dc7cfb4b8be5193e7459a906 (patch)
tree2d65442631eafc0cf9fc273de9c6dceb1405f8da /Doc/tools/extensions/pydoc_topics.py
parent22706843e0211a42a0e1b9e3b3b2527e9c2f546b (diff)
downloadcpython-c1a02f9101f0b2d9dc7cfb4b8be5193e7459a906.tar.gz
cpython-c1a02f9101f0b2d9dc7cfb4b8be5193e7459a906.zip
GH-121970: Extract `pydoc_topics` into a new extension (#131256)
Diffstat (limited to 'Doc/tools/extensions/pydoc_topics.py')
-rw-r--r--Doc/tools/extensions/pydoc_topics.py188
1 files changed, 188 insertions, 0 deletions
diff --git a/Doc/tools/extensions/pydoc_topics.py b/Doc/tools/extensions/pydoc_topics.py
new file mode 100644
index 00000000000..01efbba6283
--- /dev/null
+++ b/Doc/tools/extensions/pydoc_topics.py
@@ -0,0 +1,188 @@
+"""Support for building "topic help" for pydoc."""
+
+from __future__ import annotations
+
+from time import asctime
+from typing import TYPE_CHECKING
+
+from sphinx.builders.text import TextBuilder
+from sphinx.util import logging
+from sphinx.util.display import status_iterator
+from sphinx.util.docutils import new_document
+from sphinx.writers.text import TextTranslator
+
+if TYPE_CHECKING:
+ from collections.abc import Sequence, Set
+
+ from sphinx.application import Sphinx
+ from sphinx.util.typing import ExtensionMetadata
+
+logger = logging.getLogger(__name__)
+
+_PYDOC_TOPIC_LABELS: Sequence[str] = sorted({
+ "assert",
+ "assignment",
+ "assignment-expressions",
+ "async",
+ "atom-identifiers",
+ "atom-literals",
+ "attribute-access",
+ "attribute-references",
+ "augassign",
+ "await",
+ "binary",
+ "bitwise",
+ "bltin-code-objects",
+ "bltin-ellipsis-object",
+ "bltin-null-object",
+ "bltin-type-objects",
+ "booleans",
+ "break",
+ "callable-types",
+ "calls",
+ "class",
+ "comparisons",
+ "compound",
+ "context-managers",
+ "continue",
+ "conversions",
+ "customization",
+ "debugger",
+ "del",
+ "dict",
+ "dynamic-features",
+ "else",
+ "exceptions",
+ "execmodel",
+ "exprlists",
+ "floating",
+ "for",
+ "formatstrings",
+ "function",
+ "global",
+ "id-classes",
+ "identifiers",
+ "if",
+ "imaginary",
+ "import",
+ "in",
+ "integers",
+ "lambda",
+ "lists",
+ "naming",
+ "nonlocal",
+ "numbers",
+ "numeric-types",
+ "objects",
+ "operator-summary",
+ "pass",
+ "power",
+ "raise",
+ "return",
+ "sequence-types",
+ "shifting",
+ "slicings",
+ "specialattrs",
+ "specialnames",
+ "string-methods",
+ "strings",
+ "subscriptions",
+ "truth",
+ "try",
+ "types",
+ "typesfunctions",
+ "typesmapping",
+ "typesmethods",
+ "typesmodules",
+ "typesseq",
+ "typesseq-mutable",
+ "unary",
+ "while",
+ "with",
+ "yield",
+})
+
+
+class PydocTopicsBuilder(TextBuilder):
+ name = "pydoc-topics"
+
+ def init(self) -> None:
+ super().init()
+ self.topics: dict[str, str] = {}
+
+ def get_outdated_docs(self) -> str:
+ # Return a string describing what an update build will build.
+ return "all pydoc topics"
+
+ def write_documents(self, _docnames: Set[str]) -> None:
+ env = self.env
+
+ labels: dict[str, tuple[str, str, str]]
+ labels = env.domains.standard_domain.labels
+
+ # docname -> list of (topic_label, label_id) pairs
+ doc_labels: dict[str, list[tuple[str, str]]] = {}
+ for topic_label in _PYDOC_TOPIC_LABELS:
+ try:
+ docname, label_id, _section_name = labels[topic_label]
+ except KeyError:
+ logger.warning("label %r not in documentation", topic_label)
+ continue
+ doc_labels.setdefault(docname, []).append((topic_label, label_id))
+
+ for docname, label_ids in status_iterator(
+ doc_labels.items(),
+ "building topics... ",
+ length=len(doc_labels),
+ stringify_func=_display_labels,
+ ):
+ doctree = env.get_and_resolve_doctree(docname, builder=self)
+ doc_ids = doctree.ids
+ for topic_label, label_id in label_ids:
+ document = new_document("<section node>")
+ document.append(doc_ids[label_id])
+ visitor = TextTranslator(document, builder=self)
+ document.walkabout(visitor)
+ body = "\n".join(map(str.rstrip, visitor.body.splitlines()))
+ self.topics[topic_label] = body + "\n"
+
+ def finish(self) -> None:
+ topics_repr = "\n".join(
+ f" '{topic}': {_repr(self.topics[topic])},"
+ for topic in sorted(self.topics)
+ )
+ topics = f"""\
+# Autogenerated by Sphinx on {asctime()}
+# as part of the release process.
+
+topics = {{
+{topics_repr}
+}}
+"""
+ self.outdir.joinpath("topics.py").write_text(topics, encoding="utf-8")
+
+
+def _display_labels(item: tuple[str, Sequence[tuple[str, str]]]) -> str:
+ _docname, label_ids = item
+ labels = [name for name, _id in label_ids]
+ if len(labels) > 4:
+ return f"{labels[0]}, {labels[1]}, ..., {labels[-2]}, {labels[-1]}"
+ return ", ".join(labels)
+
+
+def _repr(text: str, /) -> str:
+ """Return a triple-single-quoted representation of text."""
+ if "'''" not in text:
+ return f"r'''{text}'''"
+ text = text.replace("\\", "\\\\").replace("'''", r"\'\'\'")
+ return f"'''{text}'''"
+
+
+def setup(app: Sphinx) -> ExtensionMetadata:
+ app.add_builder(PydocTopicsBuilder)
+
+ return {
+ "version": "1.0",
+ "parallel_read_safe": True,
+ "parallel_write_safe": True,
+ }