#!/usr/bin/env python3 """Keep type_add_method locals C89-compatible without moving API calls.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search(r"\nstatic int\ntype_add_method\(PyTypeObject \*type, PyMethodDef \*meth\)\n\{(.*?)\n\}", source, re.S) assert match, "type_add_method definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.match(r"\s*PyObject \*descr;\s*PyObject \*name;\s*int err;\s*int isdescr = 1;\s*if", body), ( "type_add_method: name and err must be in the opening C89 declaration block" ) for declaration in (r"\bPyObject\s*\*\s*name\b", r"\bint\s+err\b"): assert len(re.findall(declaration, body)) == 1, "declare each moved local exactly once" # Search in execution order: duplicate cleanup calls occur on error paths. ordered = [ "if (meth->ml_flags & METH_CLASS)", "if (meth->ml_flags & METH_STATIC)", "descr = PyDescr_NewClassMethod(type, meth);", "else if (meth->ml_flags & METH_STATIC)", "PyObject *cfunc = PyCFunction_NewEx(meth, (PyObject*)type, NULL);", "descr = PyStaticMethod_New(cfunc);", "isdescr = 0;", "Py_DECREF(cfunc);", "descr = PyDescr_NewMethod(type, meth);", "if (descr == NULL)", "if (isdescr)", "name = PyDescr_NAME(descr);", "name = PyUnicode_FromString(meth->ml_name);", "if (name == NULL)", "Py_DECREF(descr);", "if (!(meth->ml_flags & METH_COEXIST))", "err = PyDict_SetDefault(type->tp_dict, name, descr) == NULL;", "err = PyDict_SetItem(type->tp_dict, name, descr) < 0;", "if (!isdescr)", "Py_DECREF(name);", "Py_DECREF(descr);", "if (err)", "return -1;", "return 0;", ] cursor = 0 for token in ordered: position = body.find(token, cursor) assert position >= 0, f"type_add_method: missing or reordered operation: {token}" cursor = position + len(token) assert len(re.findall(r"\bname\s*=(?!=)", body)) == 2, "retain both name assignment branches" assert len(re.findall(r"\berr\s*=(?!=)", body)) == 2, "retain both dictionary insertion branches" print("type_add_method C89 locals, branch ordering and reference cleanup: PASS")