]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-121970: Extract ``availability`` into a new extension (#125082)
authorAdam Turner <9087854+AA-Turner@users.noreply.github.com>
Wed, 9 Oct 2024 20:50:03 +0000 (21:50 +0100)
committerGitHub <noreply@github.com>
Wed, 9 Oct 2024 20:50:03 +0000 (21:50 +0100)
Doc/conf.py
Doc/tools/extensions/availability.py [new file with mode: 0644]
Doc/tools/extensions/pyspecific.py

index 5f22340ac434c94f2758f16e4074b690f7766efd..287e0da46eb11cb306fa81261e890cbb1b5b1efa 100644 (file)
@@ -21,6 +21,7 @@ from pyspecific import SOURCE_URI
 
 extensions = [
     'audit_events',
+    'availability',
     'c_annotations',
     'glossary_search',
     'lexers',
diff --git a/Doc/tools/extensions/availability.py b/Doc/tools/extensions/availability.py
new file mode 100644 (file)
index 0000000..47833fd
--- /dev/null
@@ -0,0 +1,125 @@
+"""Support for documenting platform availability"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from docutils import nodes
+from sphinx import addnodes
+from sphinx.util import logging
+from sphinx.util.docutils import SphinxDirective
+
+if TYPE_CHECKING:
+    from sphinx.application import Sphinx
+    from sphinx.util.typing import ExtensionMetadata
+
+logger = logging.getLogger("availability")
+
+# known platform, libc, and threading implementations
+_PLATFORMS = frozenset({
+    "AIX",
+    "Android",
+    "BSD",
+    "DragonFlyBSD",
+    "Emscripten",
+    "FreeBSD",
+    "GNU/kFreeBSD",
+    "iOS",
+    "Linux",
+    "macOS",
+    "NetBSD",
+    "OpenBSD",
+    "POSIX",
+    "Solaris",
+    "Unix",
+    "VxWorks",
+    "WASI",
+    "Windows",
+})
+_LIBC = frozenset({
+    "BSD libc",
+    "glibc",
+    "musl",
+})
+_THREADING = frozenset({
+    # POSIX platforms with pthreads
+    "pthreads",
+})
+KNOWN_PLATFORMS = _PLATFORMS | _LIBC | _THREADING
+
+
+class Availability(SphinxDirective):
+    has_content = True
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+
+    def run(self) -> list[nodes.container]:
+        title = "Availability"
+        refnode = addnodes.pending_xref(
+            title,
+            nodes.inline(title, title, classes=["xref", "std", "std-ref"]),
+            refdoc=self.env.docname,
+            refdomain="std",
+            refexplicit=True,
+            reftarget="availability",
+            reftype="ref",
+            refwarn=True,
+        )
+        sep = nodes.Text(": ")
+        parsed, msgs = self.state.inline_text(self.arguments[0], self.lineno)
+        pnode = nodes.paragraph(title, "", refnode, sep, *parsed, *msgs)
+        self.set_source_info(pnode)
+        cnode = nodes.container("", pnode, classes=["availability"])
+        self.set_source_info(cnode)
+        if self.content:
+            self.state.nested_parse(self.content, self.content_offset, cnode)
+        self.parse_platforms()
+
+        return [cnode]
+
+    def parse_platforms(self) -> dict[str, str | bool]:
+        """Parse platform information from arguments
+
+        Arguments is a comma-separated string of platforms. A platform may
+        be prefixed with "not " to indicate that a feature is not available.
+
+        Example::
+
+           .. availability:: Windows, Linux >= 4.2, not WASI
+
+        Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not
+        parsed into separate tokens.
+        """
+        platforms = {}
+        for arg in self.arguments[0].rstrip(".").split(","):
+            arg = arg.strip()
+            platform, _, version = arg.partition(" >= ")
+            if platform.startswith("not "):
+                version = False
+                platform = platform.removeprefix("not ")
+            elif not version:
+                version = True
+            platforms[platform] = version
+
+        if unknown := set(platforms).difference(KNOWN_PLATFORMS):
+            logger.warning(
+                "Unknown platform%s or syntax '%s' in '.. availability:: %s', "
+                "see %s:KNOWN_PLATFORMS for a set of known platforms.",
+                "s" if len(platforms) != 1 else "",
+                " ".join(sorted(unknown)),
+                self.arguments[0],
+                __file__,
+            )
+
+        return platforms
+
+
+def setup(app: Sphinx) -> ExtensionMetadata:
+    app.add_directive("availability", Availability)
+
+    return {
+        "version": "1.0",
+        "parallel_read_safe": True,
+        "parallel_write_safe": True,
+    }
index b6623a2b8e01f16e75a08d32c7342641b9ddcb99..bcb8a421e32d09e12b93af3ac06e162901418692 100644 (file)
@@ -24,7 +24,6 @@ from sphinx.builders import Builder
 from sphinx.domains.changeset import VersionChange, versionlabels, versionlabel_classes
 from sphinx.domains.python import PyFunction, PyMethod, PyModule
 from sphinx.locale import _ as sphinx_gettext
-from sphinx.util import logging
 from sphinx.util.docutils import SphinxDirective
 from sphinx.writers.text import TextWriter, TextTranslator
 from sphinx.util.display import status_iterator
@@ -108,80 +107,6 @@ class ImplementationDetail(SphinxDirective):
         return [pnode]
 
 
-# Support for documenting platform availability
-
-class Availability(SphinxDirective):
-
-    has_content = True
-    required_arguments = 1
-    optional_arguments = 0
-    final_argument_whitespace = True
-
-    # known platform, libc, and threading implementations
-    known_platforms = frozenset({
-        "AIX", "Android", "BSD", "DragonFlyBSD", "Emscripten", "FreeBSD",
-        "GNU/kFreeBSD", "Linux", "NetBSD", "OpenBSD", "POSIX", "Solaris",
-        "Unix", "VxWorks", "WASI", "Windows", "macOS", "iOS",
-        # libc
-        "BSD libc", "glibc", "musl",
-        # POSIX platforms with pthreads
-        "pthreads",
-    })
-
-    def run(self):
-        availability_ref = ':ref:`Availability <availability>`: '
-        avail_nodes, avail_msgs = self.state.inline_text(
-            availability_ref + self.arguments[0],
-            self.lineno)
-        pnode = nodes.paragraph(availability_ref + self.arguments[0],
-                                '', *avail_nodes, *avail_msgs)
-        self.set_source_info(pnode)
-        cnode = nodes.container("", pnode, classes=["availability"])
-        self.set_source_info(cnode)
-        if self.content:
-            self.state.nested_parse(self.content, self.content_offset, cnode)
-        self.parse_platforms()
-
-        return [cnode]
-
-    def parse_platforms(self):
-        """Parse platform information from arguments
-
-        Arguments is a comma-separated string of platforms. A platform may
-        be prefixed with "not " to indicate that a feature is not available.
-
-        Example::
-
-           .. availability:: Windows, Linux >= 4.2, not WASI
-
-        Arguments like "Linux >= 3.17 with glibc >= 2.27" are currently not
-        parsed into separate tokens.
-        """
-        platforms = {}
-        for arg in self.arguments[0].rstrip(".").split(","):
-            arg = arg.strip()
-            platform, _, version = arg.partition(" >= ")
-            if platform.startswith("not "):
-                version = False
-                platform = platform[4:]
-            elif not version:
-                version = True
-            platforms[platform] = version
-
-        unknown = set(platforms).difference(self.known_platforms)
-        if unknown:
-            cls = type(self)
-            logger = logging.getLogger(cls.__qualname__)
-            logger.warning(
-                f"Unknown platform(s) or syntax '{' '.join(sorted(unknown))}' "
-                f"in '.. availability:: {self.arguments[0]}', see "
-                f"{__file__}:{cls.__qualname__}.known_platforms for a set "
-                "known platforms."
-            )
-
-        return platforms
-
-
 # Support for documenting decorators
 
 class PyDecoratorMixin(object):
@@ -492,7 +417,6 @@ def setup(app):
     app.add_role('issue', issue_role)
     app.add_role('gh', gh_issue_role)
     app.add_directive('impl-detail', ImplementationDetail)
-    app.add_directive('availability', Availability)
     app.add_directive('versionadded', PyVersionChange, override=True)
     app.add_directive('versionchanged', PyVersionChange, override=True)
     app.add_directive('versionremoved', PyVersionChange, override=True)