8000 GH-121970: Extract ``pydoc_topics`` into a new extension (#129116) · python/cpython@01bcf13 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 01bcf13

Browse files
authored
GH-121970: Extract pydoc_topics into a new extension (#129116)
1 parent 29caec6 commit 01bcf13

File tree

4 files changed

+12969
-17555
lines changed

4 files changed

+12969
-17555
lines changed

Doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
'changes',
2929
'glossary_search',
3030
'lexers',
31+
'pydoc_topics',
3132
'pyspecific',
3233
'sphinx.ext.coverage',
3334
'sphinx.ext.doctest',

Doc/tools/extensions/pydoc_topics.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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&q F438 uot;,
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+
self.topics[topic_label] = visitor.body
147+
148+
def finish(self) -> None:
149+
topics_repr = "\n".join(
150+
f" '{topic}': {_repr(self.topics[topic])},"
151+
for topic in sorted(self.topics)
152+
)
153+
topics = f"""\
154+
# Autogenerated by Sphinx on {asctime()}
155+
# as part of the release process.
156+
157+
topics = {{
158+
{topics_repr}
159+
}}
160+
"""
161+
self.outdir.joinpath("topics.py").write_text(topics, encoding="utf-8")
162+
163+
164+
def _display_labels(item: tuple[str, Sequence[tuple[str, str]]]) -> str:
165+
_docname, label_ids = item
166+
labels = [name for name, _id in label_ids]
167+
if len(labels) > 4:
168+
return f"{labels[0]}, {labels[1]}, ..., {labels[-2]}, {labels[-1]}"
169+
return ", ".join(labels)
170+
171+
172+
def _repr(text: str, /) -> str:
173+
"""Return a triple-single-quoted representation of text."""
174+
if "'''" not in text:
175+
return f"r'''{text}'''"
176+
text = text.replace("\\", "\\\\").replace("'''", r"\'\'\'")
177+
return f"'''{text}'''"
178+
179+
180+
def setup(app: Sphinx) -> ExtensionMetadata:
181+
app.add_builder(PydocTopicsBuilder)
182+
183+
return {
184+
"version": "1.0",
185+
"parallel_read_safe": True,
186+
"parallel_write_safe": True,
187+
}

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

3125
ISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s'
@@ -223,69 +217,6 @@ def run(self):
223217
return []
224218

225219

226-
# Support for building "topic help" for pydoc
227-
228-
pydoc_topic_labels = [
229-
'assert', 'assignment', 'assignment-expressions', 'async', 'atom-identifiers',
230-
'atom-literals', 'attribute-access', 'attribute-references', 'augassign', 'await',
231-
'binary', 'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
232-
'bltin-null-object', 'bltin-type-objects', 'booleans',
233-
'break', 'callable-types', 'calls', 'class', 'comparisons', 'compound',
234-
'context-managers', 'continue', 'conversions', 'customization', 'debugger',
235-
'del', 'dict', 'dynamic-features', 'else', 'exceptions', 'execmodel',
236-
'exprlists', 'floating', 'for', 'formatstrings', 'function', 'global',
237-
'id-classes', 'identifiers', 'if', 'imaginary', 'import', 'in', 'integers',
238-
'lambda', 'lists', 'naming', 'nonlocal', 'numbers', 'numeric-types',
239-
'objects', 'operator-summary', 'pass', 'power', 'raise', 'return',
240-
'sequence-types', 'shifting', 'slicings', 'specialattrs', 'specialnames',
241-
'string-methods', 'strings', 'subscriptions', 'truth', 'try', 'types',
242-
'typesfunctions', 'typesmapping', 'typesmethods', 'typesmodules',
243-
'typesseq', 'typesseq-mutable', 'unary', 'while', 'with', 'yield'
244-
]
245-
246-
247-
class PydocTopicsBuilder(Builder):
248-
name = 'pydoc-topics'
249-
250-
default_translator_class = TextTranslator
251-
252-
def init(self):
253-
self.topics = {}
254-
self.secnumbers = {}
255-
256-
def get_outdated_docs(self):
257-
return 'all pydoc topics'
258-
259-
def get_target_uri(self, docname, typ=None):
260-
return '' # no URIs
261-
262-
def write(self, *ignored):
263-
writer = TextWriter(self)
264-
for label in status_iterator(pydoc_topic_labels,
265-
'building topics... ',
266-
length=len(pydoc_topic_labels)):
267-
if label not in self.env.domaindata['std']['labels']:
268-
self.env.logger.warning(f'label {label!r} not in documentation')
269-
continue
270-
docname, labelid, sectname = self.env.domaindata['std']['labels'][label]
271-
doctree = self.env.get_and_resolve_doctree(docname, self)
272-
document = new_document('<section node>')
273-
document.append(doctree.ids[labelid])
274-
destination = StringOutput(encoding='utf-8')
275-
writer.write(document, destination)
276-
self.topics[label] = writer.output
277-
278-
def finish(self):
279-
f = open(path.join(self.outdir, 'topics.py'), 'wb')
280-
try:
281-
f.write('# -*- coding: utf-8 -*-\n'.encode('utf-8'))
282-
f.write(('# Autogenerated by Sphinx on %s\n' % asctime()).encode('utf-8'))
283-
f.write('# as part of the release process.\n'.encode('utf-8'))
284-
f.write(('topics = ' + pformat(self.topics) + '\n').encode('utf-8'))
285-
finally:
286-
f.close()
287-
288-
289220
# Support for documenting Opcodes
290221

291222
opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')
@@ -365,7 +296,6 @@ def setup(app):
365296
app.add_role('issue', issue_role)
366297
app.add_role('gh', gh_issue_role)
367298
app.add_directive('impl-detail', ImplementationDetail)
368-
app.add_builder(PydocTopicsBuilder)
369299
app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
370300
app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
371301
app.add_object_type('monitoring-event', 'monitoring-event', '%s (monitoring event)', parse_monitoring_event)

0 commit comments

Comments
 (0)
0