Skip to content

Commit c1a02f9

Browse files
authored
GH-121970: Extract pydoc_topics into a new extension (#131256)
1 parent 2270684 commit c1a02f9

File tree

4 files changed

+13021
-17646
lines changed

4 files changed

+13021
-17646
lines changed

Doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'issue_role',
3434
'lexers',
3535
'misc_news',
36+
'pydoc_topics',
3637
'pyspecific',
3738
'sphinx.ext.coverage',
3839
'sphinx.ext.doctest',

Doc/tools/extensions/pydoc_topics.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""Support for building "topic help" for pydoc."""
2+
3+
from __future__ import annotations
4+
5+
from time import asctime
6+
from typing import TYPE_CHECKING
7+
8+
from sphinx.builders.text import TextBuilder
9+
from sphinx.util import logging
10+
from sphinx.util.display import status_iterator
11+
from sphinx.util.docutils import new_document
12+
from sphinx.writers.text import TextTranslator
13+
14+
if TYPE_CHECKING:
15+
from collections.abc import Sequence, Set
16+
17+
from sphinx.application import Sphinx
18+
from sphinx.util.typing import ExtensionMetadata
19+
20+
logger = logging.getLogger(__name__)
21+
22+
_PYDOC_TOPIC_LABELS: Sequence[str] = sorted({
23+
"assert",
24+
"assignment",
25+
"assignment-expressions",
26+
"async",
27+
"atom-identifiers",
28+
"atom-literals",
29+
"attribute-access",
30+
"attribute-references",
31+
"augassign",
32+
"await",
33+
"binary",
34+
"bitwise",
35+
"bltin-code-objects",
36+
"bltin-ellipsis-object",
37+
"bltin-null-object",
38+
"bltin-type-objects",
39+
"booleans",
40+
"break",
41+
"callable-types",
42+
"calls",
43+
"class",
44+
"comparisons",
45+
"compound",
46+
"context-managers",
47+
"continue",
48+
"conversions",
49+
"customization",
50+
"debugger",
51+
"del",
52+
"dict",
53+
"dynamic-features",
54+
"else",
55+
"exceptions",
56+
"execmodel",
57+
"exprlists",
58+
"floating",
59+
"for",
60+
"formatstrings",
61+
"function",
62+
"global",
63+
"id-classes",
64+
"identifiers",
65+
"if",
66+
"imaginary",
67+
"import",
68+
"in",
69+
"integers",
70+
"lambda",
71+
"lists",
72+
"naming",
73+
"nonlocal",
74+
"numbers",
75+
"numeric-types",
76+
"objects",
77+
"operator-summary",
78+
"pass",
79+
"power",
80+
"raise",
81+
"return",
82+
"sequence-types",
83+
"shifting",
84+
"slicings",
85+
"specialattrs",
86+
"specialnames",
87+
"string-methods",
88+
"strings",
89+
"subscriptions",
90+
"truth",
91+
"try",
92+
"types",
93+
"typesfunctions",
94+
"typesmapping",
95+
"typesmethods",
96+
"typesmodules",
97+
"typesseq",
98+
"typesseq-mutable",
99+
"unary",
100+
"while",
101+
"with",
102+
"yield",
103+
})
104+
105+
106+
class PydocTopicsBuilder(TextBuilder):
107+
name = "pydoc-topics"
108+
109+
def init(self) -> None:
110+
super().init()
111+
self.topics: dict[str, str] = {}
112+
113+
def get_outdated_docs(self) -> str:
114+
# Return a string describing what an update build will build.
115+
return "all pydoc topics"
116+
117+
def write_documents(self, _docnames: Set[str]) -> None:
118+
env = self.env
119+
120+
labels: dict[str, tuple[str, str, str]]
121+
labels = env.domains.standard_domain.labels
122+
123+
# docname -> list of (topic_label, label_id) pairs
124+
doc_labels: dict[str, list[tuple[str, str]]] = {}
125+
for topic_label in _PYDOC_TOPIC_LABELS:
126+
try:
127+
docname, label_id, _section_name = labels[topic_label]
128+
except KeyError:
129+
logger.warning("label %r not in documentation", topic_label)
130+
continue
131+
doc_labels.setdefault(docname, []).append((topic_label, label_id))
132+
133+
for docname, label_ids in status_iterator(
134+
doc_labels.items(),
135+
"building topics... ",
136+
length=len(doc_labels),
137+
stringify_func=_display_labels,
138+
):
139+
doctree = env.get_and_resolve_doctree(docname, builder=self)
140+
doc_ids = doctree.ids
141+
for topic_label, label_id in label_ids:
142+
document = new_document("<section node>")
143+
document.append(doc_ids[label_id])
144+
visitor = TextTranslator(document, builder=self)
145+
document.walkabout(visitor)
146+
body = "\n".join(map(str.rstrip, visitor.body.splitlines()))
147+
self.topics[topic_label] = body + "\n"
148+
149+
def finish(self) -> None:
150+
topics_repr = "\n".join(
151+
f" '{topic}': {_repr(self.topics[topic])},"
152+
for topic in sorted(self.topics)
153+
)
154+
topics = f"""\
155+
# Autogenerated by Sphinx on {asctime()}
156+
# as part of the release process.
157+
158+
topics = {{
159+
{topics_repr}
160+
}}
161+
"""
162+
self.outdir.joinpath("topics.py").write_text(topics, encoding="utf-8")
163+
164+
165+
def _display_labels(item: tuple[str, Sequence[tuple[str, str]]]) -> str:
166+
_docname, label_ids = item
167+
labels = [name for name, _id in label_ids]
168+
if len(labels) > 4:
169+
return f"{labels[0]}, {labels[1]}, ..., {labels[-2]}, {labels[-1]}"
170+
return ", ".join(labels)
171+
172+
173+
def _repr(text: str, /) -> str:
174+
"""Return a triple-single-quoted representation of text."""
175+
if "'''" not in text:
176+
return f"r'''{text}'''"
177+
text = text.replace("\\", "\\\\").replace("'''", r"\'\'\'")
178+
return f"'''{text}'''"
179+
180+
181+
def setup(app: Sphinx) -> ExtensionMetadata:
182+
app.add_builder(PydocTopicsBuilder)
183+
184+
return {
185+
"version": "1.0",
186+
"parallel_read_safe": True,
187+
"parallel_write_safe": True,
188+
}

Doc/tools/extensions/pyspecific.py

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,14 @@
1212
import re
1313
import io
1414
from os import getenv, path
15-
from time import asctime
16-
from pprint import pformat
1715

1816
from docutils import nodes
19-
from docutils.io import StringOutput
2017
from docutils.parsers.rst import directives
21-
from docutils.utils import new_document, unescape
18+
from docutils.utils import unescape
2219
from sphinx import addnodes
23-
from sphinx.builders import Builder
2420
from sphinx.domains.python import PyFunction, PyMethod, PyModule
2521
from sphinx.locale import _ as sphinx_gettext
2622
from sphinx.util.docutils import SphinxDirective
27-
from sphinx.writers.text import TextWriter, TextTranslator
28-
from sphinx.util.display import status_iterator
2923

3024
# Used in conf.py and updated here by python/release-tools/run_release.py
3125
SOURCE_URI = 'https://github.com/python/cpython/tree/main/%s'
@@ -57,69 +51,6 @@ def run(self):
5751
return PyMethod.run(self)
5852

5953

60-
# Support for building "topic help" for pydoc
61-
62-
pydoc_topic_labels = [
63-
'assert', 'assignment', 'assignment-expressions', 'async', 'atom-identifiers',
64-
'atom-literals', 'attribute-access', 'attribute-references', 'augassign', 'await',
65-
'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
66-
'bltin-null-object', 'bltin-type-objects', 'booleans',
67-
'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
68-
'context-managers', 'continue', 'conversions', 'customization', 'debugger',
69-
'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
70-
'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
71-
'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
72-
'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
73-
'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
74-
'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
75-
'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
76-
'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
77-
'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
78-
]
79-
80-
81-
class PydocTopicsBuilder(Builder):
82-
name = 'pydoc-topics'
83-
84-
default_translator_class = TextTranslator
85-
86-
def init(self):
87-
self.topics = {}
88-
self.secnumbers = {}
89-
90-
def get_outdated_docs(self):
91-
return 'all pydoc topics'
92-
93-
def get_target_uri(self, docname, typ=None):
94-
return '' # no URIs
95-
96-
def write(self, *ignored):
97-
writer = TextWriter(self)
98-
for label in status_iterator(pydoc_topic_labels,
99-
'building topics... ',
100-
length=len(pydoc_topic_labels)):
101-
if label not in self.env.domaindata['std']['labels']:
102-
self.env.logger.warning(f'label {label!r} not in documentation')
103-
continue
104-
docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
105-
doctree = self.env.get_and_resolve_doctree(docname, self)
106-
document = new_document('<section node>')
107-
document.append(doctree.ids[labelid])
108-
destination = StringOutput(encoding='utf-8')
109-
writer.write(document, destination)
110-
self.topics[label] = writer.output
111-
112-
def finish(self):
113-
f = open(path.join(self.outdir, 'topics.py'), 'wb')
114-
try:
115-
f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8'))
116-
f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8'))
117-
f.write('# as part of the release process.\n'.encode('utf-8'))
118-
f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8'))
119-
finally:
120-
f.close()
121-
122-
12354
# Support for documenting Opcodes
12455

12556
opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')
@@ -196,7 +127,6 @@ def patch_pairindextypes(app, _env) -> None:
196127

197128

198129
def setup(app):
199-
app.add_builder(PydocTopicsBuilder)
200130
app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
201131
app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
202132
app.add_object_type('monitoring-event', 'monitoring-event', '%s (monitoring event)', parse_monitoring_event)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy