# 67. Stable ABI

# 67. Stable ABI

The stable ABI is CPython’s binary compatibility interface for extension modules. It lets an extension module be compiled once and loaded by multiple CPython versions, as long as those versions support the requested ABI level.

The normal CPython C API exposes many implementation details. An extension compiled against those details often needs a separate binary wheel for each Python version. The stable ABI reduces that burden by restricting the extension to a smaller, more stable set of functions, types, and macros.

The stable ABI is closely related to the limited API:

| Term | Meaning |
|---|---|
| Limited API | Source-level subset selected at compile time |
| Stable ABI | Binary-level interface promised across CPython versions |
| `Py_LIMITED_API` | Compile-time macro that asks headers to expose the limited API |
| `abi3` | Wheel tag used for stable-ABI extension binaries |

## 67.1 Why the Stable ABI Exists

Python extension modules are native binaries. A binary compiled for one CPython version may depend on details that differ in another version.

Examples of unstable details:

```text
object struct layout
type object internals
frame internals
thread state internals
private macros
bytecode details
interpreter state structures
```

Without a stable ABI, package authors often publish wheels like:

```text
cp310-cp310-manylinux_x86_64.whl
cp311-cp311-manylinux_x86_64.whl
cp312-cp312-manylinux_x86_64.whl
cp313-cp313-manylinux_x86_64.whl
```

With the stable ABI, one binary can target multiple CPython versions:

```text
cp38-abi3-manylinux_x86_64.whl
```

This reduces build matrix size and simplifies distribution.

## 67.2 Normal ABI vs Stable ABI

The normal CPython ABI gives extensions broad access to CPython implementation details.

Stable ABI restricts that access.

| Area | Normal ABI | Stable ABI |
|---|---|---|
| Object layout | Often visible | Mostly opaque |
| Struct fields | Often directly accessed | Usually hidden |
| Macros | May inspect internals | Restricted or function-backed |
| Compatibility | Version-specific | Cross-version |
| Performance | Maximum possible | Slightly more indirect |
| Power | Full CPython-specific access | Smaller supported surface |

The normal ABI is useful for extensions that need maximum speed or deep CPython integration.

The stable ABI is useful for extensions that value binary portability.

## 67.3 The Limited API

The limited API is the source-level mechanism for building against the stable ABI.

An extension opts in with:

```c
#define Py_LIMITED_API 0x03080000
#include <Python.h>
```

This means:

```text
use the limited API available since Python 3.8
```

The macro value is a hexadecimal version number:

| Macro value | Target minimum |
|---|---|
| `0x03080000` | Python 3.8 |
| `0x03090000` | Python 3.9 |
| `0x030A0000` | Python 3.10 |
| `0x030B0000` | Python 3.11 |
| `0x030C0000` | Python 3.12 |

If you target an older limited API version, your binary can run on more Python versions, but you can use fewer APIs.

## 67.4 The `abi3` Wheel Tag

Python wheels use compatibility tags.

A normal CPython extension wheel may use a tag like:

```text
cp312-cp312-manylinux_x86_64
```

This means it targets CPython 3.12 specifically.

A stable ABI wheel may use:

```text
cp38-abi3-manylinux_x86_64
```

This means:

```text
built for CPython
uses stable ABI
requires at least Python 3.8
platform is manylinux x86_64
```

The `abi3` tag is the packaging signal that the extension avoids version-specific CPython ABI dependencies.

## 67.5 What Becomes Opaque

The stable ABI makes many structures opaque.

Under the full API, code may access fields directly:

```c
Py_TYPE(obj)
Py_REFCNT(obj)
type->tp_name
type->tp_basicsize
```

Under the limited API, direct field access is reduced. Extension code should prefer accessor functions and supported APIs.

For example, instead of depending on layout details, use operations such as:

```c
PyObject_Type(obj)
PyObject_GetAttrString(obj, "name")
PyLong_AsLong(obj)
PyUnicode_AsUTF8String(obj)
```

The stable ABI works by preserving callable entry points rather than preserving every internal structure layout.

## 67.6 Why Struct Layout Is a Problem

C struct layout is part of the binary interface.

Suppose an extension compiles against:

```c
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
    int field_a;
} SomeObject;
```

If a later CPython version changes the layout:

```c
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
    void *new_field;
    int field_a;
} SomeObject;
```

old native code may read the wrong memory offset.

This is why stable ABI code avoids direct layout assumptions. Opaque pointers allow CPython to change internals while keeping function signatures stable.

## 67.7 Example Stable ABI Extension

A small extension can target the limited API:

