GH-121970: Extract ``pydoc_topics`` into a new extension by AA-Turner · Pull Request #129116 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Doc/conf.py
187 changes: 187 additions & 0 deletions Doc/tools/extensions/pydoc_topics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"""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)
self.topics[topic_label] = visitor.body

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,
}
72 changes: 1 addition & 71 deletions Doc/tools/extensions/pyspecific.py
Loading