https://github.com/python/cpython/commit/99edd54fa7f2765cc4ad1d3988bbda5f4d726b08
commit: 99edd54fa7f2765cc4ad1d3988bbda5f4d726b08
branch: main
author: Petr Viktorin <[email protected]>
committer: encukou <[email protected]>
date: 2026-10-21T23:01:11+02:00
summary:
gh-156870: Add common Stable ABI notes for tables of definitions (GH-156871)
files:
M Doc/c-api/exceptions.rst
M Doc/c-api/iterator.rst
M Doc/tools/extensions/c_annotations.py
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 7f4e1ecc2aba26d..55b7e36b9b680e5 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -1130,125 +1130,192 @@ For completeness, here are all the variables:
Exception types
---------------
-.. list-table::
- :align: left
- :widths: auto
- :header-rows: 1
-
- * * C name
- * Python name
- * * .. c:var:: PyObject *PyExc_BaseException
- * :exc:`BaseException`
- * * .. c:var:: PyObject *PyExc_BaseExceptionGroup
- * :exc:`BaseExceptionGroup`
- * * .. c:var:: PyObject *PyExc_Exception
- * :exc:`Exception`
- * * .. c:var:: PyObject *PyExc_ArithmeticError
- * :exc:`ArithmeticError`
- * * .. c:var:: PyObject *PyExc_AssertionError
- * :exc:`AssertionError`
- * * .. c:var:: PyObject *PyExc_AttributeError
- * :exc:`AttributeError`
- * * .. c:var:: PyObject *PyExc_BlockingIOError
- * :exc:`BlockingIOError`
- * * .. c:var:: PyObject *PyExc_BrokenPipeError
- * :exc:`BrokenPipeError`
- * * .. c:var:: PyObject *PyExc_BufferError
- * :exc:`BufferError`
- * * .. c:var:: PyObject *PyExc_ChildProcessError
- * :exc:`ChildProcessError`
- * * .. c:var:: PyObject *PyExc_ConnectionAbortedError
- * :exc:`ConnectionAbortedError`
- * * .. c:var:: PyObject *PyExc_ConnectionError
- * :exc:`ConnectionError`
- * * .. c:var:: PyObject *PyExc_ConnectionRefusedError
- * :exc:`ConnectionRefusedError`
- * * .. c:var:: PyObject *PyExc_ConnectionResetError
- * :exc:`ConnectionResetError`
- * * .. c:var:: PyObject *PyExc_EOFError
- * :exc:`EOFError`
- * * .. c:var:: PyObject *PyExc_FileExistsError
- * :exc:`FileExistsError`
- * * .. c:var:: PyObject *PyExc_FileNotFoundError
- * :exc:`FileNotFoundError`
- * * .. c:var:: PyObject *PyExc_FloatingPointError
- * :exc:`FloatingPointError`
- * * .. c:var:: PyObject *PyExc_GeneratorExit
- * :exc:`GeneratorExit`
- * * .. c:var:: PyObject *PyExc_ImportCycleError
- * :exc:`ImportCycleError`
- * * .. c:var:: PyObject *PyExc_ImportError
- * :exc:`ImportError`
- * * .. c:var:: PyObject *PyExc_IndentationError
- * :exc:`IndentationError`
- * * .. c:var:: PyObject *PyExc_IndexError
- * :exc:`IndexError`
- * * .. c:var:: PyObject *PyExc_InterruptedError
- * :exc:`InterruptedError`
- * * .. c:var:: PyObject *PyExc_IsADirectoryError
- * :exc:`IsADirectoryError`
- * * .. c:var:: PyObject *PyExc_KeyError
- * :exc:`KeyError`
- * * .. c:var:: PyObject *PyExc_KeyboardInterrupt
- * :exc:`KeyboardInterrupt`
- * * .. c:var:: PyObject *PyExc_LookupError
- * :exc:`LookupError`
- * * .. c:var:: PyObject *PyExc_MemoryError
- * :exc:`MemoryError`
- * * .. c:var:: PyObject *PyExc_ModuleNotFoundError
- * :exc:`ModuleNotFoundError`
- * * .. c:var:: PyObject *PyExc_NameError
- * :exc:`NameError`
- * * .. c:var:: PyObject *PyExc_NotADirectoryError
- * :exc:`NotADirectoryError`
- * * .. c:var:: PyObject *PyExc_NotImplementedError
- * :exc:`NotImplementedError`
- * * .. c:var:: PyObject *PyExc_OSError
- * :exc:`OSError`
- * * .. c:var:: PyObject *PyExc_OverflowError
- * :exc:`OverflowError`
- * * .. c:var:: PyObject *PyExc_PermissionError
- * :exc:`PermissionError`
- * * .. c:var:: PyObject *PyExc_ProcessLookupError
- * :exc:`ProcessLookupError`
- * * .. c:var:: PyObject *PyExc_PythonFinalizationError
- * :exc:`PythonFinalizationError`
- * * .. c:var:: PyObject *PyExc_RecursionError
- * :exc:`RecursionError`
- * * .. c:var:: PyObject *PyExc_ReferenceError
- * :exc:`ReferenceError`
- * * .. c:var:: PyObject *PyExc_RuntimeError
- * :exc:`RuntimeError`
- * * .. c:var:: PyObject *PyExc_StopAsyncIteration
- * :exc:`StopAsyncIteration`
- * * .. c:var:: PyObject *PyExc_StopIteration
- * :exc:`StopIteration`
- * * .. c:var:: PyObject *PyExc_SyntaxError
- * :exc:`SyntaxError`
- * * .. c:var:: PyObject *PyExc_SystemError
- * :exc:`SystemError`
- * * .. c:var:: PyObject *PyExc_SystemExit
- * :exc:`SystemExit`
- * * .. c:var:: PyObject *PyExc_TabError
- * :exc:`TabError`
- * * .. c:var:: PyObject *PyExc_TimeoutError
- * :exc:`TimeoutError`
- * * .. c:var:: PyObject *PyExc_TypeError
- * :exc:`TypeError`
- * * .. c:var:: PyObject *PyExc_UnboundLocalError
- * :exc:`UnboundLocalError`
- * * .. c:var:: PyObject *PyExc_UnicodeDecodeError
- * :exc:`UnicodeDecodeError`
- * * .. c:var:: PyObject *PyExc_UnicodeEncodeError
- * :exc:`UnicodeEncodeError`
- * * .. c:var:: PyObject *PyExc_UnicodeError
- * :exc:`UnicodeError`
- * * .. c:var:: PyObject *PyExc_UnicodeTranslateError
- * :exc:`UnicodeTranslateError`
- * * .. c:var:: PyObject *PyExc_ValueError
- * :exc:`ValueError`
- * * .. c:var:: PyObject *PyExc_ZeroDivisionError
- * :exc:`ZeroDivisionError`
+.. stable-abi-note::
+
+ These pointers are part of the :ref:`Stable ABI <stable>` since the
+ version in the *S.ABI* column (or since 3.2 when *S.ABI* is empty).
+ Exceptions marked N/A are *not* part of the Stable ABI.
+
+.. omit-stable-abi-notes::
+
+ .. list-table::
+ :align: left
+ :widths: auto
+ :header-rows: 1
+
+ * * C name
+ * .. stable-abi-note::
+
+ S.ABI
+ * Python name
+ * * .. c:var:: PyObject *PyExc_BaseException
+ *
+ * :exc:`BaseException`
+ * * .. c:var:: PyObject *PyExc_BaseExceptionGroup
+ * 3.11
+ * :exc:`BaseExceptionGroup`
+ * * .. c:var:: PyObject *PyExc_Exception
+ *
+ * :exc:`Exception`
+ * * .. c:var:: PyObject *PyExc_ArithmeticError
+ *
+ * :exc:`ArithmeticError`
+ * * .. c:var:: PyObject *PyExc_AssertionError
+ *
+ * :exc:`AssertionError`
+ * * .. c:var:: PyObject *PyExc_AttributeError
+ *
+ * :exc:`AttributeError`
+ * * .. c:var:: PyObject *PyExc_BlockingIOError
+ * 3.7
+ * :exc:`BlockingIOError`
+ * * .. c:var:: PyObject *PyExc_BrokenPipeError
+ * 3.7
+ * :exc:`BrokenPipeError`
+ * * .. c:var:: PyObject *PyExc_BufferError
+ *
+ * :exc:`BufferError`
+ * * .. c:var:: PyObject *PyExc_ChildProcessError
+ * 3.7
+ * :exc:`ChildProcessError`
+ * * .. c:var:: PyObject *PyExc_ConnectionAbortedError
+ * 3.7
+ * :exc:`ConnectionAbortedError`
+ * * .. c:var:: PyObject *PyExc_ConnectionError
+ * 3.7
+ * :exc:`ConnectionError`
+ * * .. c:var:: PyObject *PyExc_ConnectionRefusedError
+ * 3.7
+ * :exc:`ConnectionRefusedError`
+ * * .. c:var:: PyObject *PyExc_ConnectionResetError
+ * 3.7
+ * :exc:`ConnectionResetError`
+ * * .. c:var:: PyObject *PyExc_EOFError
+ *
+ * :exc:`EOFError`
+ * * .. c:var:: PyObject *PyExc_FileExistsError
+ * 3.7
+ * :exc:`FileExistsError`
+ * * .. c:var:: PyObject *PyExc_FileNotFoundError
+ * 3.7
+ * :exc:`FileNotFoundError`
+ * * .. c:var:: PyObject *PyExc_FloatingPointError
+ *
+ * :exc:`FloatingPointError`
+ * * .. c:var:: PyObject *PyExc_GeneratorExit
+ *
+ * :exc:`GeneratorExit`
+ * * .. c:var:: PyObject *PyExc_ImportCycleError
+ * N/A
+ * :exc:`ImportCycleError`
+ * * .. c:var:: PyObject *PyExc_ImportError
+ *
+ * :exc:`ImportError`
+ * * .. c:var:: PyObject *PyExc_IndentationError
+ *
+ * :exc:`IndentationError`
+ * * .. c:var:: PyObject *PyExc_IndexError
+ *
+ * :exc:`IndexError`
+ * * .. c:var:: PyObject *PyExc_InterruptedError
+ * 3.7
+ * :exc:`InterruptedError`
+ * * .. c:var:: PyObject *PyExc_IsADirectoryError
+ * 3.7
+ * :exc:`IsADirectoryError`
+ * * .. c:var:: PyObject *PyExc_KeyError
+ *
+ * :exc:`KeyError`
+ * * .. c:var:: PyObject *PyExc_KeyboardInterrupt
+ *
+ * :exc:`KeyboardInterrupt`
+ * * .. c:var:: PyObject *PyExc_LookupError
+ *
+ * :exc:`LookupError`
+ * * .. c:var:: PyObject *PyExc_MemoryError
+ *
+ * :exc:`MemoryError`
+ * * .. c:var:: PyObject *PyExc_ModuleNotFoundError
+ * 3.6
+ * :exc:`ModuleNotFoundError`
+ * * .. c:var:: PyObject *PyExc_NameError
+ *
+ * :exc:`NameError`
+ * * .. c:var:: PyObject *PyExc_NotADirectoryError
+ * 3.7
+ * :exc:`NotADirectoryError`
+ * * .. c:var:: PyObject *PyExc_NotImplementedError
+ *
+ * :exc:`NotImplementedError`
+ * * .. c:var:: PyObject *PyExc_OSError
+ *
+ * :exc:`OSError`
+ * * .. c:var:: PyObject *PyExc_OverflowError
+ *
+ * :exc:`OverflowError`
+ * * .. c:var:: PyObject *PyExc_PermissionError
+ * 3.7
+ * :exc:`PermissionError`
+ * * .. c:var:: PyObject *PyExc_ProcessLookupError
+ * 3.7
+ * :exc:`ProcessLookupError`
+ * * .. c:var:: PyObject *PyExc_PythonFinalizationError
+ *
+ * :exc:`PythonFinalizationError`
+ * * .. c:var:: PyObject *PyExc_RecursionError
+ * 3.7
+ * :exc:`RecursionError`
+ * * .. c:var:: PyObject *PyExc_ReferenceError
+ *
+ * :exc:`ReferenceError`
+ * * .. c:var:: PyObject *PyExc_RuntimeError
+ *
+ * :exc:`RuntimeError`
+ * * .. c:var:: PyObject *PyExc_StopAsyncIteration
+ * 3.7
+ * :exc:`StopAsyncIteration`
+ * * .. c:var:: PyObject *PyExc_StopIteration
+ *
+ * :exc:`StopIteration`
+ * * .. c:var:: PyObject *PyExc_SyntaxError
+ *
+ * :exc:`SyntaxError`
+ * * .. c:var:: PyObject *PyExc_SystemError
+ *
+ * :exc:`SystemError`
+ * * .. c:var:: PyObject *PyExc_SystemExit
+ *
+ * :exc:`SystemExit`
+ * * .. c:var:: PyObject *PyExc_TabError
+ *
+ * :exc:`TabError`
+ * * .. c:var:: PyObject *PyExc_TimeoutError
+ * 3.7
+ * :exc:`TimeoutError`
+ * * .. c:var:: PyObject *PyExc_TypeError
+ *
+ * :exc:`TypeError`
+ * * .. c:var:: PyObject *PyExc_UnboundLocalError
+ *
+ * :exc:`UnboundLocalError`
+ * * .. c:var:: PyObject *PyExc_UnicodeDecodeError
+ *
+ * :exc:`UnicodeDecodeError`
+ * * .. c:var:: PyObject *PyExc_UnicodeEncodeError
+ *
+ * :exc:`UnicodeEncodeError`
+ * * .. c:var:: PyObject *PyExc_UnicodeError
+ *
+ * :exc:`UnicodeError`
+ * * .. c:var:: PyObject *PyExc_UnicodeTranslateError
+ *
+ * :exc:`UnicodeTranslateError`
+ * * .. c:var:: PyObject *PyExc_ValueError
+ *
+ * :exc:`ValueError`
+ * * .. c:var:: PyObject *PyExc_ZeroDivisionError
+ *
+ * :exc:`ZeroDivisionError`
.. versionadded:: 3.3
:c:data:`PyExc_BlockingIOError`, :c:data:`PyExc_BrokenPipeError`,
@@ -1308,37 +1375,59 @@ Notes:
Warning types
-------------
-.. list-table::
- :align: left
- :widths: auto
- :header-rows: 1
-
- * * C name
- * Python name
- * * .. c:var:: PyObject *PyExc_Warning
- * :exc:`Warning`
- * * .. c:var:: PyObject *PyExc_BytesWarning
- * :exc:`BytesWarning`
- * * .. c:var:: PyObject *PyExc_DeprecationWarning
- * :exc:`DeprecationWarning`
- * * .. c:var:: PyObject *PyExc_EncodingWarning
- * :exc:`EncodingWarning`
- * * .. c:var:: PyObject *PyExc_FutureWarning
- * :exc:`FutureWarning`
- * * .. c:var:: PyObject *PyExc_ImportWarning
- * :exc:`ImportWarning`
- * * .. c:var:: PyObject *PyExc_PendingDeprecationWarning
- * :exc:`PendingDeprecationWarning`
- * * .. c:var:: PyObject *PyExc_ResourceWarning
- * :exc:`ResourceWarning`
- * * .. c:var:: PyObject *PyExc_RuntimeWarning
- * :exc:`RuntimeWarning`
- * * .. c:var:: PyObject *PyExc_SyntaxWarning
- * :exc:`SyntaxWarning`
- * * .. c:var:: PyObject *PyExc_UnicodeWarning
- * :exc:`UnicodeWarning`
- * * .. c:var:: PyObject *PyExc_UserWarning
- * :exc:`UserWarning`
+.. stable-abi-note::
+
+ These pointers are part of the :ref:`Stable ABI <stable>` since the
+ version in the *S.ABI* column (or since 3.2 when *S.ABI* is empty).
+
+.. omit-stable-abi-notes::
+
+ .. list-table::
+ :align: left
+ :widths: auto
+ :header-rows: 1
+
+ * * C name
+ * .. stable-abi-note::
+
+ S.ABI
+ * Python name
+ * * .. c:var:: PyObject *PyExc_Warning
+ *
+ * :exc:`Warning`
+ * * .. c:var:: PyObject *PyExc_BytesWarning
+ *
+ * :exc:`BytesWarning`
+ * * .. c:var:: PyObject *PyExc_DeprecationWarning
+ *
+ * :exc:`DeprecationWarning`
+ * * .. c:var:: PyObject *PyExc_EncodingWarning
+ * 3.10
+ * :exc:`EncodingWarning`
+ * * .. c:var:: PyObject *PyExc_FutureWarning
+ *
+ * :exc:`FutureWarning`
+ * * .. c:var:: PyObject *PyExc_ImportWarning
+ *
+ * :exc:`ImportWarning`
+ * * .. c:var:: PyObject *PyExc_PendingDeprecationWarning
+ *
+ * :exc:`PendingDeprecationWarning`
+ * * .. c:var:: PyObject *PyExc_ResourceWarning
+ * 3.7
+ * :exc:`ResourceWarning`
+ * * .. c:var:: PyObject *PyExc_RuntimeWarning
+ *
+ * :exc:`RuntimeWarning`
+ * * .. c:var:: PyObject *PyExc_SyntaxWarning
+ *
+ * :exc:`SyntaxWarning`
+ * * .. c:var:: PyObject *PyExc_UnicodeWarning
+ *
+ * :exc:`UnicodeWarning`
+ * * .. c:var:: PyObject *PyExc_UserWarning
+ *
+ * :exc:`UserWarning`
.. versionadded:: 3.2
:c:data:`PyExc_ResourceWarning`.
diff --git a/Doc/c-api/iterator.rst b/Doc/c-api/iterator.rst
index bfbfe3c92799809..2276620cc303141 100644
--- a/Doc/c-api/iterator.rst
+++ b/Doc/c-api/iterator.rst
@@ -72,23 +72,28 @@ Builtin Iterator Types
These are built-in iteration types that are included in Python's C API, but
provide no additional functions. They are here for completeness.
-
-.. list-table::
- :widths: auto
- :header-rows: 1
-
- * * C type
- * Python type
- * * .. c:var:: PyTypeObject PyEnum_Type
- * :py:class:`enumerate`
- * * .. c:var:: PyTypeObject PyFilter_Type
- * :py:class:`filter`
- * * .. c:var:: PyTypeObject PyMap_Type
- * :py:class:`map`
- * * .. c:var:: PyTypeObject PyReversed_Type
- * :py:class:`reversed`
- * * .. c:var:: PyTypeObject PyZip_Type
- * :py:class:`zip`
+.. stable-abi-note::
+
+ These pointers are part of the :ref:`Stable ABI <stable>`.
+
+.. omit-stable-abi-notes::
+
+ .. list-table::
+ :widths: auto
+ :header-rows: 1
+
+ * * C type
+ * Python type
+ * * .. c:var:: PyTypeObject PyEnum_Type
+ * :py:class:`enumerate`
+ * * .. c:var:: PyTypeObject PyFilter_Type
+ * :py:class:`filter`
+ * * .. c:var:: PyTypeObject PyMap_Type
+ * :py:class:`map`
+ * * .. c:var:: PyTypeObject PyReversed_Type
+ * :py:class:`reversed`
+ * * .. c:var:: PyTypeObject PyZip_Type
+ * :py:class:`zip`
Other Iterator Objects
diff --git a/Doc/tools/extensions/c_annotations.py
b/Doc/tools/extensions/c_annotations.py
index fbbf01fa7da02bd..7a7badb31c96014 100644
--- a/Doc/tools/extensions/c_annotations.py
+++ b/Doc/tools/extensions/c_annotations.py
@@ -197,8 +197,21 @@ def add_annotations(app: Sphinx, doctree: nodes.document)
-> None:
f"{objtype!r} not in {record_object_types!r}"
)
raise ValueError(msg)
- annotation = _stable_abi_annotation(record)
- node.insert(0, annotation)
+
+ # Skip the note if any ancestor has 'omit-stable-abi-notes'
+ # in a 'c_annotations' attribute.
+ ancestor = node
+ while ancestor:
+ if 'omit-stable-abi-notes' in ancestor.get(
+ 'c_annotations',
+ [],
+ ):
+ break
+ ancestor = ancestor.parent
+ else:
+ # no skip; add the annotation
+ annotation = _stable_abi_annotation(record)
+ node.insert(0, annotation)
# Unstable API annotation.
if name.startswith("PyUnstable"):
@@ -463,6 +476,43 @@ def run(self) -> list[nodes.Node]:
return [node]
+class StableABINote(SphinxDirective):
+ """A manual Stable ABI note
+
+ Normally, notes are auto-generated.
+ This directive allows adding a similarly styled note manually.
+ Pair with omit-stable-abi-notes to override the automatic generation.
+ """
+
+ has_content = True
+
+ def run(self) -> list[nodes.Node]:
+ self.assert_has_content()
+ nodes = self.parse_content_to_nodes()
+ for node in nodes:
+ node.setdefault("classes", []).append('stableabi')
+ return nodes
+
+
+class OmitStableABINotes(SphinxDirective):
+ """A block where automatic Stable ABI notes are not generated
+
+ Normally used for dense lists/tables of definitions, where individual notes
+ are omitted and the whole block has a common manual note.
+ """
+
+ has_content = True
+
+ def run(self) -> list[nodes.Node]:
+ self.assert_has_content()
+ nodes = self.parse_content_to_nodes()
+ for node in nodes:
+ node.setdefault("c_annotations", []).append(
+ 'omit-stable-abi-notes',
+ )
+ return nodes
+
+
def init_annotations(app: Sphinx) -> None:
# Using domaindata is a bit hack-ish,
# but allows storing state without a global variable or closure.
@@ -485,6 +535,8 @@ def setup(app: Sphinx) -> ExtensionMetadata:
app.add_directive("limited-api-list", LimitedAPIList)
app.add_directive("version-hex-cheatsheet", VersionHexCheatsheet)
app.add_directive("corresponding-type-slot", CorrespondingTypeSlot)
+ app.add_directive("omit-stable-abi-notes", OmitStableABINotes)
+ app.add_directive("stable-abi-note", StableABINote)
app.connect("builder-inited", init_annotations)
app.connect("doctree-read", add_annotations)
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]