#!/usr/bin/env python3 """Check the slow-path MRO result declaration and allocation order for C89.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic PyObject \*\nmro_implementation\(PyTypeObject \*type\)\n\{(.*?)\n\}", source, re.S, ) assert match, "mro_implementation definition missing" body = match.group(1) opening = body.split("if (!_PyType_IsReady(type))", 1)[0] assert re.search(r"\bPyObject \*result;", opening), ( "mro_implementation: PyObject *result; must precede statements (C89)" ) assert len(re.findall(r"\bPyObject \*result;", body)) == 1, ( "declare the slow-path result exactly once without initialization" ) expected = """ for (i = 0; i < n; i++) { PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i)); to_merge[i] = base->tp_mro; } to_merge[n] = bases; result = PyList_New(1); if (result == NULL) { PyMem_Free(to_merge); return NULL; } Py_INCREF(type); PyList_SET_ITEM(result, 0, (PyObject *)type); if (pmerge(result, to_merge, n + 1) < 0) { Py_CLEAR(result); } PyMem_Free(to_merge); return result; """ tail = body[body.index(" for (i = 0; i < n; i++)", body.index("to_merge = PyMem_New")):] assert tail.split() == expected.split(), ( "preserve result allocation after merge-array population and all subsequent control flow" ) assert body.count("PyList_New(1)") == 1, "allocate the slow-path result exactly once" assert "PyObject *result = PyTuple_New(k + 1);" in body, ( "preserve the separate fast-path result initialization" ) print("mro_implementation result C89 declaration and unchanged allocation order: PASS")