]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] Add a new Sphinx `soft-deprecated` directive (GH-148630) (#148714)
authorHugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Wed, 22 Apr 2026 21:41:40 +0000 (00:41 +0300)
committerGitHub <noreply@github.com>
Wed, 22 Apr 2026 21:41:40 +0000 (00:41 +0300)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Co-authored-by: Stan Ulbrych <stan@python.org>
Doc/c-api/allocation.rst
Doc/c-api/file.rst
Doc/c-api/frame.rst
Doc/c-api/long.rst
Doc/c-api/module.rst
Doc/c-api/monitoring.rst
Doc/c-api/sequence.rst
Doc/library/mimetypes.rst
Doc/library/os.rst
Doc/tools/extensions/changes.py
Doc/tools/templates/dummy.html

index 59044d2d88cc1685de973dfd7c034de035704c77..5f7a9152eccfc0a1e6ef68dd364d26c88d1171d5 100644 (file)
@@ -2,7 +2,7 @@
 
 .. _allocating-objects:
 
-Allocating Objects on the Heap
+Allocating objects on the heap
 ==============================
 
 
@@ -153,10 +153,12 @@ Allocating Objects on the Heap
       To allocate and create extension modules.
 
 
-Deprecated aliases
-^^^^^^^^^^^^^^^^^^
+Soft-deprecated aliases
+^^^^^^^^^^^^^^^^^^^^^^^
 
-These are :term:`soft deprecated` aliases to existing functions and macros.
+.. soft-deprecated:: 3.10
+
+These are aliases to existing functions and macros.
 They exist solely for backwards compatibility.
 
 
@@ -164,7 +166,7 @@ They exist solely for backwards compatibility.
    :widths: auto
    :header-rows: 1
 
-   * * Deprecated alias
+   * * Soft-deprecated alias
      * Function
    * * .. c:macro:: PyObject_NEW(type, typeobj)
      * :c:macro:`PyObject_New`
index d89072ab24e241d9eebc40d2f8096e1f61bd33f0..dcafefdc0458722e77771216954424191210f2a7 100644 (file)
@@ -2,7 +2,7 @@
 
 .. _fileobjects:
 
-File Objects
+File objects
 ------------
 
 .. index:: pair: object; file
@@ -136,11 +136,12 @@ the :mod:`io` APIs instead.
    failure; the appropriate exception will be set.
 
 
-Deprecated API
-^^^^^^^^^^^^^^
+Soft-deprecated API
+^^^^^^^^^^^^^^^^^^^
 
+.. soft-deprecated:: 3.15
 
-These are :term:`soft deprecated` APIs that were included in Python's C API
+These are APIs that were included in Python's C API
 by mistake. They are documented solely for completeness; use other
 ``PyFile*`` APIs instead.
 
index 967cfc727655ecbe53bafe684fa0bc2318490598..4159ff6e5965fbd8d783c040f78b9e9921ac02b1 100644 (file)
@@ -1,6 +1,6 @@
 .. highlight:: c
 
-Frame Objects
+Frame objects
 -------------
 
 .. c:type:: PyFrameObject
@@ -147,7 +147,7 @@ See also :ref:`Reflection <reflection>`.
    Return the line number that *frame* is currently executing.
 
 
-Frame Locals Proxies
+Frame locals proxies
 ^^^^^^^^^^^^^^^^^^^^
 
 .. versionadded:: 3.13
@@ -169,7 +169,7 @@ See :pep:`667` for more information.
    Return non-zero if *obj* is a frame :func:`locals` proxy.
 
 
-Legacy Local Variable APIs
+Legacy local variable APIs
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 These APIs are :term:`soft deprecated`. As of Python 3.13, they do nothing.
@@ -178,40 +178,34 @@ They exist solely for backwards compatibility.
 
 .. c:function:: void PyFrame_LocalsToFast(PyFrameObject *f, int clear)
 
-   This function is :term:`soft deprecated` and does nothing.
-
    Prior to Python 3.13, this function would copy the :attr:`~frame.f_locals`
    attribute of *f* to the internal "fast" array of local variables, allowing
    changes in frame objects to be visible to the interpreter. If *clear* was
    true, this function would process variables that were unset in the locals
    dictionary.
 
-   .. versionchanged:: 3.13
+   .. soft-deprecated:: 3.13
       This function now does nothing.
 
 
 .. c:function:: void PyFrame_FastToLocals(PyFrameObject *f)
 
-   This function is :term:`soft deprecated` and does nothing.
-
    Prior to Python 3.13, this function would copy the internal "fast" array
    of local variables (which is used by the interpreter) to the
    :attr:`~frame.f_locals` attribute of *f*, allowing changes in local
    variables to be visible to frame objects.
 
-   .. versionchanged:: 3.13
+   .. soft-deprecated:: 3.13
       This function now does nothing.
 
 
 .. c:function:: int PyFrame_FastToLocalsWithError(PyFrameObject *f)
 
-   This function is :term:`soft deprecated` and does nothing.
-
    Prior to Python 3.13, this function was similar to
    :c:func:`PyFrame_FastToLocals`, but would return ``0`` on success, and
    ``-1`` with an exception set on failure.
 
-   .. versionchanged:: 3.13
+   .. soft-deprecated:: 3.13
       This function now does nothing.
 
 
@@ -219,7 +213,7 @@ They exist solely for backwards compatibility.
    :pep:`667`
 
 
-Internal Frames
+Internal frames
 ^^^^^^^^^^^^^^^
 
 Unless using :pep:`523`, you will not need this.
@@ -249,5 +243,3 @@ Unless using :pep:`523`, you will not need this.
    Return the currently executing line number, or -1 if there is no line number.
 
    .. versionadded:: 3.12
-
-
index acc3f9668fdbaf291aa49d47233063393ca6a890..31cf1abf0c338138c2bd4e59eb935ccc58a248d0 100644 (file)
@@ -197,12 +197,10 @@ distinguished from a number.  Use :c:func:`PyErr_Occurred` to disambiguate.
 
    .. c:function:: long PyLong_AS_LONG(PyObject *obj)
 
-      A :term:`soft deprecated` alias.
       Exactly equivalent to the preferred ``PyLong_AsLong``. In particular,
       it can fail with :exc:`OverflowError` or another exception.
 
-      .. deprecated:: 3.14
-         The function is soft deprecated.
+      .. soft-deprecated:: 3.14
 
 .. c:function:: int PyLong_AsInt(PyObject *obj)
 
index 49ac4de6655a881e4e05be0fd76974bc6dd200c1..5a562721ce5df42c457ab9d01f07359847cb5f50 100644 (file)
@@ -604,9 +604,7 @@ or code that creates modules dynamically.
         // PyModule_AddObject() stole a reference to obj:
         // Py_XDECREF(obj) is not needed here.
 
-   .. deprecated:: 3.13
-
-      :c:func:`PyModule_AddObject` is :term:`soft deprecated`.
+   .. soft-deprecated:: 3.13
 
 
 .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)
