]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add Sphinx extension to help with ARM maintenance and cross-linking
authorPetr Špaček <pspacek@isc.org>
Tue, 10 May 2022 13:00:06 +0000 (15:00 +0200)
committerPetr Špaček <pspacek@isc.org>
Thu, 9 Jun 2022 12:44:29 +0000 (14:44 +0200)
The extension provides a "Sphinx domain factory". Each new Sphinx domain
defines a namespace for configuration statements so named.conf and
rndc.conf do not clash. Currently the Sphinx domains are instantiated
twice and resuling domains are named "namedconf" and "rndcconf".

This commit adds a single new directive:

.. statement:: max-cache-size

It is namespaced like this:

.. namedconf:statement:: max-cache-size

This directive generates a new anchor for configuration statement and it
can be referenced like :any:`max-cache-size` (if the identifier is
unique), or more specific :namedconf:ref:`max-cache-size`.

It is based on Sphinx "tutorial" extension "recipe".
Beware, some details in Sphinx docs are not up-to-date, it's better
to read Sphinx and docutil sources.

doc/arm/Makefile.am
doc/arm/_ext/iscconf.py [new file with mode: 0644]
doc/arm/_ext/namedconf.py [new file with mode: 0644]
doc/arm/_ext/rndcconf.py [new file with mode: 0644]
doc/arm/conf.py

index 798d6ba839f61e54c180f861f7d275320bd1d827..d4aa22b7677f638f0b7dedf9c46cf49939dc2a06 100644 (file)
@@ -61,6 +61,9 @@ EXTRA_DIST =                                  \
        troubleshooting.inc.rst                 \
        tsig.inc.rst                            \
        zones.inc.rst                           \
+       _ext/iscconf.py                         \
+       _ext/namedconf.py                       \
+       _ext/rndcconf.py                        \
        _static/custom.css                      \
        ../dnssec-guide                         \
        ../misc/acl.grammar.rst                 \
