Skip to content

Commit caa4924

Browse files
authored
[3.12] GH-121970: Extract availability into a new extension (GH-125082) (#125238)
(cherry picked from commit cbfd392)
1 parent cba3776 commit caa4924

File tree

3 files changed

+124
-76
lines changed

3 files changed

+124
-76
lines changed

Doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
extensions = [
2323
'audit_events',
24+
'availability',
2425
'c_annotations',
2526
'glossary_search',
2627
'lexers',

Doc/tools/extensions/availability.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
"""Support for documenting platform availability"""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
from docutils import nodes
8+
from sphinx import addnodes
9+
from sphinx.util import logging
10+
from sphinx.util.docutils import SphinxDirective
11+
12+
if TYPE_CHECKING:
13+
from sphinx.application import Sphinx
14+
from sphinx.util.typing import ExtensionMetadata
15+
16+
logger = logging.getLogger("availability")
17+
18+
# known platform, libc, and threading implementations
19+
_PLATFORMS = frozenset({
20+
"AIX",
21+
"Android",
22+
"BSD",
23+
"DragonFlyBSD",
24+
"Emscripten",
25+
"FreeBSD",
26+
"Linux",
27+
"macOS",
28+
"NetBSD",
29+
"OpenBSD",
30+
"POSIX",
31+
"Solaris",
32+
"Unix",
33+
"VxWorks",
34+
"WASI",
35+
"Windows",
36+
})
37+
_LIBC = frozenset({
38+
"BSD libc",
39+
"glibc",
40+
"musl",
41+
})
42+
_THREADING = frozenset({
43+
# POSIX platforms with pthreads
44+
"pthreads",
45+
})
46+
KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING
47+
48+
49+
class Availability(SphinxDirective):
50+
has_content = True
51+
required_arguments = 1
52+
optional_arguments = 0
53+
final_argument_whitespace = True
54+
55+
def run(self) -> list[nodes.container]:
56+
title = "Availability"
57+
refnode = addnodes.pending_xref(
58+
title,
59+
nodes.inline(title, title, classes=["xref", "std", "std-ref"]),
60+
refdoc=self.env.docname,
61+
refdomain="std",
62+
refexplicit=True,
63+
reftarget="availability",
64+
reftype="ref",
65+
refwarn=True,
66+
)
67+
sep = nodes.Text(": ")
68+
parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno)
69+
pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs)
70+
self.set_source_info(pnode)
71+
cnode = nodes.container("", pnode, classes=["availability"])
72+
self.set_source_info(cnode)
73+
if self.content:
74+
self.state.nested_parse(self.content, self.content_offset, cnode)
75+
self.parse_platforms()
76+
77+
return [cnode]
78+
79+
def parse_platforms(self) -> dict[str, str | bool]:
80+
"""Parse platform information from arguments
81+
82+
Arguments is a comma-separated string of platforms. A platform may
83+
be prefixed with "not " to indicate that a feature is not available.
84+
85+
Example::
86+
87+
.. availability:: Windows, Linux >= 4.2, not WASI
88+
89+
Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not
90+
parsed into separate tokens.
91+
"""
92+
platforms = {}
93+
for arg in self.arguments[0].rstrip(".").split(","):
94+
arg = arg.strip()
95+
platform, _, version = arg.partition(" >= ")
96+
if platform.startswith("not "):
97+
version = False
98+
platform = platform.removeprefix("not ")
99+
elif not version:
100+
version = True
101+
platforms[platform] = version
102+
103+
if unknown := set(platforms).difference(KNOWN_PLATFORMS):
104+
logger.warning(
105+
"Unknown platform%s or syntax '%s' in '.. availability:: %s', "
106+
"see %s:KNOWN_PLATFORMS for a set of known platforms.",
107+
"s" if len(platforms) != 1 else "",
108+
" ".join(sorted(unknown)),
109+
self.arguments[0],
110+
__file__,
111+
)
112+
113+
return platforms
114+
115+
116+
def setup(app: Sphinx) -> ExtensionMetadata:
117+
app.add_directive("availability", Availability)
118+
119+
return {
120+
"version": "1.0",
121+
"parallel_read_safe": True,
122+
"parallel_write_safe": True,
123+
}

