diff --git a/Doc/tools/extensions/changes.py b/Doc/tools/extensions/changes.py index 02dc51b3a76943a..e6a912cef8810ea 100644 --- a/Doc/tools/extensions/changes.py +++ b/Doc/tools/extensions/changes.py @@ -6,6 +6,7 @@ from docutils import nodes from sphinx import addnodes +from sphinx.builders.changes import ChangesBuilder from sphinx.domains.changeset import ( VersionChange, versionlabel_classes, @@ -17,6 +18,7 @@ if TYPE_CHECKING: from docutils.nodes import Node from sphinx.application import Sphinx + from sphinx.environment import BuildEnvironment from sphinx.util.typing import ExtensionMetadata @@ -146,6 +148,32 @@ def _add_glossary_link(cls, inline: nodes.inline) -> None: break +def _fixup_changesets(app: Sphinx, env: BuildEnvironment) -> None: + changesets = env.get_domain("changeset").changesets + + # The changeset domain records each entry's plain text before SoftDeprecated + # replaces the :term:, so strip the markup before the changes builder renders it. + for entries in changesets.values(): + for i, entry in enumerate(entries): + if entry.type == "soft-deprecated": + entries[i] = entry._replace( + content=SoftDeprecated._TERM_RE.sub(r"\1", entry.content) + ) + + # DeprecatedRemoved entries are recorded under their (deprecated, + # removed) version tuple, which the changes builder ignores. + # Re-file them under both versions. + for versions in [v for v in changesets if isinstance(v, tuple)]: + deprecated, removed = versions + for entry in changesets.pop(versions): + changesets.setdefault(deprecated, []).append( + entry._replace(type="deprecated") + ) + changesets.setdefault(removed, []).append( + entry._replace(type="versionremoved") + ) + + def setup(app: Sphinx) -> ExtensionMetadata: # Override Sphinx's directives with support for 'next' app.add_directive("versionadded", PyVersionChange, override=True) @@ -155,9 +183,15 @@ def setup(app: Sphinx) -> ExtensionMetadata: # Register the ``.. deprecated-removed::`` directive app.add_directive("deprecated-removed", DeprecatedRemoved) + # _fixup_changesets() changes these entries to 'deprecated'/'versionremoved' + ChangesBuilder.typemap["deprecated-removed"] = "deprecated-removed" # Register the ``.. soft-deprecated::`` directive app.add_directive("soft-deprecated", SoftDeprecated) + ChangesBuilder.typemap["soft-deprecated"] = "soft deprecated" + + # Repair the recorded changesets for the couple of custom directives above + app.connect("env-updated", _fixup_changesets) return { "version": "1.0",