Skip to main content

Modules/_ctypes/ctypes.h

cpython 3.14 @ ab2d84fe1023/Modules/_ctypes/ctypes.h

ctypes.h is the internal header shared by every C file in Modules/_ctypes/. It defines the per-module state struct (ctypes_state), the core object hierarchy (CDataObject, CThunkObject, PyCFuncPtrObject, CFieldObject), the type-metadata store (StgInfo) that replaced StgDict in Python 3.13, and the FFI argument wrapper (PyCArgObject). It also carries all shared function declarations and the value-conversion typedef trio (GETFUNC, SETFUNC, PARAMFUNC). Nothing in this header is exposed to Python directly; it is the internal ABI contract among all ctypes C translation units.

Map

LinesSymbolRole
1–16alloca portability blockEnsures alloca() is available on SVR4, MSVC, and GCC/Clang
17–22core includespycore_moduleobject.h, pycore_typeobject.h, critical-section and atomic-wrapper headers
24–38Py_FFI_COMPLEX_AVAILABLERuntime check for C99 complex-type libffi support on Apple platforms (gh-128156)
40–46PARAMFLAG_*POSIX-only in/out/lcid parameter-direction flags
49–62CTYPES_MAX_ARGCOUNTStack-allocation guard: 1024 args (1000 on Emscripten)
74–104ctypes_statePer-module state: all ctypes PyTypeObject * pointers plus shared PyObject * caches
110–130get_module_state(), get_module_state_by_class(), get_module_state_by_def()Inline accessors for ctypes_state from a module, type, or metatype
170–172GETFUNC, SETFUNC, PARAMFUNCFunction-pointer typedefs for value-conversion callbacks
200–207fielddescPredefined primitive-type descriptor: format char, size, alignment, get/set functions
215–227tagCDataObject / CDataObjectCore ctypes object: raw buffer pointer, ownership flag, base chain, reference dict
229–239CThunkObjectFFI closure wrapper: writeable and executable closure pointers, ffi_cif, converters
241–268PyCFuncPtrObjectFunction-pointer object: extends CDataObject with argtypes, restype, checker, errcheck
284–286CDataObject_CheckExact, CDataObject_Check, _CDataObject_HasExternalBufferFast type and buffer-location checks
327–348CFieldObjectStruct/union field descriptor: byte offset, bit offset, field size, get/set functions
393–422StgInfoPer-type metadata store: size, align, length, ffi_type, proto, flags, format, shape
440–469STGINFO_LOCK, STGINFO_UNLOCK, stginfo_get_dict_final(), stginfo_set_dict_final()Thread-safety macros and inline helpers guarding the dict_final finalization flag
502–508FUNCFLAG_*Calling-convention flags: CDECL, PYTHONAPI, USE_ERRNO, USE_LASTERROR, STDCALL
510–511TYPEFLAG_*Type characteristic flags: ISPOINTER, HASPOINTER
515–521_ctypes_callproc() declarationCentral FFI dispatcher signature
524–540tagPyCArgObject / PyCArgObjectFFI argument wrapper: tag char, value union, owning object reference
545PyCArgObject_new()Constructor declaration for PyCArgObject
548–557PyCData_get(), PyCData_set()Public get/set accessors for ctypes data values
577_ctypes_get_ffi_type()Returns the ffi_type * appropriate for a given restype object
740–757PyStgInfo_FromType(), PyStgInfo_FromObject(), PyStgInfo_FromAny()StgInfo lookup by type, instance, or either
770–790PyStgInfo_Init()Initializes StgInfo on freshly created ctypes types

Reading

Module State: ctypes_state

The struct at line 74 collects every PyTypeObject * needed by the ctypes subsystem alongside shared PyObject * caches such as array_cache, error_object_name, and PyExc_ArgError. Keeping all of this in a single per-module struct is the standard CPython pattern for avoiding global state and enabling subinterpreter isolation. Three inline accessors retrieve the state pointer depending on what is available at the call site: a module object (get_module_state), a heap type (get_module_state_by_class), or a metatype (get_module_state_by_def).

// CPython: Modules/_ctypes/ctypes.h:74 ctypes_state
typedef struct {
PyTypeObject *DictRemover_Type;
PyTypeObject *PyCArg_Type;
PyTypeObject *PyCField_Type;
PyTypeObject *PyCThunk_Type;
/* ... 15 more type pointers ... */
PyObject *_unpickle;
PyObject *array_cache;
PyObject *error_object_name;
PyObject *PyExc_ArgError;
PyObject *swapped_suffix;
} ctypes_state;

static inline ctypes_state *
get_module_state(PyObject *module)
{
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (ctypes_state *)state;
}

Core Object Hierarchy

