Skip to content

Commit 17cd307

Browse files
authored
PEP 743: Add Py_COMPAT_API_VERSION to the Python C API (#3715)
1 parent e749257 commit 17cd307

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed

.github/CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ peps/pep-0738.rst @encukou
621621
peps/pep-0740.rst @dstufft
622622
peps/pep-0741.rst @vstinner
623623
peps/pep-0742.rst @JelleZijlstra
624+
peps/pep-0743.rst @vstinner
624625
# ...
625626
# peps/pep-0754.rst
626627
# ...

peps/pep-0743.rst

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
PEP: 743
2+
Title: Add Py_COMPAT_API_VERSION to the Python C API
3+
Author: Victor Stinner <[email protected]>
4+
Status: Draft
5+
Type: Standards Track
6+
Created: 11-Mar-2024
7+
Python-Version: 3.13
8+
9+
.. highlight:: c
10+
11+
12+
Abstract
13+
========
14+
15+
Add ``Py_COMPAT_API_VERSION`` and ``Py_COMPAT_API_VERSION_MAX`` macros
16+
to opt-in for planned incompatible C API changes in a C extension.
17+
Maintainers can decide when they make their C extension compatible
18+
and also decide which future Python version they want to be compatible
19+
with.
20+
21+
22+
Rationale
23+
=========
24+
25+
Python releases enforce C API changes
26+
-------------------------------------
27+
28+
Every Python 3.x release has a long list of C API changes, including
29+
incompatible changes. C extensions have to be updated to work on the
30+
newly released Python.
31+
32+
Some incompatible changes are driven by new features: they cannot be
33+
avoided, unless we decide to not add these features. Other reasons:
34+
35+
* Remove deprecated API (see :pep:`387`).
36+
* Ease the implementation of another change.
37+
* Change or remove error-prone API.
38+
39+
Currently, there is no middle ground between "not change the C API" and
40+
"incompatible C API changes impact everybody". Either a C extension is
41+
updated or the new Python version cannot be used. Such all-or-nothing
42+
deal does not satisfy C extension maintainers nor C extensions users.
43+
44+
45+
Limited C API
46+
-------------
47+
48+
The limited C API is versioned: the ``Py_LIMITED_API`` macro can be set
49+
to a Python version to select which API is available. On the Python
50+
side, it allows introducing incompatible changes at a specific
51+
``Py_LIMITED_API`` version. For example, if ``Py_LIMITED_API`` is set to
52+
Python 3.11 or newer, the ``<stdio.h>`` is no longer included by
53+
``Python.h``, whereas C extensions targeting Python 3.10 are not
54+
affected.
55+
56+
The difference here is that upgrading Python does not change if
57+
``<stdio.h>`` is included or not, but updating ``Py_LIMITED_API`` does.
58+
Updating ``Py_LIMITED_API`` is an deliberate action made by the C
59+
extension maintainer. It gives more freedom to decide **when** the
60+
maintainer is ready to deal with the latest batch of incompatible
61+
changes.
62+
63+
A similar version can be used with the regular (non-limited) C API.
64+
65+
66+
Deprecation and compiler warnings
67+
---------------------------------
68+
69+
Deprecated functions are marked with ``Py_DEPRECATED()``. Using a
70+
deprecated function emits a compiler warning.
71+
72+
The problem is that ``pip`` and ``build`` tools hide compiler logs by
73+
default, unless a build fails. Moreover, it's easy to miss a single
74+
warning in the middle of hundred lines of logs.
75+
76+
Schedule changes
77+
----------------
78+
79+
Currently, there is no way to schedule a C API change: announce it but
80+
also provide a way to maintainers to test their C extensions with the
81+
change. Either a change is not made, or everybody must update their code
82+
if they want to update Python.
83+
84+
85+
Specification
86+
=============
87+
88+
New macros
89+
----------
90+
91+
Add new ``Py_COMPAT_API_VERSION`` and ``Py_COMPAT_API_VERSION_MAX``
92+
macros. They can be set to test if a C extension is prepared for future
93+
C API changes: compatible with future Python versions.
94+
95+
The ``Py_COMPAT_API_VERSION`` macro can be set to a specific Python
96+
version. For example, ``Py_COMPAT_API_VERSION=0x030e0000`` tests C API
97+
changes scheduled in Python 3.14.
98+
99+
If the ``Py_COMPAT_API_VERSION`` macro is set to
100+
``Py_COMPAT_API_VERSION_MAX``, all scheduled C API changes are tested at
101+
once.
102+
103+
If the ``Py_COMPAT_API_VERSION`` macro is not set, it is to
104+
``PY_VERSION_HEX`` by default.
105+
106+
The ``Py_COMPAT_API_VERSION`` macro can be set in a single C file or for
107+
a whole project in compiler flags. The macro does not affected other
108+
projects or Python itself.
109+
110+
111+
Example in Python
112+
-----------------
113+
114+
For example, the ``PyImport_ImportModuleNoBlock()`` function is
115+
deprecated in Python 3.13 and scheduled for removal in Python 3.15. The
116+
function can be declared in the Python C API with the following
117+
declaration:
118+
119+
.. code-block:: c
120+
121+
#if Py_COMPAT_API_VERSION < 0x030f0000
122+
Py_DEPRECATED(3.13) PyAPI_FUNC(PyObject *) PyImport_ImportModuleNoBlock(
123+
const char *name /* UTF-8 encoded string */
124+
);
125+
#endif
126+
127+
If ``if Py_COMPAT_API_VERSION`` is equal to or greater than Python 3.15
128+
(``0x030f0000``), the ``PyImport_ImportModuleNoBlock()`` function is not
129+
declared, and so using it fails with a build error.
130+
131+
Goals
132+
-----
133+
134+
* Reduce the number of C API changes affecting C extensions when
135+
updating Python.
136+
* When testing C extensions (for example, optional CI test),
137+
``Py_COMPAT_API_VERSION`` can be set to ``Py_COMPAT_API_VERSION_MAX``
138+
to detect future incompatibilities. For mandatory tests, it is
139+
recommended to set ``Py_COMPAT_API_VERSION`` to a specific Python
140+
version.
141+
* For core developers, make sure that the C API can still evolve
142+
without being afraid of breaking an unknown number of C extensions.
143+
144+
Non-goals
145+
---------
146+
147+
* Freeze the API forever: this is not the stable ABI. For example,
148+
deprecated functions will continue to be removed on a regular basis.
149+
* C extensions maintainers not using ``Py_COMPAT_API_VERSION`` will
150+
still be affected by C API changes when updating Python.
151+
* Provide a stable ABI: the macro only impacts the regular (non-limited)
152+
API.
153+
* Silver bullet solving all C API issues.
154+
155+
156+
Examples of ``Py_COMPAT_API_VERSION`` usages
157+
============================================
158+
159+
* Remove deprecated functions.
160+
* Remove deprecated structure members, such as
161+
``PyBytesObject.ob_shash``.
162+
* Remove a standard ``#include``, such as ``#include <string.h>``,
163+
from ``<Python.h>``.
164+
* Change the behavior of a function or a macro. For example, calling
165+
``PyObject_SetAttr(obj, name, NULL)`` can fail, to enforce the usage
166+
of the ``PyObject_DelAttr()`` function instead to delete an attribute.
167+
168+
169+
Implementation
170+
==============
171+
172+
* `Issue gh-116587 <https://github.com/python/cpython/issues/116587>`_
173+
* PR: `Add Py_COMPAT_API_VERSION and Py_COMPAT_API_VERSION_MAX macros
174+
<https://github.com/python/cpython/pull/116588>`_
175+
176+
177+
Backwards Compatibility
178+
=======================
179+
180+
There is no impact on backward compatibility.
181+
182+
Adding ``Py_COMPAT_API_VERSION`` and ``Py_COMPAT_API_VERSION_MAX``
183+
macros has no effect on backward compatibility. Only developers setting
184+
the ``Py_COMPAT_API_VERSION`` macro in their project will be impacted by
185+
effects of this macro which is the expected behavior.
186+
187+
188+
Discussions
189+
===========
190+
191+
* C API Evolutions: `Macro to hide deprecated functions
192+
<https://github.com/capi-workgroup/api-evolution/issues/24>`_
193+
(October 2023)
194+
* C API Problems: `Opt-in macro for a new clean API? Subset of functions
195+
with no known issues
196+
<https://github.com/capi-workgroup/problems/issues/54>`_
197+
(June 2023)
198+
199+
200+
Prior Art
201+
=========
202+
203+
* ``Py_LIMITED_API`` macro of :pep:`384` "Defining a Stable ABI".
204+
* Rejected :pep:`606` "Python Compatibility Version" which has a global
205+
scope.
206+
207+
208+
Copyright
209+
=========
210+
211+
This document is placed in the public domain or under the
212+
CC0-1.0-Universal license, whichever is more permissive.

0 commit comments

Comments
 (0)