#!/usr/bin/env python3 """Check the mro_entries declaration and preserve base lookup ordering.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic int\ntype_new_get_bases\(type_new_ctx \*ctx, PyObject \*\*type\)\n\{(.*?)\n\}", source, re.S, ) assert match, "type_new_get_bases definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.search( r"for \(i = 0; i < nbases; i\+\+\) \{\s*" r"PyObject \*base = PyTuple_GET_ITEM\(ctx->bases, i\);\s*" r"PyObject \*mro_entries;\s*if \(PyType_Check\(base\)\)", body, ), "type_new_get_bases: mro_entries must precede loop statements (C89)" assert len(re.findall(r"\bPyObject\s*\*\s*mro_entries\b", body)) == 1, ( "declare mro_entries exactly once" ) loop = body[body.index(" for ("):body.index(" winner = _PyType_CalculateMetaclass(")] expected = """ for (i = 0; i < nbases; i++) { PyObject *base = PyTuple_GET_ITEM(ctx->bases, i); PyObject *mro_entries; if (PyType_Check(base)) { continue; } if (_PyObject_LookupAttr(base, &_Py_ID(__mro_entries__), &mro_entries) < 0) { return -1; } if (mro_entries != NULL) { PyErr_SetString(PyExc_TypeError, "type() doesn't support MRO entry resolution; " "use types.new_class()"); Py_DECREF(mro_entries); return -1; } } """ assert loop.split() == expected.split(), ( "preserve base lookup, type skip, attribute lookup, error handling, and decref order" ) print("type_new_get_bases mro_entries C89 declaration and lookup ordering: PASS")