2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

Add Sphinx extension to help with ARM maintenance and cross-linking

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.
This commit is contained in:
Petr Špaček
2022-05-10 15:00:06 +02:00
parent 57b72ba1cd
commit a23fa7edc9
5 changed files with 228 additions and 6 deletions

176
doc/arm/_ext/iscconf.py Normal file
View File

@@ -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,
}