Doc/tools/extensions/pyspecific.py

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from sphinx.domains.changeset import VersionChange, versionlabels, versionlabel_classes
2525
from sphinx.domains.python import PyFunction, PyMethod, PyModule
2626
from sphinx.locale import _ as sphinx_gettext
27-
from sphinx.util import logging
2827
from sphinx.util.docutils import SphinxDirective
2928
from sphinx.writers.text import TextWriter, TextTranslator
3029
from sphinx.util.display import status_iterator
@@ -108,80 +107,6 @@ def run(self):
108107
return [pnode]
109108

110109

111-
# Support for documenting platform availability
112-
113-
class Availability(SphinxDirective):
114-
115-
has_content = True
116-
required_arguments = 1
117-
optional_arguments = 0
118-
final_argument_whitespace = True
119-
120-
# known platform, libc, and threading implementations
121-
known_platforms = frozenset({
122-
"AIX", "Android", "BSD", "DragonFlyBSD", "Emscripten", "FreeBSD",
123-
"Linux", "NetBSD", "OpenBSD", "POSIX", "Solaris", "Unix", "VxWorks",
124-
"WASI", "Windows", "macOS",
125-
# libc
126-
"BSD libc", "glibc", "musl",
127-
# POSIX platforms with pthreads
128-
"pthreads",
129-
})
130-
131-
def run(self):
132-
availability_ref = ':ref:`Availability <availability>`: '
133-
avail_nodes, avail_msgs = self.state.inline_text(
134-
availability_ref + self.arguments[0],
135-
self.lineno)
136-
pnode = nodes.paragraph(availability_ref + self.arguments[0],
137-
'', *avail_nodes, *avail_msgs)
138-
self.set_source_info(pnode)
139-
cnode = nodes.container("", pnode, classes=["availability"])
140-
self.set_source_info(cnode)
141-
if self.content:
142-
self.state.nested_parse(self.content, self.content_offset, cnode)
143-
self.parse_platforms()
144-
145-
return [cnode]
146-
147-
def parse_platforms(self):
148-
"""Parse platform information from arguments
149-
150-
Arguments is a comma-separated string of platforms. A platform may
151-
be prefixed with "not " to indicate that a feature is not available.
152-
153-
Example::
154-
155-
.. availability:: Windows, Linux >= 4.2, not Emscripten, not WASI
156-
157-
Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not
158-
parsed into separate tokens.
159-
"""
160-
platforms = {}
161-
for arg in self.arguments[0].rstrip(".").split(","):
162-
arg = arg.strip()
163-
platform, _, version = arg.partition(" >= ")
164-
if platform.startswith("not "):
165-
version = False
166-
platform = platform[4:]
167-
elif not version:
168-
version = True
169-
platforms[platform] = version
170-
171-
unknown = set(platforms).difference(self.known_platforms)
172-
if unknown:
173-
cls = type(self)
174-
logger = logging.getLogger(cls.__qualname__)
175-
logger.warning(
176-
f"Unknown platform(s) or syntax '{' '.join(sorted(unknown))}' "
177-
f"in '.. availability:: {self.arguments[0]}', see "
178-
f"{__file__}:{cls.__qualname__}.known_platforms for a set "
179-
"known platforms."
180-
)
181-
182-
return platforms
183-
184-
185110
# Support for documenting decorators
186111

187112
class PyDecoratorMixin(object):
@@ -473,7 +398,6 @@ def setup(app):
473398
app.add_role('issue', issue_role)
474399
app.add_role('gh', gh_issue_role)
475400
app.add_directive('impl-detail', ImplementationDetail)
476-
app.add_directive('availability', Availability)
477401
app.add_directive('deprecated-removed', DeprecatedRemoved)
478402
app.add_builder(PydocTopicsBuilder)
479403
app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)

0 commit comments

Comments
 (0)