Source code for sphinx_toolbox.changeset

#!/usr/bin/env python3
#
#  changeset.py
"""
Customised versions of Sphinx's :rst:dir:`versionadded`, :rst:dir:`versionchanged`
and :rst:dir:`deprecated` directives to correctly handle bullet lists.

.. versionadded:: 2.11.0
.. extensions:: sphinx_toolbox.changeset


Usage
------

.. rst:directive:: .. versionadded:: version

   Documents the version of the project which added the described feature.

   The first argument must be given and is the version in question; you can add
   a second argument consisting of a *brief* explanation of the change.
   Alternatively, a longer description my be given in the body of the directive.

.. rst:directive:: .. versionchanged:: version

   Similar to :rst:dir:`versionadded`, but describes when and what changed in
   the feature in some way (new parameters, changed side effects, etc.).

.. rst:directive:: .. deprecated:: version

   Similar to :rst:dir:`versionchanged`, but describes when the feature was deprecated.
   An explanation can also be given, for example to inform the reader what should be used instead.

.. latex:vspace:: 10px

This extension also adds the following directive:

.. rst:directive:: .. versionremoved:: version [details]

   Similar to :rst:dir:`versionchanged`, but describes when the feature was or will be removed.
   An explanation can also be given, for example to inform the reader what should be used instead.


.. latex:clearpage::

API Reference
----------------

"""  # noqa: D400
#
#  Copyright © 2021 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.
#
#  Based on Sphinx
#  Copyright (c) 2007-2021 by the Sphinx team.
#  |  All rights reserved.
#  |
#  |  Redistribution and use in source and binary forms, with or without
#  |  modification, are permitted provided that the following conditions are
#  |  met:
#  |
#  |  * Redistributions of source code must retain the above copyright
#  |    notice, this list of conditions and the following disclaimer.
#  |
#  |  * Redistributions in binary form must reproduce the above copyright
#  |    notice, this list of conditions and the following disclaimer in the
#  |    documentation and/or other materials provided with the distribution.
#  |
#  |  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  |  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  |  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  |  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  |  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  |  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  |  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  |  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  |  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  |  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  |  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# stdlib
from typing import List, Optional, cast

# 3rd party
import sphinx.domains.changeset
from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.locale import _

# this package
from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version

__all__ = ("VersionChange", "setup")

versionlabels = {
		"versionremoved": _("Removed in version %s"),
		**sphinx.domains.changeset.versionlabels,
		}

versionlabel_classes = {
		"versionremoved": "removed",
		**sphinx.domains.changeset.versionlabel_classes,
		}


[docs]class VersionChange(sphinx.domains.changeset.VersionChange): """ Directive to describe a addition/change/deprecation/removal in a specific version. """ def run(self) -> List[Node]: """ Process the content of the directive. """ node = addnodes.versionmodified() node.document = self.state.document self.set_source_info(node) node["type"] = self.name node["version"] = self.arguments[0] text = versionlabels[self.name] % self.arguments[0] if len(self.arguments) == 2: inodes, messages = self.state.inline_text(self.arguments[1], self.lineno + 1) para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False) self.set_source_info(para) node.append(para) else: messages = [] # pylint: disable=W8301 if self.content: self.state.nested_parse(self.content, self.content_offset, node) classes = ["versionmodified", versionlabel_classes[self.name]] # pylint: disable=W8301 if len(node): to_add: Optional[nodes.Node] = None if isinstance(node[0], nodes.paragraph) and node[0].rawsource: content = nodes.inline(node[0].rawsource, translatable=True) content.source = node[0].source content.line = node[0].line content += node[0].children node[0].replace_self(nodes.paragraph('', '', content, translatable=False)) elif isinstance(node[0], (nodes.bullet_list, nodes.enumerated_list)): # Fix for incorrect ordering with bullet lists node.insert(0, nodes.compound('')) to_add = nodes.paragraph('', '') para = cast(nodes.paragraph, node[0]) para.insert(0, nodes.inline('', f'{text}: ', classes=classes)) if to_add is not None: node.insert(0, to_add) else: para = nodes.paragraph( '', '', nodes.inline('', f'{text}.', classes=classes), translatable=False, ) node.append(para) domain = cast( sphinx.domains.changeset.ChangeSetDomain, self.env.get_domain("changeset"), ) domain.note_changeset(node) ret: List[Node] = [node] # pylint: disable=W8301 ret += messages return ret
[docs]@metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.changeset`. :param app: The Sphinx application. """ app.add_directive("deprecated", VersionChange, override=True) app.add_directive("versionadded", VersionChange, override=True) app.add_directive("versionchanged", VersionChange, override=True) app.add_directive("versionremoved", VersionChange, override=True) return { "parallel_read_safe": True, "parallel_write_safe": True, }