Version 3.15 of the Stable ABI will be compatible with both free-threaded andGIL-enabled builds.To allow this, thePyObject internal structure and related APIswill be removed from version 3.15 of the Limited API, requiring migration tonew API for common tasks like defining modules and most classes.
Binary distributions (wheels) built with Limited API version 3.15 and aboveshould use the ABI tagabi3.abi3t.
This PEP uses “GIL-enabled build” as an antonym to “free-threaded build”,that is, an interpreter or extension built withoutPy_GIL_DISABLED.
The Stable ABI is currently not available for free-threaded builds.Extensions will fail to build whenPy_LIMITED_API is defined,and extensions built for GIL-enabled builds of CPython will fail to load(or crash) on free-threaded builds.
In itsacceptance postforPEP 779, the Steering Council stated that it “expects that Stable ABIfor free-threading should be prepared and defined for Python 3.15”.
This PEP proposes the Stable ABI for free-threading.
Python’s Stable ABI, as defined inPEP 384 andPEP 652, provides a way tocompile extension modules that can be loaded on multiple minor versions of theCPython interpreter.Several projects use this to limit the number ofwheels (binary artefacts)that need to be built and distributed for each release, and/or to make iteasier to test with pre-release versions of Python.
With free-threading builds (PEP 703) being on track to eventually becomethe default (PEP 779), we need a way to make the Stable ABI availableto those builds.
To build against the Stable ABI, the extension must use aLimited API,that is, only a subset of the functions, structures, etc. that CPythonexposes.Both the Limited API and the Stable ABI are versioned, and building againstStable ABI 3.X requires using only Limited API 3.X, and yields an extensionthat is ABI-compatible with CPython 3.X andany later version(though bugs in CPython sometimes cause incompatibilities in practice).
The Limited API is not “stable”: newer versions may remove API thatwere a part of older versions.This PEP proposes the most significant such removal to date.
The design in this PEP makes several assumptions:
However, we won’t block the possibility of extending compatibility toCPython 3.14 and below.See arejected idea for how this could work.
Version 3.15 of the Limited API will:
PyObjectPyVarObjectPyModuleDef_BasePyModuleDefPyObject_HEAD_PyObject_EXTRA_INITPyObject_HEAD_INITPyObject_VAR_HEADPy_SET_TYPE()Making thePyObject,PyVarObject andPyModuleDef structuresopaque means:
For example, instead ofo->ob_type, extensions must usePy_TYPE(o).This usage has been the preferred practice for some time.
sizeof(PyObject) will no longer work.structwithoutPyObject (or other base class struct) atthe beginning, withPyObject_GetTypeData() calls needed to accessthe memory.PyModuleDef variables needed to defineextension modules.Extensions will need to switch to API added inPEP 793.The following functions will become unusable in practice (in the new LimitedAPI), since an extension cannot create valid, statically allocated, inputfor them. To ease the transition for extension developers,they will not yet be removed from the Limited API:
Implementation of this PEP requiresPEP 793 (PyModExport):A new entry point for C extension modules), which was accepted for Python3.15.
Since existing ways of defining modules use API that this PEP removes(namely,PyModuleDef), extensions will need to migrate to PEP 793’snew “export hook” when switching to Limited API 3.15.
Users – or rather the tools they use for building and installing extensions –will continue to be responsible for not putting incompatible extensions onPython’s import paths.This decision makes sense since tools typically have much richer metadata thanwhat CPython can check.Typically, build tools and installers usePyPA packaging metadata andplatform compatibility tags to communicate compatibility details, but othermodels are possible.
However, CPython will add a line of defense against outdated or misconfiguredtools, or human mistakes, in the form of a newmodule slot containingbasic ABI information.This information will be checked when a module is loaded, and incompatibleextensions will be rejected.The specifics are left to the C API working group(seeissue 72).
This slot will becomemandatory with the new export hook added inPEP 793.(That PEP currently says “there are no required slots”; it will be updated.)
abi3Additionally, in free-threaded builds,PyModuleDef_Init() will detectextensions using the pre-free-threading Stable ABI, emit an informativemessage when one is loaded,and raise an exception.(Implementation note: A message will be printed before raising the exception,because extensions that attempt to handle an exception using incompatible ABIwill likely crash and lose the exception’s message.)
This check for olderabi3 relies on internal bit patterns and may beremoved in future CPython versions, if the internal object layout needsto change.
abi3t wheel tagWheels that use a stable ABI compatible with free-threading CPython buildsshould use a newABI tag:abi3t.The name is chosen to reflect the fact that this ABI is similar toabi3,with limitations necessary to support free-threading (which uses the lettert in existing, version-specific ABI tags likecp314t).
Package installers should treat this tag as completely separate fromabi3.They should allowabi3t-tagged wheels for free-threaded builds whereverthey currently allowabi3-tagged ones for (orherwise equal) non-free-threadedbuilds.
Build tools should generateabi3.abi3t instead ofabi3 when the Pythontag iscp315 and above (or equivalently: when settingPy_LIMITED_APIto3.15 (0x030f0000) or above).abi3.abi3t is acompressed tag set that signals compatibility with bothabi3 andabi3t.
Note
The version of the Stable ABI is indicated by thePython wheel tag; thisPEP does not change that.For example, a wheel taggedcp315-abi3.abi3t will be compatible with3.15, 3.16, and later versions;cp317-abi3.abi3t will be compatible with 3.17+.
Theabi3t tag can be used in extensions compatible with earlier versions offree-threaded Python.For example, an extension compatible with GIL-enabledand free-threadedbuilds of3.14, 3.15, and higher versions would be taggedcpy314-abi3.abi3t.This PEP does not propose an official way to build such extensions, but sincea mechanism for that can be added later (seea Rejected idea), installers should be ready to acceptthe tag.
Implementing this PEP will make it possible to build extensions thatcan be successfully loaded on free-threaded Python, but not necessarily onesthat are thread-safe without a GIL.
Limited API to allow thread-safety without a GIL – presumablyPyMutex,PyCriticalSection, andsimilar – will be added via the C API working group, or in a follow-up PEP.
Limited API 3.15 will not be backwards-compatible with older CPython releases,due to the need to use newPyModExport API added inPEP 793.
Extension authors who cannot switch may continue to use Limited API 3.14and below.For compatibility with free-threaded builds, they can compile usingversion-specific ABI – for example, compile on CPython 3.15 without definingPy_LIMITED_API.
Limited API 3.15 will be forward-compatible with future versions of CPython 3.x.Older versions of the Limited API (that is, 3.14 and below) will continueto be forward-compatible with GIL-enabled builds of CPython 3.x, starting withthe version that introduced the given Limited API.
The following table summarizes compatibility of wheel tags with CPythoninterpreters. “GIL” stands for GIL-enabled interpreter; “FT” stands for afree-threaded one.
| Wheel tag | 3.14 (GIL) | 3.14 (FT) | 3.15 (GIL) | 3.15 (FT) | 3.16+ (GIL) | 3.16+ (FT) |
|---|---|---|---|---|---|---|
cp314-cp314 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
cp314-cp314t | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
cp314-abi3 | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
cp314-abi3t (*) | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
cp314-abi3.abi3t (*) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
cp315-cp315 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
cp315-cp315t | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
cp315-abi3 | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
cp315-abi3t (*) | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
cp315-abi3.abi3t | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
(*): Wheels with these tags cannot be built; see table below
The following table summarizes which wheel tag should be used for an extensionbuilt with a given interpreter andPy_LIMITED_API macro:
| To get the wheel tag… | Compile on… | withPy_LIMITED_API set to… | Note |
|---|---|---|---|
cp314-cp314 | 3.14 (GIL) | (unset) | existing |
cp314-cp314t | 3.14 (FT) | (unset) | existing |
cp314-abi3 | 3.14+ (GIL) | PY_PACK_VERSION(3,14) | existing |
cp314-abi3t | N/A | N/A | out of spec |
cp314-abi3.abi3t | N/A | N/A | reserved |
cp315-cp315 | 3.15 (GIL) | (unset) | continued |
cp315-cp315t | 3.15 (FT) | (unset) | continued |
cp315-abi3 | 3.15+ (GIL) | PY_PACK_VERSION(3,15) | discontinued |
cp315-abi3t | N/A | N/A | out of spec |
cp315-abi3.abi3t | 3.15+ (any) | PY_PACK_VERSION(3,15) | new |
Values in theNote column:
abi3.abi3t rather than onlyabi3.The entry is included for installers that decompose compressed tag sets.None known.
A porting guide will need to explain how to move to APIs added inPEP 697 (Limited C API for Extending Opaque Types)andPEP 793 (PyModExport).
This PEP combines several pieces, implemented individually:
PyObject is available in CPython main branch after defining the_Py_OPAQUE_PYOBJECT macro.Implemented in GitHub pull requestpython/cpython#136505.PyModExport, seePEP 793 andGitHub issue #140550.abi3 was implemented in GitHub pull requestpython/cpython#137957.It would be possible to:
abi3t”) specifically for free-threading,which would be incompatible with the existingabi3.Extensions would need no code changes to targetabi3t and builds would becompatible with free-threaded CPython (3.14 and above).Py_OPAQUE_PYOBJECT”), which would makePyObject opaque as in this PEP. Extensions would need code changes as inthis PEP, and as in this PEP, compiled extensions (“abi3.abi3t”) wouldbe compatible with all builds of CPython 3.15+.This scheme was rejected as too complex.It would also make the free-threading memory layout ofPyObject partof the stable ABI, preventing future adjustments.
It’s possible to build acp314-abi3.abi3t extension – one compatiblewith 3.14 (both free-threaded build and default).There are several challenges around this:
So, providing a mechanism to build such extensions is best suited to anexternal project (for example, one likepythoncapi-compat).It’s out of scope for CPython’s C API, and this PEP.
To sketch how such a mechanism could work:
The main issue that prevents compatibility with Python 3.14 is that withopaquePyObject andPyModuleDef, it is not feasible to initializean extension module.The solution,PEP 793, is only being added in Python 3.15.
It is possible to work around this using the fact that the 3.14 ABIs (bothfree-threading and GIL-enabled) are “frozen”, so it is possible for anextension to query the running interpreter, and for 3.14, useastruct definition corresponding to the detected build’sPyModuleDef.
A previous version of this PEP avoided adding a new wheel tag (abi3t),and specified that wheels taggedabi3 would be compatible withfree-threading if thePython tag iscp315 or higher.
Such a scheme would work for this PEP, but it cannot express that an extensionis compatible with both GIL-enabled and free-threaded buildsof CPython 3.14 or lower.Adding a new explicit tag means thatif we allow building such wheels in thefuture, packaging tools should not need additional changes to support them.They would be taggedcp314-abi3.abi3t.
abi4 wheel tagInstead ofabi3t, we could “bump the version” and useabi4 insteadas the wheel ABI tag.In the wheel tag, the difference is largely cosmetic.
However, one thing this PEP does not propose is changing thefilenametag: extensions will be named with the extensions like.abi3.so.Changing this while keeping compatibility with GIL-enabled builds would bean unnecessary technical change.
Usingabi3.abi4 in wheel tags but only.abi3 in filenames wouldlook more inconsistent thanabi3.abi3t and.abi3.
If we addabi4 tag, thePy_LIMITED_API value would either need to:
4 to matchabi4, but no longer correspondtoPY_VERSION_HEX (making it harder to generate and check), orabi4.Addingabi3t is a smaller change than addingabi4, making it workbetter as a transitional state before larger changes likePEP 809’sabi2026.
This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.
Source:https://github.com/python/peps/blob/main/peps/pep-0803.rst
Last modified:2025-12-05 13:48:20 GMT