#!/usr/bin/env python3 """Keep the type-spec name buffer length C89-safe at its evaluation point.""" 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"\bPy_ssize_t name_buf_len;", opening), ( "name_buf_len must be declared in the opening declaration block (C89), before statements" ) assert len(re.findall(r"\bPy_ssize_t\s+name_buf_len\b", body)) == 1, ( "declare name_buf_len exactly once" ) assert len(re.findall(r"\bname_buf_len\s*=", body)) == 1, ( "assign name_buf_len exactly once" ) expected = """ res->ht_name = PyUnicode_FromString(s); if (!res->ht_name) { goto fail; } res->ht_qualname = Py_NewRef(res->ht_name); name_buf_len = strlen(spec->name) + 1; res->_ht_tpname = PyMem_Malloc(name_buf_len); if (res->_ht_tpname == NULL) { goto fail; } type->tp_name = memcpy(res->_ht_tpname, spec->name, name_buf_len); res->ht_module = Py_XNewRef(module); """ actual = body[body.index(" res->ht_name = PyUnicode_FromString(s);"): body.index(" if (!bases)")] assert actual.split() == expected.split(), ( "preserve name creation, length evaluation timing, allocation, failure handling, and copy" ) print("PyType_FromModuleAndSpec name_buf_len C89 declaration and same-point initialization: PASS")