```c
#define Py_LIMITED_API 0x03080000
#define PY_SSIZE_T_CLEAN
#include <Python.h>

static PyObject *
demo_add(PyObject *self, PyObject *args)
{
    long a;
    long b;

    if (!PyArg_ParseTuple(args, "ll", &a, &b)) {
        return NULL;
    }

    return PyLong_FromLong(a + b);
}

static PyMethodDef methods[] = {
    {"add", demo_add, METH_VARARGS, "Add two integers"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef module = {
    PyModuleDef_HEAD_INIT,
    "demo",
    "Stable ABI demo module",
    -1,
    methods
};

PyMODINIT_FUNC
PyInit_demo(void)
{
    return PyModule_Create(&module);
}
```

This style avoids direct object layout access and uses stable C API functions.

## 67.8 Building with Setuptools

A minimal setup file:

```python
from setuptools import Extension, setup

setup(
    name="demo",
    ext_modules=[
        Extension(
            "demo",
            ["demo.c"],
            py_limited_api=True,
            define_macros=[
                ("Py_LIMITED_API", "0x03080000"),
            ],
        )
    ],
)
```

For wheels, build configuration must also emit the `abi3` tag. Many projects use `bdist_wheel` options or modern build backend settings for this.

The important rule is that both the compiled extension and the wheel metadata must agree that the extension uses the stable ABI.

## 67.9 What the Stable ABI Allows

Stable ABI extensions can still do many useful things:

```text
create modules
define functions
create Python objects
parse arguments
raise exceptions
call Python callables
manipulate lists and dicts through API functions
work with Unicode
work with bytes
use capsules
use buffers through supported APIs
define heap types through supported mechanisms
```

For many extension modules, this is enough.

A wrapper around a C library often fits well:

```text
Python arguments
    ↓
convert to C values
    ↓
call native library
    ↓
convert result to Python object
```

This does not usually require direct access to CPython internals.

## 67.10 What the Stable ABI Restricts

Stable ABI code should avoid:

```text
direct struct field access
private `_Py*` APIs
internal headers
version-specific frame internals
direct bytecode assumptions
direct type object mutation
macros that expand to internal layout access
interpreter private state
```

Some performance-oriented extensions depend on these details and cannot use the stable ABI easily.

For example, an extension that heavily inspects frame objects, modifies type internals, or uses private vectorcall details may need the full CPython API.

## 67.11 Static Types vs Heap Types

Stable ABI design prefers heap types over static `PyTypeObject` definitions.

A static type often requires direct initialization of a `PyTypeObject` struct:

```c
static PyTypeObject MyType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "demo.MyType",
    .tp_basicsize = sizeof(MyObject),
    .tp_new = My_new,
};
```

This exposes dependence on the layout of `PyTypeObject`.

Heap types use `PyType_Spec` and slot definitions:

```c
static PyType_Slot My_slots[] = {
    {Py_tp_new, My_new},
    {Py_tp_dealloc, My_dealloc},
    {0, NULL}
};

static PyType_Spec My_spec = {
    .name = "demo.MyType",
    .basicsize = sizeof(MyObject),
    .itemsize = 0,
    .flags = Py_TPFLAGS_DEFAULT,
    .slots = My_slots,
};
```

Then create the type at runtime:

```c
PyObject *type = PyType_FromSpec(&My_spec);
```

This avoids exposing the full `PyTypeObject` layout to the extension binary.

## 67.12 Stable ABI Type Design

A stable ABI extension type should use:

```text
PyType_Spec
PyType_Slot
heap allocation
module state
accessor functions
documented slot IDs
```

Prefer this shape:

```text
module init
    create heap type
    add type to module
    keep state in module
    avoid static mutable Python objects
```

This aligns better with modern CPython work on subinterpreters and runtime isolation.

## 67.13 Reference Counting Still Applies

The stable ABI does not remove manual ownership.

This code still returns a new reference:

```c
return PyLong_FromLong(42);
```

This code still requires cleanup:

```c
PyObject *s = PyUnicode_FromString("hello");
if (s == NULL) {
    return NULL;
}

Py_DECREF(s);
```

The stable ABI changes what binary symbols and structures the extension can use. It does not change C API ownership rules.

## 67.14 Performance Tradeoffs

Stable ABI may cost performance in some cases.

Reasons:

```text
less direct field access
more function calls
fewer private fast paths
less access to version-specific optimizations
```

For many extensions, this overhead is small because most time is spent in the native library or algorithm.

For extensions that wrap high-frequency Python object operations, the cost can be more visible.

Good candidates:

```text
compression libraries
database bindings
cryptography wrappers
file format parsers
image codecs
native algorithms with coarse calls
```

Poorer candidates:

```text
extensions doing many tiny object-level operations
profilers using private frame internals
debuggers relying on interpreter structures
JIT or bytecode tools
performance-critical custom containers
```

## 67.15 ABI Compatibility vs API Compatibility

API compatibility means source code still compiles.

ABI compatibility means an existing binary still loads and works.

These are different.

| Compatibility | Question |
|---|---|
| API | Can I recompile this source? |
| ABI | Can I reuse this compiled binary? |

The stable ABI is about ABI compatibility.

An extension using the full C API may remain source-compatible across Python versions, yet still require recompilation for each version.

## 67.16 The Cost of Depending on Internals

Private CPython internals can be tempting.

Example motivations:

```text
avoid allocation
read type fields directly
access frame state
skip error checks
use private fast paths
reuse internal helper functions
```

But each private dependency increases maintenance cost.

Risks:

```text
compile failure on new Python versions
binary crash after upgrade
subinterpreter incompatibility
debug-build differences
free-threading incompatibility
platform-specific bugs
```

Stable ABI code gives up these shortcuts in exchange for stronger binary durability.

## 67.17 Stable ABI and Free-Threaded CPython

Free-threaded CPython work makes ABI discipline more important.

Code that assumes details such as direct reference count layout, global interpreter state, or GIL-protected global mutation may become fragile.

The stable ABI encourages extensions to use documented operations rather than internal fields. This does not automatically make an extension free-threading-safe, but it reduces dependence on implementation details that are likely to evolve.

Thread safety still requires separate design:

```text
protect native state
avoid unsafe globals
respect Python object ownership
use documented thread APIs
avoid hidden interpreter assumptions
```

## 67.18 Stable ABI and Subinterpreters

Subinterpreters also favor stable, isolated extension design.

Good stable ABI design avoids:

```text
static mutable Python objects
cached interpreter-specific objects
global borrowed references
direct interpreter state pointers
single global module state
```

Prefer:

```text
per-module state
heap types
capsules with immutable function tables
explicit initialization
documented APIs
```

The stable ABI and subinterpreter-safe design are not identical, but they point in the same direction: fewer hidden process-global assumptions.

## 67.19 When to Use the Stable ABI

Use the stable ABI when:

```text
you want fewer wheels
your extension wraps a native library
your API surface is modest
you can avoid private CPython internals
binary portability matters more than maximum micro-optimization
```

Use the full API when:

```text
you need CPython internals
you depend on private performance paths
you implement low-level runtime tooling
you inspect frames deeply
you need APIs outside the limited subset
```

A library may also split itself:

```text
stable ABI core wrapper
    +
version-specific optional accelerator
```

This gives broad compatibility with optional specialized speedups.

## 67.20 Practical Checklist

Before choosing stable ABI, check:

| Question | Good answer |
|---|---|
| Can the extension avoid private `_Py*` APIs? | Yes |
| Can it avoid direct struct field access? | Yes |
| Can it use heap types instead of static types? | Yes |
| Is most work done in native code rather than Python object churn? | Yes |
| Does it need frame or bytecode internals? | No |
| Does it require one wheel across Python versions? | Yes |

If most answers align, stable ABI is a strong choice.

## 67.21 Common Mistakes

| Mistake | Result |
|---|---|
| Defining `Py_LIMITED_API` but using private APIs | Build or runtime failure |
| Building with limited API but publishing wrong wheel tag | Installer confusion |
| Directly initializing static `PyTypeObject` | ABI coupling |
| Assuming stable ABI gives source portability to all versions | It only covers selected APIs |
| Ignoring reference ownership | Leaks or crashes |
| Caching interpreter-specific globals | Subinterpreter bugs |
| Using macros that access struct fields | Breaks limited API discipline |

Stable ABI requires consistent build, code, and packaging choices.

## 67.22 Chapter Summary

The stable ABI is CPython’s cross-version binary interface for extension modules. It lets one compiled extension binary work across multiple CPython versions by restricting code to the limited API.

The limited API is selected with `Py_LIMITED_API`. The resulting wheel usually uses the `abi3` tag. This reduces wheel build matrices and improves binary portability.

The tradeoff is reduced access to CPython internals. Stable ABI extensions should avoid direct struct access, private APIs, static type layout assumptions, and interpreter-specific global state.

For many native wrappers, codecs, parsers, database drivers, and compute libraries, the stable ABI is a practical default. For extensions that need deep runtime internals or maximum object-level speed, the full CPython API remains necessary.