index b0227c2f4faf15ccc7e47be5b1dd908ac1e76102..4bfcb86abf58ed164fb28cbfef9ab6ac9e1f8205 100644 (file)
@@ -205,6 +205,4 @@ would typically correspond to a Python function.
 
    .. versionadded:: 3.13
 
-   .. deprecated:: 3.14
-
-      This function is :term:`soft deprecated`.
+   .. soft-deprecated:: 3.14
index df5bf6b64a93a0088652fa588005062f7b80b7a2..6bae8f25ad75d151b70451617c39819701516e7b 100644 (file)
@@ -109,9 +109,8 @@ Sequence Protocol
 
    Alias for :c:func:`PySequence_Contains`.
 
-   .. deprecated:: 3.14
-      The function is :term:`soft deprecated` and should no longer be used to
-      write new code.
+   .. soft-deprecated:: 3.14
+      The function should no longer be used to write new code.
 
 
 .. c:function:: Py_ssize_t PySequence_Index(PyObject *o, PyObject *value)
index f489b60af3cb44ce17b560085344adef5abe2027..81212b1fea9326617da2b3fc30f3339b026549c8 100644 (file)
@@ -56,7 +56,7 @@ the information :func:`init` sets up.
    .. versionchanged:: 3.8
       Added support for *url* being a :term:`path-like object`.
 
-   .. deprecated:: 3.13
+   .. soft-deprecated:: 3.13
       Passing a file path instead of URL is :term:`soft deprecated`.
       Use :func:`guess_file_type` for this.
 
index 721f18e6af7498804011b180253433623d49abf5..8a0a5a0d74b7f131777951aa7525d64970d0a342 100644 (file)
@@ -4711,9 +4711,8 @@ written in Python, such as a mail server's external command delivery program.
       Use :class:`subprocess.Popen` or :func:`subprocess.run` to
       control options like encodings.
 
