| """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, |
| } |