#!/usr/bin/env python3 """Check type_new_set_doc's tp_doc declaration and allocation timing.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic int\ntype_new_set_doc\(PyTypeObject \*type\)\n\{(.*?)\n\}", source, re.S, ) assert match, "type_new_set_doc definition missing" body = re.sub(r"/\*.*?\*/|//[^\n]*", "", match.group(1), flags=re.S) assert re.match( r"\s*const char \*doc_str;" r"\s*Py_ssize_t size;" r"\s*char \*tp_doc;" r"\s*PyObject \*doc = PyDict_GetItemWithError" r"\(type->tp_dict, &_Py_ID\(__doc__\)\);", body, ), "type_new_set_doc: tp_doc must be in the opening declaration block (C89)" assert len(re.findall(r"\bchar\s*\*\s*tp_doc\b", body)) == 1, ( "declare tp_doc exactly once" ) assert len(re.findall(r"(?)\btp_doc\s*=(?!=)", body)) == 1, ( "assign local tp_doc exactly once" ) assert len(re.findall(r"\bPyObject_Malloc\s*\(", body)) == 1, ( "allocate exactly once" ) assert re.search( r"size = strlen\(doc_str\) \+ 1;\s*" r"tp_doc = \(char \*\)PyObject_Malloc\(size\);\s*" r"if \(tp_doc == NULL\)", body, ), "preserve allocation after size calculation and before the NULL check" print("type_new_set_doc tp_doc C89 declaration and allocation timing: PASS")