#!/usr/bin/env python3 """Keep lookup_maybe_method's descriptor getter C89-safe without reordering.""" import re import sys from pathlib import Path source = Path(sys.argv[1] if len(sys.argv) > 1 else "cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic PyObject \*\nlookup_maybe_method\(PyObject \*self, PyObject \*attr, int \*unbound\)\n\{(.*?)\n\}", source, re.S, ) assert match, "lookup_maybe_method definition missing" body = match.group(1) assert re.match( r"\s*PyObject \*res = _PyType_Lookup\(Py_TYPE\(self\), attr\);\s*descrgetfunc f;", body, ), "lookup_maybe_method: f must be declared in the opening declaration block (C89)" assert len(re.findall(r"\bdescrgetfunc\s+f\b", body)) == 1, ( "lookup_maybe_method: declare the descriptor getter exactly once" ) expected = """ PyObject *res = _PyType_Lookup(Py_TYPE(self), attr); descrgetfunc f; if (res == NULL) { return NULL; } if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { /* Avoid temporary PyMethodObject */ *unbound = 1; Py_INCREF(res); } else { *unbound = 0; f = Py_TYPE(res)->tp_descr_get; if (f == NULL) { Py_INCREF(res); } else { res = f(res, self, (PyObject *)(Py_TYPE(self))); } } return res; """ assert body.split() == expected.split(), ( "lookup_maybe_method: preserve getter evaluation after *unbound = 0 and all control flow" ) print("lookup_maybe_method descriptor getter C89 declaration and unchanged evaluation/control flow: PASS")