#!/usr/bin/env python3 """Check custom_mro's C89 declaration and lookup/decref evaluation order.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() start = source.index("type_mro_modified(PyTypeObject *type, PyObject *bases) {") end = source.index("\n}", start) function = source[start:end] start = function.index(" if (custom) {") end = function.index("\n n = PyTuple_GET_SIZE(bases);", start) block = function[start:end] assert re.match( r"if \(custom\) \{\s*PyObject \*mro_meth, \*type_mro_meth;\s*" r"int custom_mro;\s*mro_meth =", block.strip(), ), "custom_mro must be declared in the opening declaration block" assert len(re.findall(r"\bint\s+custom_mro\b", function)) == 1, ( "declare custom_mro exactly once" ) expected = """ if (custom) { PyObject *mro_meth, *type_mro_meth; int custom_mro; mro_meth = lookup_maybe_method( (PyObject *)type, &_Py_ID(mro), &unbound); if (mro_meth == NULL) { goto clear; } type_mro_meth = lookup_maybe_method( (PyObject *)&PyType_Type, &_Py_ID(mro), &unbound); if (type_mro_meth == NULL) { Py_DECREF(mro_meth); goto clear; } custom_mro = (mro_meth != type_mro_meth); Py_DECREF(mro_meth); Py_DECREF(type_mro_meth); if (custom_mro) { goto clear; } } """ assert block.split() == expected.split(), ( "preserve both successful lookups before custom_mro assignment, " "then decrefs and conditional, including lookup failure handling" ) print("type_mro_modified custom_mro C89 declaration and evaluation order: PASS")