Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
101 changes: 101 additions & 0 deletions docs/src/sphinxext/readingtime_validator.py
Original file line number Diff line number Diff line change
@@ -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,
}
3 changes: 2 additions & 1 deletion docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`)

Expand Down
Loading