--- /dev/null
+"""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,
+ }
from docutils.utils import new_document, unescape
from sphinx import addnodes
from sphinx.builders import Builder
-from sphinx.domains.changeset import VersionChange, versionlabels, versionlabel_classes
from sphinx.domains.python import PyFunction, PyMethod, PyModule
from sphinx.locale import _ as sphinx_gettext
from sphinx.util.docutils import SphinxDirective
return PyMethod.run(self)
-# Support for documenting version of changes, additions, deprecations
-
-def expand_version_arg(argument, release):
- """Expand "next" to the current version"""
- if argument == 'next':
- return sphinx_gettext('{} (unreleased)').format(release)
- return argument
-
-
-class PyVersionChange(VersionChange):
- def run(self):
- # 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):
- # 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] = ''
-
-
# Support for including Misc/NEWS
issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I)
app.add_role('issue', issue_role)
app.add_role('gh', gh_issue_role)
app.add_directive('impl-detail', ImplementationDetail)
- 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)
- app.add_directive('deprecated-removed', DeprecatedRemoved)
app.add_builder(PydocTopicsBuilder)
app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)