#!/usr/bin/env python3 """Keep find_name_in_mro mro/res C89-safe without changing lookup timing.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nfind_name_in_mro\(PyTypeObject \*type, PyObject \*name, int \*error\)\n" r"\{(.*?)\n\}", source, re.S, ) assert match, "find_name_in_mro definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.match(r"\s*Py_hash_t hash;\s*PyObject \*mro;\s*PyObject \*res;\s*Py_ssize_t n;\s*(?:Py_ssize_t i;\s*)?if", body), ( "find_name_in_mro: mro and res must be declared at function entry (C89)" ) assert not re.search(r"for\s*\(\s*Py_ssize_t\s+i\b", body), ( "find_name_in_mro: i remains declared in the for initializer (C89)" ) expected = """ Py_hash_t hash; PyObject *mro; PyObject *res; Py_ssize_t n; Py_ssize_t i; if (!PyUnicode_CheckExact(name) || (hash = _PyASCIIObject_CAST(name)->hash) == -1) { hash = PyObject_Hash(name); if (hash == -1) { *error = -1; return NULL; } } mro = type->tp_mro; if (mro == NULL) { if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) { if (PyType_Ready(type) < 0) { *error = -1; return NULL; } mro = type->tp_mro; } if (mro == NULL) { *error = 1; return NULL; } } res = NULL; Py_INCREF(mro); n = PyTuple_GET_SIZE(mro); for (i = 0; i < n; i++) { PyObject *base = PyTuple_GET_ITEM(mro, i); PyObject *dict = _PyType_CAST(base)->tp_dict; assert(dict && PyDict_Check(dict)); res = _PyDict_GetItem_KnownHash(dict, name, hash); if (res != NULL) { break; } if (PyErr_Occurred()) { *error = -1; goto done; } } *error = 0; done: Py_DECREF(mro); return res;""" assert body.split() == expected.split(), ( "find_name_in_mro: preserve hash/readiness checks, mro/res assignment timing, " "reference handling, lookup, errors, and opening n/i declarations" ) print("find_name_in_mro mro/res C89 declarations and same-point assignments: PASS")