Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 803 – Stable ABI for Free-Threaded Builds

Author:
Petr Viktorin <encukou at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Requires:
703,793,697
Created:
19-Aug-2025
Python-Version:
3.15
Post-History:
08-Sep-2025

Table of Contents

Abstract

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.

Terminology

This PEP uses “GIL-enabled build” as an antonym to “free-threaded build”,that is, an interpreter or extension built withoutPy_GIL_DISABLED.

Motivation

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.

Background

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.

Rationale

The design in this PEP makes several assumptions:

One ABI
A single compiled extension module should support bothfree-threaded and GIL-enabled builds.
No backwards compatibility now
The new limited API will not support CPython 3.14 and below.Projects that need this support can build separate extensions specificallyfor the 3.14 free-threaded interpreter, and for older stable ABI versions.

However, we won’t block the possibility of extending compatibility toCPython 3.14 and below.See arejected idea for how this could work.

API changes are OK
The new Limited API may require extension authors to make significantchanges to their code.Projects that cannot do this (yet) can continue using Limited API 3.14,which will yield extensions compatible with GIL-enabled builds only.
No extra configuration
We do not introduce new “knobs” that influence what API is availableand what the ABI is compatible with.

Specification

Opaque PyObject

Version 3.15 of the Limited API will:

Implications

Making thePyObject,PyVarObject andPyModuleDef structuresopaque means:

  • Their fields may not be directly accessed.

    For example, instead ofo->ob_type, extensions must usePy_TYPE(o).This usage has been the preferred practice for some time.

  • Their size and alignment will not be available.Expressions such assizeof(PyObject) will no longer work.
  • They cannot be embedded in other structures.This mainly affects instance structs of extension-defined types,which will need to be defined using API added inPEP 697 – that is,using astructwithoutPyObject (or other base class struct) atthe beginning, withPyObject_GetTypeData() calls needed to accessthe memory.
  • Variables of these types cannot be created.This mainly affects staticPyModuleDef 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:

New Export Hook (PEP 793)

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.

Runtime ABI checks

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.)

Check for olderabi3

Additionally, 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.

Theabi3t wheel tag

Wheels 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.

New API

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.

Backwards and Forwards Compatibility

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.

Compatibility Overview

The following table summarizes compatibility of wheel tags with CPythoninterpreters. “GIL” stands for GIL-enabled interpreter; “FT” stands for afree-threaded one.

Wheel tag3.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-cp3143.14 (GIL)(unset)existing
cp314-cp314t3.14 (FT)(unset)existing
cp314-abi33.14+ (GIL)PY_PACK_VERSION(3,14)existing
cp314-abi3tN/AN/Aout of spec
cp314-abi3.abi3tN/AN/Areserved
cp315-cp3153.15 (GIL)(unset)continued
cp315-cp315t3.15 (FT)(unset)continued
cp315-abi33.15+ (GIL)PY_PACK_VERSION(3,15)discontinued
cp315-abi3tN/AN/Aout of spec
cp315-abi3.abi3t3.15+ (any)PY_PACK_VERSION(3,15)new

Values in theNote column:

  • existing: The wheel tag is currently in use
  • continued: The wheel tag continues the existing scheme
  • discontinued: The wheel tag continues the existing scheme, but it willbe discouraged. Older tools may still generate it. Installers willcontinue to accept it, but only for GIL-enabled builds, even though the wheelwould be compatible with free-threaded ones.
  • new: Proposed in this PEP.
  • reserved: A mechanism to build a matching extension is not proposed inthis PEP, but may be added in the future.Installers should be prepared to handle the tag.
  • out of spec: Should not be used as-is: extensions should be taggedabi3.abi3t rather than onlyabi3.The entry is included for installers that decompose compressed tag sets.

Security Implications

None known.

How to Teach This

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).

Reference Implementation

This PEP combines several pieces, implemented individually:

  • OpaquePyObject is available in CPython main branch after defining the_Py_OPAQUE_PYOBJECT macro.Implemented in GitHub pull requestpython/cpython#136505.
  • ForPyModExport, seePEP 793 andGitHub issue #140550.
  • A version-checking slot was implemented in GitHub pull requestpython/cpython#137212.
  • A check for olderabi3 was implemented in GitHub pull requestpython/cpython#137957.
  • For wheel tags, there is no implementation yet.
  • A porting guide is not yet written.

Rejected Ideas

Add an alternative stable ABI for free-threading

It would be possible to:

  • Add a new stable ABI (“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).
  • Define an additional macro (“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.

Shims for compatibility with CPython 3.14

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:

  • making it convenient and safe for general extensions
  • testing it (as CPython’s test suite doesn’t involve other CPython versionsthan the one being tested)

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.

Using the Python version wheel tag to determine compatibility

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.

Adding anabi4 wheel tag

Instead 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:

  • change to start with4 to matchabi4, but no longer correspondtoPY_VERSION_HEX (making it harder to generate and check), or
  • not change, making it inconsistent withabi4.

Addingabi3t is a smaller change than addingabi4, making it workbetter as a transitional state before larger changes likePEP 809’sabi2026.

Copyright

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


[8]ページ先頭

©2009-2025 Movatter.jp