How to Use Free Threading Stable ABI

Starting with the 3.15 release, CPython has support for compiling extensions targeting a unique kind of Stable ABI with interpreters having the global interpreter lock (GIL) disabled. For 3.14 and 3.13, continue compiling with the version-specific ABI. This document describes how to adapt C API extensions to support free threading.

Identifying the Free-Threaded Limited API Build in C

Define Py_TARGET_ABI3T to the lowest Python version your extension supports, either in the form of Py_PACK_VERSION(3.15) or its direct hex value (such as 0x30f0000 for 3.15). You can use it to enable code that only runs under the free-threaded build:

#ifdef Py_TARGET_ABI3T
/* code that only runs in the free-threaded stable ABI build */
#endif

PyObject and PyVarObject opaqueness

Accessing any member of PyObject directly is now prohibited, unlike the GIL stable ABI, where accessing such members are merely discouraged. For instance, prefer Py_TYPE() and Py_SET_TYPE() over ob_type, Py_REFCNT, Py_IncRef() and Py_DecRef() over ob_refcnt, etc. Also, embedding PyObject_HEAD within a struct is impossible.

Similarly, members of PyVarObject are not visible. If you need any object of such type to be passed as a PyObject parameter to any API function, cast it directly as PyObject.

Module Initialization

Extension modules need to explicitly indicate that they support running with the GIL disabled; otherwise importing the extension will raise a warning and enable the GIL at runtime.

Multi-phase and single-phase initialization is supported to indicate that an extension module targeting the stable ABI supports running with the GIL disabled, though the former is preferred.

Multi-Phase Initialization

Extensions that use multi-phase initialization (functions like PyModuleDef_Init(), PyModExport_* export hook, PyModule_FromSlotsAndSpec()) should add a Py_mod_gil slot in the module definition. If your extension supports older versions of CPython, you should guard the slot with a Py_GIL_DISABLED check. Additionally, prefer PySlot over PyModuleDef_Slot.

static PySlot module_slots[] = {
    ...
#ifdef Py_GIL_DISABLED
    PySlot_STATIC_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
#endif
    PySlot_END
};

Furthermore, using PyABIInfo_VAR and Py_mod_abi is recommended so that an extension module loaded for an incompatible interpreter will trigger an exception, rather than fail with a crash.

#ifdef PY_VERSION_HEX >= 0x030F0000
PyABIInfo_VAR(abi_info);
#endif Py_GIL_DISABLED

static PySlot mymodule_slots[] = {
   ...
#ifdef PY_VERSION_HEX >= 0x030F0000
   PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
#endif
   PySlot_END
};

Single-Phase Initialization

Although members of PyModuleDef is still available for no-GIL Stable ABI and can be used for single-phase initialization (that is, PyModule_Create()), they are not exposed when targeting the regular Stable ABI. Prefer multi-phased initializtion when possible.

Critical Sections

Equivalent functions:

Macro functions

C API functions

Py_BEGIN_CRITICAL_SECTION Py_END_CRITICAL_SECTION

PyCriticalSection_Begin PyCriticalSection_End

Py_BEGIN_CRITICAL_SECTION2 Py_END_CRITICAL_SECTION2

PyCriticalSection2_Begin PyCriticalSection2_End

Py_BEGIN_CRITICAL_SECTION_MUTEX Py_END_CRITICAL_SECTION

PyCriticalSection_BeginMutex PyCriticalSection_End

Py_BEGIN_CRITICAL_SECTION2_MUTEX Py_END_CRITICAL_SECTION2

PyCriticalSection2_BeginMutex PyCriticalSection2_End

Platform-specific considerations

On some platforms, Python will look for and load shared library files named with the abi3 or abi3t tag (for example, mymodule.abi3t.so). Free-threaded interpreters prefer abi3t, but can fall back to abi3. Thus, extensions compatible with both ABIs should use the abi3t tag.

Python does not necessarily check that extensions it loads have compatible ABI. Extension authors are encouraged to add a check using the Py_mod_abi slot or the PyABIInfo_Check() function.

Limited C API Build Tools

If you use setuptools to build your extension, a future version of setuptools will allow py_limited_api=True to be set to allow targeting limited API when building with the free-threaded build. uv supports targeting PEP 803 as of 0.11.3: https://github.com/astral-sh/uv/releases/tag/0.11.3.

Other build tools will support this ABI as well.

See also

Porting Extension Modules to Support Free-Threading: A community-maintained porting guide for extension authors.