#!/usr/bin/env python3 """Check object_set_class's C89 newto declaration and evaluation timing.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic int\nobject_set_class\(PyObject \*self, PyObject \*value, " r"void \*closure\)\n\{(.*?)\n\}", source, re.S, ) assert match, "object_set_class definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.match( r"\s*PyTypeObject \*oldto = Py_TYPE\(self\);\s*PyTypeObject \*newto;\s*if", body, ), "object_set_class: newto must be in the opening declaration block (C89)" assert len(re.findall(r"\bPyTypeObject\s*\*\s*newto\b", body)) == 1, "declare newto exactly once" assert len(re.findall(r"\bnewto\s*=(?!=)", body)) == 1, "assign newto exactly once" expected = """ if (value == NULL) { PyErr_SetString(PyExc_TypeError, "can't delete __class__ attribute"); return -1; } if (!PyType_Check(value)) { PyErr_Format(PyExc_TypeError, "__class__ must be set to a class, not '%s' object", Py_TYPE(value)->tp_name); return -1; } newto = (PyTypeObject *)value; if (PySys_Audit("object.__setattr__", "OsO", self, "__class__", value) < 0) { return -1; } """ assert body[body.index("if (value == NULL)"):body.index("if (!(PyType_IsSubtype")].split() == expected.split(), ( "object_set_class: preserve rejection paths, cast after type validation, " "and audit before compatibility checks" ) print("object_set_class newto C89 declaration and evaluation timing: PASS")