CDataObject (line 215) is the base for every concrete ctypes value. b_ptr points to the actual C memory. b_needsfree controls whether that memory is owned by this object. b_base chains sub-objects back to a parent, keeping the parent alive while a pointer into its buffer exists. b_objects is a dict of additional Python object references needed to prevent dangling pointers from embedded Python-owned buffers.

CThunkObject (line 229) wraps an ffi_closure so that Python callables can be passed as C function pointers. It carries two pointers to the same closure memory: pcl_write (writeable, for setup time) and pcl_exec (executable, for call time), reflecting W^X page-permission restrictions on modern platforms.

PyCFuncPtrObject (line 241) extends CDataObject to represent a C function pointer. It adds argtypes, restype, checker, and errcheck Python attributes alongside COM-specific fields used on Windows.

// CPython: Modules/_ctypes/ctypes.h:215 tagCDataObject
struct tagCDataObject {
PyObject_HEAD
char *b_ptr; /* pointer to memory block */
int b_needsfree; /* need _we_ free the memory? */
CDataObject *b_base; /* pointer to base object or NULL */
Py_ssize_t b_size; /* size of memory block in bytes */
Py_ssize_t b_length; /* number of references we need */
Py_ssize_t b_index; /* index into base's b_object list */
PyObject *b_objects; /* dictionary of references we need to keep */
union value b_value;
};

StgInfo: Type Metadata Since 3.13

Before Python 3.13, ctypes stored per-type layout metadata in a custom dict subclass called StgDict. Since 3.13 that subclass is gone. Metadata now lives in StgInfo (line 393), a plain C struct embedded in each ctypes metatype object. The ffi_type_pointer field is the libffi structure descriptor built up during field registration. dict_final is an atomic flag set once _fields_ has been fully processed, after which further modification is rejected.

Thread safety during the finalization window is provided by STGINFO_LOCK / STGINFO_UNLOCK (line 440), which expand to Py_BEGIN_CRITICAL_SECTION / Py_END_CRITICAL_SECTION on free-threaded builds and to no-ops on GIL builds. The inline helpers stginfo_get_dict_final() and stginfo_set_dict_final() use FT_ATOMIC_LOAD_UINT8_RELAXED / FT_ATOMIC_STORE_UINT8_RELAXED to keep reads and writes safe across both build modes.

// CPython: Modules/_ctypes/ctypes.h:393 StgInfo
typedef struct {
int initialized;
Py_ssize_t size; /* number of bytes */
Py_ssize_t align; /* alignment requirements */
Py_ssize_t length; /* number of fields */
ffi_type ffi_type_pointer;
PyObject *proto; /* for arrays: item type; for pointers: pointed-to type */
SETFUNC setfunc;
GETFUNC getfunc;
PARAMFUNC paramfunc;
PyObject *argtypes;
PyObject *converters;
PyObject *restype;
PyObject *checker;
PyObject *pointer_type;
PyObject *module;
int flags;
uint8_t dict_final;
char *format;
int ndim;
Py_ssize_t *shape;
} StgInfo;

PyCArgObject: The FFI Argument Wrapper

PyCArgObject (line 524) is an opaque wrapper produced by from_param() converters. It carries a single C value in a union value tagged by a single character in tag. The tag drives the ffi_type * selection and the PyCArg_repr() format string. The obj field keeps the originating Python object alive while the argument is live on the C call stack. The size field is used by the 'z' tag to record the string length including the NUL terminator.

// CPython: Modules/_ctypes/ctypes.h:524 tagPyCArgObject
struct tagPyCArgObject {
PyObject_HEAD
ffi_type *pffi_type;
char tag;
union {
char c;
short h;
int i;
long l;
long long q;
double d;
float f;
void *p;
} value;
PyObject *obj;
Py_ssize_t size;
};

gopy notes

StgInfo replaced StgDict in 3.13. A gopy port of ctypes must embed an equivalent metadata struct on each ctypes metatype rather than subclassing a dict type. The STGINFO_LOCK / STGINFO_UNLOCK pattern maps to a sync.Mutex embedded in the equivalent Go struct, conditional on a free-threaded build tag. CTYPES_MAX_ARGCOUNT (1024) must be reproduced as a named constant to keep the stack-allocation guard in callproc.c faithful to CPython behavior.

CPython 3.14 changes

StgInfo was introduced in 3.13 to replace StgDict. In 3.14 free-threaded support added a PyMutex mutex field inside StgInfo under Py_GIL_DISABLED, and the critical-section macros were wired through pycore_critical_section.h (line 21). The pycore_pyatomic_ft_wrappers.h include (line 22) provides FT_ATOMIC_LOAD_UINT8_RELAXED and FT_ATOMIC_STORE_UINT8_RELAXED used by the dict_final inline helpers. The Py_FFI_COMPLEX_AVAILABLE macro block (lines 24-38) was added to handle Apple libffi's runtime-only support for C99 complex types, gated on __builtin_available(macOS 10.15, *) where the builtin is available.