"""Support for documenting version of changes, additions, deprecations.""" from __future__ import annotations from typing import TYPE_CHECKING from sphinx.domains.changeset import ( VersionChange, versionlabel_classes, versionlabels, ) from sphinx.locale import _ as sphinx_gettext if TYPE_CHECKING: from docutils.nodes import Node from sphinx.application import Sphinx from sphinx.util.typing import ExtensionMetadata def expand_version_arg(argument: str, release: str) -> str: """Expand "next" to the current version""" if argument == "next": return sphinx_gettext("{} (unreleased)").format(release) return argument class PyVersionChange(VersionChange): def run(self) -> list[Node]: # Replace the 'next' special token with the current development version self.arguments[0] = expand_version_arg( self.arguments[0], self.config.release ) return super().run() class DeprecatedRemoved(VersionChange): required_arguments = 2 _deprecated_label = sphinx_gettext( "Deprecated since version %s, will be removed in version %s" ) _removed_label = sphinx_gettext( "Deprecated since version %s, removed in version %s" ) def run(self) -> list[Node]: # Replace the first two arguments (deprecated version and removed version) # with a single tuple of both versions. version_deprecated = expand_version_arg( self.arguments[0], self.config.release ) version_removed = self.arguments.pop(1) if version_removed == "next": raise ValueError( "deprecated-removed:: second argument cannot be `next`" ) self.arguments[0] = version_deprecated, version_removed # Set the label based on if we have reached the removal version current_version = tuple(map(int, self.config.version.split("."))) removed_version = tuple(map(int, version_removed.split("."))) if current_version < removed_version: versionlabels[self.name] = self._deprecated_label versionlabel_classes[self.name] = "deprecated" else: versionlabels[self.name] = self._removed_label versionlabel_classes[self.name] = "removed" try: return super().run() finally: # reset versionlabels and versionlabel_classes versionlabels[self.name] = "" versionlabel_classes[self.name] = "" def setup(app: Sphinx) -> ExtensionMetadata: # Override Sphinx's directives with support for 'next' app.add_directive("versionadded", PyVersionChange, override=True) app.add_directive("versionchanged", PyVersionChange, override=True) app.add_directive("versionremoved", PyVersionChange, override=True) app.add_directive("deprecated", PyVersionChange, override=True) # Register the ``.. deprecated-removed::`` directive app.add_directive("deprecated-removed", DeprecatedRemoved) return { "version": "1.0", "parallel_read_safe": True, "parallel_write_safe": True, }