|
18 | 18 | from docutils.utils import unescape
|
19 | 19 | from sphinx import addnodes
|
20 | 20 | from sphinx.domains.python import PyFunction, PyMethod, PyModule
|
| 21 | +from sphinx.util import logging |
21 | 22 | from sphinx.locale import _ as sphinx_gettext
|
22 | 23 | from sphinx.util.docutils import SphinxDirective
|
23 | 24 |
|
@@ -90,6 +91,76 @@ def run(self):
|
90 | 91 | return [pnode]
|
91 | 92 |
|
92 | 93 |
|
| 94 | +# Support for documenting platform availability |
| 95 | + |
| 96 | +class Availability(SphinxDirective): |
| 97 | + |
| 98 | + has_content = True |
| 99 | + required_arguments = 1 |
| 100 | + optional_arguments = 0 |
| 101 | + final_argument_whitespace = True |
| 102 | + |
| 103 | + # known platform, libc, and threading implementations |
| 104 | + known_platforms = frozenset({ |
| 105 | + "AIX", "Android", "BSD", "DragonFlyBSD", "Emscripten", "FreeBSD", |
| 106 | + "GNU/kFreeBSD", "Linux", "NetBSD", "OpenBSD", "POSIX", "Solaris", |
| 107 | + "Unix", "VxWorks", "WASI", "Windows", "macOS", "iOS", |
| 108 | + # libc |
| 109 | + "BSD libc", "glibc", "musl", |
| 110 | + # POSIX platforms with pthreads |
| 111 | + "pthreads", |
| 112 | + }) |
| 113 | + |
| 114 | + def run(self): |
| 115 | + availability_ref = ':ref:`Availability <availability>`: ' |
| 116 | + avail_nodes, avail_msgs = self.state.inline_text( |
| 117 | + availability_ref + self.arguments[0], |
| 118 | + self.lineno) |
| 119 | + pnode = nodes.paragraph(availability_ref + self.arguments[0], |
| 120 | + '', *avail_nodes, *avail_msgs) |
| 121 | + self.set_source_info(pnode) |
| 122 | + cnode = nodes.container("", pnode, classes=["availability"]) |
| 123 | + self.set_source_info(cnode) |
| 124 | + if self.content: |
| 125 | + self.state.nested_parse(self.content, self.content_offset, cnode) |
| 126 | + self.parse_platforms() |
| 127 | + |
| 128 | + return [cnode] |
| 129 | + |
| 130 | + def parse_platforms(self): |
| 131 | + """Parse platform information from arguments |
| 132 | + Arguments is a comma-separated string of platforms. A platform may |
| 133 | + be prefixed with "not " to indicate that a feature is not available. |
| 134 | + Example:: |
| 135 | + .. availability:: Windows, Linux >= 4.2, not WASI |
| 136 | + Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not |
| 137 | + parsed into separate tokens. |
| 138 | + """ |
| 139 | + platforms = {} |
| 140 | + for arg in self.arguments[0].rstrip(".").split(","): |
| 141 | + arg = arg.strip() |
| 142 | + platform, _, version = arg.partition(" >= ") |
| 143 | + if platform.startswith("not "): |
| 144 | + version = False |
| 145 | + platform = platform[4:] |
| 146 | + elif not version: |
| 147 | + version = True |
| 148 | + platforms[platform] = version |
| 149 | + |
| 150 | + unknown = set(platforms).difference(self.known_platforms) |
| 151 | + if unknown: |
| 152 | + cls = type(self) |
| 153 | + logger = logging.getLogger(cls.__qualname__) |
| 154 | + logger.warning( |
| 155 | + f"Unknown platform(s) or syntax '{' '.join(sorted(unknown))}' " |
| 156 | + f"in '.. availability:: {self.arguments[0]}', see " |
| 157 | + f"{__file__}:{cls.__qualname__}.known_platforms for a set " |
| 158 | + "known platforms." |
| 159 | + ) |
| 160 | + |
| 161 | + return platforms |
| 162 | + |
| 163 | + |
93 | 164 | class PyCoroutineMixin(object):
|
94 | 165 | def handle_signature(self, sig, signode):
|
95 | 166 | ret = super(PyCoroutineMixin, self).handle_signature(sig, signode)
|
@@ -260,6 +331,7 @@ def setup(app):
|
260 | 331 | app.add_role('issue', issue_role)
|
261 | 332 | app.add_role('gh', gh_issue_role)
|
262 | 333 | app.add_directive('impl-detail', ImplementationDetail)
|
| 334 | + app.add_directive('availability', Availability) |
263 | 335 | app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
|
264 | 336 | app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
|
265 | 337 | app.add_object_type('monitoring-event', 'monitoring-event', '%s (monitoring event)', parse_monitoring_event)
|
|
0 commit comments