-   .. deprecated:: 3.14
-      The function is :term:`soft deprecated` and should no longer be used to
-      write new code. The :mod:`subprocess` module is recommended instead.
+   .. soft-deprecated:: 3.14
+      The :mod:`subprocess` module is recommended instead.
 
 
 .. function:: posix_spawn(path, argv, env, *, file_actions=None, \
@@ -4941,9 +4940,8 @@ written in Python, such as a mail server's external command delivery program.
    .. versionchanged:: 3.6
       Accepts a :term:`path-like object`.
 
-   .. deprecated:: 3.14
-      These functions are :term:`soft deprecated` and should no longer be used
-      to write new code. The :mod:`subprocess` module is recommended instead.
+   .. soft-deprecated:: 3.14
+      The :mod:`subprocess` module is recommended instead.
 
 
 .. data:: P_NOWAIT
index 8de5e7f78c66272d2331a418eb1720720903ef74..02dc51b3a76943add2ed0a0ca10d1def2ad7ef56 100644 (file)
@@ -2,8 +2,10 @@
 
 from __future__ import annotations
 
-from typing import TYPE_CHECKING
+import re
 
+from docutils import nodes
+from sphinx import addnodes
 from sphinx.domains.changeset import (
     VersionChange,
     versionlabel_classes,
@@ -11,6 +13,7 @@ from sphinx.domains.changeset import (
 )
 from sphinx.locale import _ as sphinx_gettext
 
+TYPE_CHECKING = False
 if TYPE_CHECKING:
     from docutils.nodes import Node
     from sphinx.application import Sphinx
@@ -73,6 +76,76 @@ class DeprecatedRemoved(VersionChange):
             versionlabel_classes[self.name] = ""
 
 
+class SoftDeprecated(PyVersionChange):
+    """Directive for soft deprecations that auto-links to the glossary term.
+
+    Usage::
+
+        .. soft-deprecated:: 3.15
+
+           Use :func:`new_thing` instead.
+
+    Renders as: "Soft deprecated since version 3.15: Use new_thing() instead."
+    with "Soft deprecated" linking to the glossary definition.
+    """
+
+    _TERM_RE = re.compile(r":term:`([^`]+)`")
+
+    def run(self) -> list[Node]:
+        versionlabels[self.name] = sphinx_gettext(
+            ":term:`Soft deprecated` since version %s"
+        )
+        versionlabel_classes[self.name] = "soft-deprecated"
+        try:
+            result = super().run()
+        finally:
+            versionlabels[self.name] = ""
+            versionlabel_classes[self.name] = ""
+
+        for node in result:
+            # Add "versionchanged" class so existing theme CSS applies
+            node["classes"] = node.get("classes", []) + ["versionchanged"]
+            # Replace the plain-text "Soft deprecated" with a glossary reference
+            for inline in node.findall(nodes.inline):
+                if "versionmodified" in inline.get("classes", []):
+                    self._add_glossary_link(inline)
+
+        return result
+
+    @classmethod
+    def _add_glossary_link(cls, inline: nodes.inline) -> None:
+        """Replace :term:`soft deprecated` text with a cross-reference to the
+        'Soft deprecated' glossary entry."""
+        for child in inline.children:
+            if not isinstance(child, nodes.Text):
+                continue
+
+            text = str(child)
+            match = cls._TERM_RE.search(text)
+            if match is None:
+                continue
+
+            ref = addnodes.pending_xref(
+                "",
+                nodes.Text(match.group(1)),
+                refdomain="std",
+                reftype="term",
+                reftarget="soft deprecated",
+                refwarn=True,
+            )
+
+            start, end = match.span()
+            new_nodes: list[nodes.Node] = []
+            if start > 0:
+                new_nodes.append(nodes.Text(text[:start]))
+            new_nodes.append(ref)
+            if end < len(text):
+                new_nodes.append(nodes.Text(text[end:]))
+
+            child.parent.replace(child, new_nodes)
+            break
+
+
 def setup(app: Sphinx) -> ExtensionMetadata:
     # Override Sphinx's directives with support for 'next'
     app.add_directive("versionadded", PyVersionChange, override=True)
@@ -83,6 +156,9 @@ def setup(app: Sphinx) -> ExtensionMetadata:
     # Register the ``.. deprecated-removed::`` directive
     app.add_directive("deprecated-removed", DeprecatedRemoved)
 
+    # Register the ``.. soft-deprecated::`` directive
+    app.add_directive("soft-deprecated", SoftDeprecated)
+
     return {
         "version": "1.0",
         "parallel_read_safe": True,
index 75f6607d8f3698dda76ecb9c1e8732c9f03289f7..699e518801cbcd75e7288f1760b928739d48fb9c 100644 (file)
@@ -29,6 +29,7 @@ In extensions/changes.py:
 
 {% trans %}Deprecated since version %s, will be removed in version %s{% endtrans %}
 {% trans %}Deprecated since version %s, removed in version %s{% endtrans %}
+{% trans %}:term:`Soft deprecated` since version %s{% endtrans %}
 
 In docsbuild-scripts, when rewriting indexsidebar.html with actual versions: