Skip to content

Commit 15cb0b9

Browse files
[3.14] Add a new Sphinx soft-deprecated directive (GH-148630)
(cherry picked from commit e9bbf86) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Stan Ulbrych <stan@python.org>
1 parent 67100b3 commit 15cb0b9

File tree

11 files changed

+107
-44
lines changed

11 files changed

+107
-44
lines changed

Doc/c-api/allocation.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
.. _allocating-objects:
44

5-
Allocating Objects on the Heap
5+
Allocating objects on the heap
66
==============================
77

88

@@ -153,18 +153,20 @@ Allocating Objects on the Heap
153153
To allocate and create extension modules.
154154

155155

156-
Deprecated aliases
157-
^^^^^^^^^^^^^^^^^^
156+
Soft-deprecated aliases
157+
^^^^^^^^^^^^^^^^^^^^^^^
158158

159-
These are :term:`soft deprecated` aliases to existing functions and macros.
159+
.. soft-deprecated:: 3.15
160+
161+
These are aliases to existing functions and macros.
160162
They exist solely for backwards compatibility.
161163

162164

163165
.. list-table::
164166
:widths: auto
165167
:header-rows: 1
166168

167-
* * Deprecated alias
169+
* * Soft-deprecated alias
168170
* Function
169171
* * .. c:macro:: PyObject_NEW(type, typeobj)
170172
* :c:macro:`PyObject_New`

Doc/c-api/file.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
.. _fileobjects:
44

5-
File Objects
5+
File objects
66
------------
77

88
.. index:: pair: object; file
@@ -136,11 +136,12 @@ the :mod:`io` APIs instead.
136136
failure; the appropriate exception will be set.
137137
138138
139-
Deprecated API
140-
^^^^^^^^^^^^^^
139+
Soft-deprecated API
140+
^^^^^^^^^^^^^^^^^^^
141141
142+
.. soft-deprecated:: 3.15
142143
143-
These are :term:`soft deprecated` APIs that were included in Python's C API
144+
These are APIs that were included in Python's C API
144145
by mistake. They are documented solely for completeness; use other
145146
``PyFile*`` APIs instead.
146147

Doc/c-api/frame.rst

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.. highlight:: c
22

3-
Frame Objects
3+
Frame objects
44
-------------
55

66
.. c:type:: PyFrameObject
@@ -147,7 +147,7 @@ See also :ref:`Reflection <reflection>`.
147147
Return the line number that *frame* is currently executing.
148148
149149
150-
Frame Locals Proxies
150+
Frame locals proxies
151151
^^^^^^^^^^^^^^^^^^^^
152152
153153
.. versionadded:: 3.13
@@ -169,7 +169,7 @@ See :pep:`667` for more information.
169169
Return non-zero if *obj* is a frame :func:`locals` proxy.
170170
171171
172-
Legacy Local Variable APIs
172+
Legacy local variable APIs
173173
^^^^^^^^^^^^^^^^^^^^^^^^^^
174174
175175
These APIs are :term:`soft deprecated`. As of Python 3.13, they do nothing.
@@ -178,48 +178,42 @@ They exist solely for backwards compatibility.
178178
179179
.. c:function:: void PyFrame_LocalsToFast(PyFrameObject *f, int clear)
180180
181-
This function is :term:`soft deprecated` and does nothing.
182-
183181
Prior to Python 3.13, this function would copy the :attr:`~frame.f_locals`
184182
attribute of *f* to the internal "fast" array of local variables, allowing
185183
changes in frame objects to be visible to the interpreter. If *clear* was
186184
true, this function would process variables that were unset in the locals
187185
dictionary.
188186
189-
.. versionchanged:: 3.13
187+
.. soft-deprecated:: 3.13
190188
This function now does nothing.
191189
192190
193191
.. c:function:: void PyFrame_FastToLocals(PyFrameObject *f)
194192
195-
This function is :term:`soft deprecated` and does nothing.
196-
197193
Prior to Python 3.13, this function would copy the internal "fast" array
198194
of local variables (which is used by the interpreter) to the
199195
:attr:`~frame.f_locals` attribute of *f*, allowing changes in local
200196
variables to be visible to frame objects.
201197
202-
.. versionchanged:: 3.13
198+
.. soft-deprecated:: 3.13
203199
This function now does nothing.
204200
205201
206202
.. c:function:: int PyFrame_FastToLocalsWithError(PyFrameObject *f)
207203
208-
This function is :term:`soft deprecated` and does nothing.
209-
210204
Prior to Python 3.13, this function was similar to
211205
:c:func:`PyFrame_FastToLocals`, but would return ``0`` on success, and
212206
``-1`` with an exception set on failure.
213207
214-
.. versionchanged:: 3.13
208+
.. soft-deprecated:: 3.13
215209
This function now does nothing.
216210
217211
218212
.. seealso::
219213
:pep:`667`
220214
221215
222-
Internal Frames
216+
Internal frames
223217
^^^^^^^^^^^^^^^
224218
225219
Unless using :pep:`523`, you will not need this.
@@ -249,5 +243,3 @@ Unless using :pep:`523`, you will not need this.
249243
Return the currently executing line number, or -1 if there is no line number.
250244
251245
.. versionadded:: 3.12
252-
253-

Doc/c-api/long.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,10 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
197197
198198
.. c:function:: long PyLong_AS_LONG(PyObject *obj)
199199
200-
A :term:`soft deprecated` alias.
201200
Exactly equivalent to the preferred ``PyLong_AsLong``. In particular,
202201
it can fail with :exc:`OverflowError` or another exception.
203202
204-
.. deprecated:: 3.14
205-
The function is soft deprecated.
203+
.. soft-deprecated:: 3.14
206204
207205
.. c:function:: int PyLong_AsInt(PyObject *obj)
208206

Doc/c-api/module.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -604,9 +604,7 @@ or code that creates modules dynamically.
604604
// PyModule_AddObject() stole a reference to obj:
605605
// Py_XDECREF(obj) is not needed here.
606606
607-
.. deprecated:: 3.13
608-
609-
:c:func:`PyModule_AddObject` is :term:`soft deprecated`.
607+
.. soft-deprecated:: 3.13
610608
611609
612610
.. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)

Doc/c-api/monitoring.rst

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,4 @@ would typically correspond to a Python function.
205205
206206
.. versionadded:: 3.13
207207
208-
.. deprecated:: 3.14
209-
210-
This function is :term:`soft deprecated`.
208+
.. soft-deprecated:: 3.14

Doc/c-api/sequence.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ Sequence Protocol
109109
110110
Alias for :c:func:`PySequence_Contains`.
111111
112-
.. deprecated:: 3.14
113-
The function is :term:`soft deprecated` and should no longer be used to
114-
write new code.
112+
.. soft-deprecated:: 3.14
113+
The function should no longer be used to write new code.
115114
116115
117116
.. c:function:: Py_ssize_t PySequence_Index(PyObject *o, PyObject *value)

Doc/library/mimetypes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ the information :func:`init` sets up.
5656
.. versionchanged:: 3.8
5757
Added support for *url* being a :term:`path-like object`.
5858

59-
.. deprecated:: 3.13
59+
.. soft-deprecated:: 3.13
6060
Passing a file path instead of URL is :term:`soft deprecated`.
6161
Use :func:`guess_file_type` for this.
6262

Doc/library/os.rst

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4711,9 +4711,8 @@ written in Python, such as a mail server's external command delivery program.
47114711
Use :class:`subprocess.Popen` or :func:`subprocess.run` to
47124712
control options like encodings.
47134713

4714-
.. deprecated:: 3.14
4715-
The function is :term:`soft deprecated` and should no longer be used to
4716-
write new code. The :mod:`subprocess` module is recommended instead.
4714+
.. soft-deprecated:: 3.14
4715+
The :mod:`subprocess` module is recommended instead.
47174716

47184717

47194718
.. 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.
49414940
.. versionchanged:: 3.6
49424941
Accepts a :term:`path-like object`.
49434942