diff --git a/doc/arm/_ext/iscconf.py b/doc/arm/_ext/iscconf.py
new file mode 100644 (file)
index 0000000..8608e03
--- /dev/null
@@ -0,0 +1,176 @@
+############################################################################
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+############################################################################
+
+"""
+Sphinx domains for ISC configuration files.
+
+Use setup() to install new Sphinx domains for ISC configuration files.
+
+This extension is based on combination of two Sphinx extension tutorials:
+https://www.sphinx-doc.org/en/master/development/tutorials/todo.html
+https://www.sphinx-doc.org/en/master/development/tutorials/recipe.html
+"""
+
+from sphinx import addnodes
+from sphinx.directives import ObjectDescription
+from sphinx.domains import Domain
+from sphinx.roles import XRefRole
+from sphinx.util.nodes import make_refnode
+
+
+# pylint: disable=too-many-statements
+def domain_factory(domainname, domainlabel):
+    """
+    Return parametrized Sphinx domain object.
+    @param domainname Name used when referencing domain in .rst: e.g. namedconf
+    @param confname Humand-readable name for texts, e.g. named.conf
+    """
+
+    class ISCConfDomain(Domain):
+        """
+        Custom Sphinx domain for ISC config.
+        Provides .. statement:: directive to define config statement.
+        :ref:`statementname` works as usual.
+
+        See https://www.sphinx-doc.org/en/master/extdev/domainapi.html
+        """
+
+        class StatementDirective(ObjectDescription):
+            """
+            A custom directive that describes a statement,
+            e.g. max-cache-size.
+            """
+
+            has_content = True
+            required_arguments = 1
+            option_spec = {}
+
+            def handle_signature(self, sig, signode):
+                signode += addnodes.desc_name(text=sig)
+                return sig
+
+            def add_target_and_index(self, _name_cls, sig, signode):
+                signode["ids"].append(domainname + "-statement-" + sig)
+
+                iscconf = self.env.get_domain(domainname)
+                iscconf.add_statement(sig)
+
+        name = domainname
+        label = domainlabel
+
+        directives = {
+            "statement": StatementDirective,
+        }
+
+        roles = {"ref": XRefRole(warn_dangling=True)}
+        initial_data = {
+            "statements": [],  # object list for Sphinx API
+        }
+
+        indices = {}  # no custom indicies
+
+        def get_objects(self):
+            """Sphinx API: iterable of object descriptions"""
+            for obj in self.data["statements"]:
+                yield obj
+
+        # pylint: disable=too-many-arguments
+        def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
+            """
+            Sphinx API:
+            Resolve the pending_xref *node* with the given typ and target.
+            """
+            match = [
+                (docname, anchor)
+                for name, sig, typ, docname, anchor, _prio in self.get_objects()
+                if sig == target
+            ]
+
+            if len(match) == 0:
+                return None
+            todocname = match[0][0]
+            targ = match[0][1]
+
+            refnode = make_refnode(
+                builder, fromdocname, todocname, targ, contnode, targ
+            )
+            return refnode
+
+        def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
+            """
+            Sphinx API:
+            Raising NotImplementedError uses fall-back bassed on resolve_xref.
+            """
+            raise NotImplementedError
+
+        def add_statement(self, signature):
+            """
+            Add a new statement to the domain data structures.
+            No visible effect.
+            """
+            name = "{}.{}.{}".format(domainname, "statement", signature)
+            anchor = "{}-statement-{}".format(domainname, signature)
+
+            # Sphinx API: name, dispname, type, docname, anchor, priority
+            self.data["statements"].append(
+                (
+                    name,
+                    signature,
+                    domainlabel + " statement",
+                    self.env.docname,
+                    anchor,
+                    1,
+                )
+            )
+
+        def clear_doc(self, docname):
+            """
+            Sphinx API: like env-purge-doc event, but in a domain.
+
+            Remove traces of a document in the domain-specific inventories.
+            """
+            self.data["statements"] = list(
+                obj for obj in self.data["statements"] if obj[3] != docname
+            )
+
+        def merge_domaindata(self, docnames, otherdata):
+            """Sphinx API: Merge in data regarding *docnames* from a different
+            domaindata inventory (coming from a subprocess in parallel builds).
+
+            @param otherdata is self.data equivalent from another process
+
+            Beware: As of Sphinx 4.5.0, this is called multiple times in a row
+            with the same data and has to guard against duplicites.  It seems
+            that all existing domains in Sphinx distribution have todo like
+            "deal with duplicates" but do nothing about them, so we just follow
+            the suite."""
+            self.data["statements"] = list(
+                set(self.data["statements"] + otherdata["statements"])
+            )
+
+    return ISCConfDomain
+
+
+def setup(app, domainname, confname):
+    """
+    Install new parametrized Sphinx domain.
+    """
+
+    Conf = domain_factory(domainname, confname)
+    app.add_domain(Conf)
+
+    return {
+        "version": "0.1",
+        "parallel_read_safe": True,
+        "parallel_write_safe": True,
+    }
diff --git a/doc/arm/_ext/namedconf.py b/doc/arm/_ext/namedconf.py
new file mode 100644 (file)
index 0000000..40dc070
--- /dev/null
@@ -0,0 +1,22 @@
+############################################################################
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+############################################################################
+
+"""
+Sphinx domain "namedconf". See iscconf.py for details.
+
+"""
+import iscconf
+
+
+def setup(app):
+    return iscconf.setup(app, "namedconf", "named.conf")
diff --git a/doc/arm/_ext/rndcconf.py b/doc/arm/_ext/rndcconf.py
new file mode 100644 (file)
index 0000000..2a7d2cd
--- /dev/null
@@ -0,0 +1,22 @@
+############################################################################
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+############################################################################
+
+"""
+Sphinx domain "rndcconf". See iscconf.py for details.
+"""
+
+import iscconf
+
+
+def setup(app):
+    return iscconf.setup(app, "rndcconf", "rndc.conf")
index 84061baae28d70b0e4ae75e6b4e7a8076adf39ea..578591e463314a7d7c4befc6deeccd728fc80f05 100644 (file)
@@ -13,7 +13,9 @@
 
 # flake8: noqa: E501
 
+from pathlib import Path
 import re
+import sys
 
 from typing import List, Tuple
 
@@ -99,12 +101,9 @@ def setup(app):
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
+# documentation root, make it absolute.
 #
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
-
+sys.path.append(str(Path(__file__).resolve().parent / "_ext"))
 
 # -- Project information -----------------------------------------------------
 
@@ -135,7 +134,7 @@ release = version
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = []
+extensions = ["namedconf", "rndcconf"]
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ["_templates"]