#!/usr/bin/env python3 """Check descriptor dictionary locals and evaluation after the NULL guard.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() failures = [] for function, local_type, local, factory in ( ("type_add_members", "PyMemberDef", "memb", "PyDescr_NewMember"), ("type_add_getset", "PyGetSetDef", "gsp", "PyDescr_NewGetSet"), ): match = re.search(r"\nstatic int\n" + function + r"\(PyTypeObject \*type\)\n\{(.*?)\n\}", source, re.S) assert match, function + " definition missing" body = match.group(1) opening = rf"\s*{local_type} \*{local} = type->tp_\w+;\s*PyObject \*dict;\s*if" if not re.match(opening, body): failures.append(function + ": dict must be in opening C89 declarations") continue assert len(re.findall(r"\bPyObject\s*\*\s*dict\b", body)) == 1 assert len(re.findall(r"\bdict\s*=(?!=)", body)) == 1 guard = f"if ({local} == NULL) {{\n return 0;\n }}" ordered = [guard, "dict = type->tp_dict;", f"for (; {local}->name != NULL; {local}++)", f"PyObject *descr = {factory}(type, {local});", "if (descr == NULL)", "return -1;", "if (PyDict_SetDefault(dict, PyDescr_NAME(descr), descr) == NULL)", "Py_DECREF(descr);", "return -1;", "Py_DECREF(descr);", "return 0;"] cursor = 0 for token in ordered: position = body.find(token, cursor) assert position >= 0, f"{function}: missing/reordered operation: {token}" cursor = position + len(token) print(function + ": C89 dictionary declaration, NULL guard and cleanup: PASS") assert not failures, "; ".join(failures)