4944-
.. deprecated:: 3.14
4945-
These functions are :term:`soft deprecated` and should no longer be used
4946-
to write new code. The :mod:`subprocess` module is recommended instead.
4943+
.. soft-deprecated:: 3.14
4944+
The :mod:`subprocess` module is recommended instead.
49474945

49484946

49494947
.. data:: P_NOWAIT

Doc/tools/extensions/changes.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
from __future__ import annotations
44

5-
from typing import TYPE_CHECKING
5+
import re
66

7+
from docutils import nodes
8+
from sphinx import addnodes
79
from sphinx.domains.changeset import (
810
VersionChange,
911
versionlabel_classes,
1012
versionlabels,
1113
)
1214
from sphinx.locale import _ as sphinx_gettext
1315

16+
TYPE_CHECKING = False
1417
if TYPE_CHECKING:
1518
from docutils.nodes import Node
1619
from sphinx.application import Sphinx
@@ -73,6 +76,76 @@ def run(self) -> list[Node]:
7376
versionlabel_classes[self.name] = ""
7477

7578

79+
class SoftDeprecated(PyVersionChange):
80+
"""Directive for soft deprecations that auto-links to the glossary term.
81+
82+
Usage::
83+
84+
.. soft-deprecated:: 3.15
85+
86+
Use :func:`new_thing` instead.
87+
88+
Renders as: "Soft deprecated since version 3.15: Use new_thing() instead."
89+
with "Soft deprecated" linking to the glossary definition.
90+
"""
91+
92+
_TERM_RE = re.compile(r":term:`([^`]+)`")
93+
94+
def run(self) -> list[Node]:
95+
versionlabels[self.name] = sphinx_gettext(
96+
":term:`Soft deprecated` since version %s"
97+
)
98+
versionlabel_classes[self.name] = "soft-deprecated"
99+
try:
100+
result = super().run()
101+
finally:
102+
versionlabels[self.name] = ""
103+
versionlabel_classes[self.name] = ""
104+
105+
for node in result:
106+
# Add "versionchanged" class so existing theme CSS applies
107+
node["classes"] = node.get("classes", []) + ["versionchanged"]
108+
# Replace the plain-text "Soft deprecated" with a glossary reference
109+
for inline in node.findall(nodes.inline):
110+
if "versionmodified" in inline.get("classes", []):
111+
self._add_glossary_link(inline)
112+
113+
return result
114+
115+
@classmethod
116+
def _add_glossary_link(cls, inline: nodes.inline) -> None:
117+
"""Replace :term:`soft deprecated` text with a cross-reference to the
118+
'Soft deprecated' glossary entry."""
119+
for child in inline.children:
120+
if not isinstance(child, nodes.Text):
121+
continue
122+
123+
text = str(child)
124+
match = cls._TERM_RE.search(text)
125+
if match is None:
126+
continue
127+
128+
ref = addnodes.pending_xref(
129+
"",
130+
nodes.Text(match.group(1)),
131+
refdomain="std",
132+
reftype="term",
133+
reftarget="soft deprecated",
134+
refwarn=True,
135+
)
136+
137+
start, end = match.span()
138+
new_nodes: list[nodes.Node] = []
139+
if start > 0:
140+
new_nodes.append(nodes.Text(text[:start]))
141+
new_nodes.append(ref)
142+
if end < len(text):
143+
new_nodes.append(nodes.Text(text[end:]))
144+
145+
child.parent.replace(child, new_nodes)
146+
break
147+
148+
76149
def setup(app: Sphinx) -> ExtensionMetadata:
77150
# Override Sphinx's directives with support for 'next'
78151
app.add_directive("versionadded", PyVersionChange, override=True)
@@ -83,6 +156,9 @@ def setup(app: Sphinx) -> ExtensionMetadata:
83156
# Register the ``.. deprecated-removed::`` directive
84157
app.add_directive("deprecated-removed", DeprecatedRemoved)
85158

159+
# Register the ``.. soft-deprecated::`` directive
160+
app.add_directive("soft-deprecated", SoftDeprecated)
161+
86162
return {
87163
"version": "1.0",
88164
"parallel_read_safe": True,

0 commit comments

Comments
 (0)