v0.0.57, the C API tiers
R06, the C API tiers, is the sixth runtime lesson and the first one in the book that looks at CPython from the C side rather than from inside Python.
The C API is three directories and two macros. Include/ is open to any extension. Include/cpython/ needs Py_LIMITED_API to be undefined, and the mechanism is an #ifndef around an #include at the bottom of each public header. Include/internal/ starts nearly every file with three lines that stop the compiler with an error unless you define Py_BUILD_CORE. On 3.15 that third directory is 148 files and 42552 lines, which is more than the other two put together.
The lesson walks the Py_LIMITED_API guard as a preprocessor stack, so the numbers are measured: 580 functions a limited build may call, 186 the guard takes away, 974 in the other two directories. It then makes the case that the functions are the cheap part. The struct layouts go too, and with no fields to read Py_TYPE becomes a call and Py_DECREF becomes a call to _Py_DecRef.
None of the tiers survive into the binary, and that turns out to be on purpose. Inside the internal headers, 530 names are spelled PyAPI_FUNC and 493 of them resolve through ctypes.pythonapi on the release build, against 757 spelled plain extern of which 3 do. Both spellings sit in the same files, often two lines apart, and 168 comments name which bundled shared extension needs each export. Two Tier 1 recordings put that on record, and the free threaded one finds six more names, which are the ones behind Py_GIL_DISABLED.
_PyDict_SizeOf gets called by hand and returns exactly what dict.__sizeof__ returns, 16 bytes short of sys.getsizeof because that adds the collector header.
Six diagrams, four glossary terms, and five differs= notes checked against a real 3.14 run. Half the cells need the headers, which a browser tab does not have, so they sit behind one flag and say so; the other half only needs ctypes, so all eleven probed cells run under Pyodide.
Merged in #185.