Skip to main content

Python/typeobject.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Python/typeobject.c

This annotation covers type creation and readying. See python_typeobject_detail for type.__call__, tp_new, tp_init, and the descriptor protocol.

Map

LinesSymbolRole
1-100type_new_impltype(name, bases, ns) — create a new type
101-250_Py_CalculateMetaclassDetermine the winning metaclass from bases
251-400mro_internalCompute the MRO using C3 linearization
401-600type_readyFinalize a type: inherit slots, build tp_dict, compute mro
601-800Slot inheritanceCopy tp_* slots from base types
801-1000update_one_slotUpdate a single slot in a type after modification
1001-1500type_set_doc / type_set_abstractmethods__doc__ and __abstractmethods__ setters

Reading

type_new_impl

// CPython: Python/typeobject.c:2980 type_new_impl
static PyObject *
type_new_impl(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
PyObject *name, *bases, *orig_dict;
/* Parse name, bases, dict */
/* 1. Determine the winning metaclass */
PyTypeObject *winner = _Py_CalculateMetaclass(metatype, bases);
if (winner != metatype) {
/* Redirect to winning metaclass */
return winner->tp_new(winner, args, kwds);
}
/* 2. Allocate the type object */
type = (PyHeapTypeObject *)metatype->tp_alloc(metatype, nslot);
/* 3. Fill in slots from the class dict */
/* 4. Set bases and compute MRO */
type->tp_bases = bases;
type->tp_mro = mro_internal(type, NULL);
/* 5. type_ready */
type_ready(type);
}

_Py_CalculateMetaclass

// CPython: Python/typeobject.c:2850 _Py_CalculateMetaclass
/* Find the most-derived metaclass among metatype and the metatypes of all bases.
Raises TypeError if two unrelated metaclasses are found. */
PyTypeObject *
_Py_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases)
{
PyTypeObject *winner = metatype;
Py_ssize_t nbases = PyTuple_GET_SIZE(bases);
for (Py_ssize_t i = 0; i < nbases; i++) {
PyTypeObject *tmp = Py_TYPE(PyTuple_GET_ITEM(bases, i));
if (PyType_IsSubtype(winner, tmp)) continue;
if (PyType_IsSubtype(tmp, winner)) { winner = tmp; continue; }
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: the metaclass of a derived class must be a "
"(non-strict) subclass of the metaclasses of all its bases");
return NULL;
}
return winner;
}

C3 MRO (mro_internal)

// CPython: Python/typeobject.c:1820 mro_internal
/* C3 linearization:
L[C(B1, B2, ...)] = C + merge(L[B1], L[B2], ..., B1 B2 ...)
merge: take the head of the first list if it does not appear
in the tail of any other list; otherwise try the next list. */
static PyObject *
mro_internal(PyTypeObject *type, PyObject **p_old_mro)

type.__mro__ is computed once at class creation time and cached. Changing __bases__ recomputes it.

type_ready key steps

// CPython: Python/typeobject.c:6280 type_ready
static int
type_ready(PyTypeObject *type)
{
/* 1. Inherit tp_base from bases[0] if not set */
/* 2. Initialize tp_dict from __dict__ */
/* 3. Inherit slots from base (tp_new, tp_hash, tp_repr, ...) */
/* 4. Special-case: add __doc__ descriptor if type has tp_doc */
/* 5. Add tp_getattro/setattro wrappers (slots → Python methods) */
/* 6. Fixup tp_richcompare to match __eq__/__lt__ etc. */
/* 7. Set type->tp_flags |= Py_TPFLAGS_READY */
}

type_ready is called on every C-defined type at interpreter startup and on every new heap type created by type.__new__.

gopy notes

type.__new__ is in objects/type_call.go:TypeNew. _Py_CalculateMetaclass is objects.CalculateMetaclass. C3 MRO is objects.ComputeMRO in objects/mro.go. type_ready equivalent is objects.TypeReady which populates the slot table and inherits from bases.