From 3eeffa8cecb36eb51b438b53fbe760fac8183464 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Thu, 11 Jun 2026 12:12:53 +0100 Subject: [PATCH] Validate that docs pages are using the readingtime directive. --- docs/src/conf.py | 1 + docs/src/sphinxext/readingtime_validator.py | 101 ++++++++++++++++++++ docs/src/whatsnew/latest.rst | 3 +- 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 docs/src/sphinxext/readingtime_validator.py diff --git a/docs/src/conf.py b/docs/src/conf.py index 70afbef386..afc5122bf5 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -155,6 +155,7 @@ def _dotv(version): "matplotlib.sphinxext.mathmpl", "matplotlib.sphinxext.plot_directive", "readingtime", + "readingtime_validator", "sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.coverage", diff --git a/docs/src/sphinxext/readingtime_validator.py b/docs/src/sphinxext/readingtime_validator.py new file mode 100644 index 0000000000..210545bccc --- /dev/null +++ b/docs/src/sphinxext/readingtime_validator.py @@ -0,0 +1,101 @@ +# Copyright Iris contributors +# +# This file is part of Iris and is released under the BSD license. +# See LICENSE in the root of the repository for full licensing details. +"""Sphinx extension to validate readingtime directive presence in docs pages.""" + +from __future__ import annotations + +import fnmatch +from pathlib import Path +from typing import TYPE_CHECKING + +from docutils import nodes # type: ignore[import-untyped] +from sphinx.util import logging as sphinx_logging + +if TYPE_CHECKING: + from sphinx.application import Sphinx + +logger = sphinx_logging.getLogger(__name__) + +READINGTIME_EXCEPTIONS = { + "index", + "*/index", + "voted_issues", + "sg_execution_times", + "copyright", + "user_manual/section_indexes/*", + "user_manual/reference/glossary", + "developers_guide/contributing_getting_involved", + "developers_guide/contributing_changes", + "developers_guide/contributing_codebase_index", + "developers_guide/contributing_documentation", + "developers_guide/contributing_testing_index", + "developers_guide/release_do_nothing", + "developers_guide/gitwash/**", + "generated/**", + "whatsnew/**", +} +""" +Pages exempt from readingtime validation. + +Supports glob patterns using standard wildcards. +""" + + +def _is_exception(docname: str) -> bool: + for pattern in READINGTIME_EXCEPTIONS: + if fnmatch.fnmatch(docname, pattern): + return True + return False + + +def _has_readingtime_directive(doctree: nodes.document) -> bool: + # Look for nodes generated by the readingtime directive + # The directive produces a raw HTML node with a specific class + for node in doctree.findall(nodes.raw): + if "reading-time" in str(node): + return True + return False + + +def _validate_readingtime( + app: Sphinx, + doctree: nodes.document, + docname: str, +) -> None: + """Validate ``readingtime`` directive presence in docs pages. + + Parameters + ---------- + app : Sphinx + The Sphinx application object. + doctree : nodes.document + The parsed document tree. + docname : str + The name of the document being processed. + + """ + # Check if this docname matches any exception pattern + if _is_exception(docname): + return + + # Validate readingtime presence + if not _has_readingtime_directive(doctree): + msg = ( + f"Missing '.. readingtime::' directive in '{docname}.rst'. " + f"Documentation pages should include a readingtime directive for consistency. " + f"If this page should be exempt, add '{docname}' to READINGTIME_EXCEPTIONS " + f"in sphinxext/readingtime_validator.py" + ) + logger.warning(msg) + + +def setup(app: Sphinx) -> dict: + app.connect("doctree-resolved", _validate_readingtime) + + return { + "version": "0.1.0", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 8486209531..da47d57397 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -114,7 +114,8 @@ This document explains the changes made to Iris for this release :func:`iris.analysis.cartography.area_weights` requires 1-dimensional lat and lon coordinates on the input :class:`~iris.cube.Cube`. (:pull:`7118`) -#. :user:`bjlittle` Added the custom `sphinx`_ ``readingtime`` directive to +#. :user:`bjlittle` and :user:`trexfeathers` added the custom `sphinx`_ ``readingtime`` + directive to automatically estimate the audiance reading time of a page and render a branded banner in-situ. (:pull:`7150`)