#!/usr/bin/env python3 """Keep the type-spec name pointer C89-safe and initialize it after flags.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nPyObject \*\nPyType_FromModuleAndSpec\(PyObject \*module, PyType_Spec \*spec, PyObject \*bases\)\n" r"\{(.*?)\n\}", source, re.S, ) assert match, "PyType_FromModuleAndSpec definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) opening = body[:body.index(" nmembers = weaklistoffset")] assert re.search(r"\bconst char \*s;", opening), ( "s must be declared in the opening declaration block (C89), before statements" ) assert len(re.findall(r"\bconst char\s*\*s\b", body)) == 1, "declare s exactly once" name_setup = body[body.index(" type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE;"):] name_setup = name_setup[:name_setup.index(" res->ht_name = PyUnicode_FromString(s);")] assert len(re.findall(r"strrchr\(spec->name, '\.'\)", name_setup)) == 1, ( "evaluate name search exactly once in name setup" ) expected = """ if (spec->name == NULL) { PyErr_SetString(PyExc_SystemError, "Type spec does not define the name field."); goto fail; } type = &res->ht_type; type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE; s = strrchr(spec->name, '.'); if (s == NULL) { s = spec->name; } else { s++; } res->ht_name = PyUnicode_FromString(s); """ actual = body[body.index(" if (spec->name == NULL)"): body.index(" if (!res->ht_name)")] assert actual.split() == expected.split(), ( "preserve name validation, flags, search initialization timing, and name selection" ) print("PyType_FromModuleAndSpec s C89 declaration and same-point initialization: PASS")