#!/usr/bin/env python3 """Regress type_new_copy_slots C89 declarations and assignment timing.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic PyObject\*\ntype_new_copy_slots\(type_new_ctx \*ctx, PyObject \*dict\)\n\{(.*?)\n\}", source, re.S, ) assert match, "type_new_copy_slots definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.match( r"\s*PyObject \*slots = ctx->slots;" r"\s*Py_ssize_t nslot = ctx->nslot;" r"\s*Py_ssize_t new_nslot = nslot - ctx->add_dict - ctx->add_weak;" r"\s*PyObject \*new_slots = PyList_New\(new_nslot\);" r"\s*Py_ssize_t j;\s*Py_ssize_t i;\s*PyObject \*tuple;\s*if\b", body, ), "type_new_copy_slots: j, i, tuple must be in the opening declaration block (C89)" assert re.search( r"if \(new_slots == NULL\)\s*\{\s*return NULL;\s*\}" r"\s*j = 0;\s*for \(i = 0; i < nslot; i\+\+\)\s*\{" r"\s*PyObject \*slot = PyTuple_GET_ITEM\(slots, i\);\s*int r;\s*if\b", body, ), "preserve allocation check, index initialization and loop; declare r before statements" assert re.search( r"slot =_Py_Mangle\(ctx->name, slot\);" r"\s*if \(!slot\)\s*\{\s*goto error;\s*\}" r"\s*PyList_SET_ITEM\(new_slots, j, slot\);" r"\s*r = PyDict_Contains\(dict, slot\);\s*if \(r < 0\)", body, ), "preserve mangling, ownership transfer, dict lookup and error-check order" assert re.search( r"j\+\+;\s*\}\s*assert\(j == new_nslot\);" r"\s*if \(PyList_Sort\(new_slots\) == -1\)\s*\{\s*goto error;\s*\}" r"\s*tuple = PyList_AsTuple\(new_slots\);\s*Py_DECREF\(new_slots\);" r"\s*if \(tuple == NULL\)", body, ), "preserve index increment, sorting, tuple conversion and cleanup order" for declaration in (r"Py_ssize_t j\b", r"Py_ssize_t i\b", r"int r\b", r"PyObject \*tuple\b"): assert len(re.findall(declaration, body)) == 1, "declare each moved local exactly once" print("type_new_copy_slots C89 declarations and assignment timing: PASS")