#!/usr/bin/env python3 """Check type_get_annotations's C89 local and preserve every executable statement.""" import re from pathlib import Path source = Path("cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic PyObject \*\ntype_get_annotations\(PyTypeObject \*type, void \*context\)\n\{(.*?)\n\}", source, re.S, ) assert match, "type_get_annotations definition missing" body = match.group(1) assert re.match(r"\s*PyObject \*annotations;\s*if\b", body), ( "type_get_annotations: annotations must be declared before the heap-type guard" ) assert len(re.findall(r"\bPyObject\s*\*\s*annotations\b", body)) == 1, ( "type_get_annotations: declare annotations exactly once" ) expected = """ PyObject *annotations; if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { PyErr_Format(PyExc_AttributeError, "type object '%s' has no attribute '__annotations__'", type->tp_name); return NULL; } annotations = PyDict_GetItemWithError(type->tp_dict, &_Py_ID(__annotations__)); if (annotations) { if (Py_TYPE(annotations)->tp_descr_get) { annotations = Py_TYPE(annotations)->tp_descr_get( annotations, NULL, (PyObject *)type); } else { Py_INCREF(annotations); } } else if (!PyErr_Occurred()) { annotations = PyDict_New(); if (annotations) { int result = PyDict_SetItem( type->tp_dict, &_Py_ID(__annotations__), annotations); if (result) { Py_CLEAR(annotations); } else { PyType_Modified(type); } } } return annotations; """ assert body.split() == expected.split(), ( "type_get_annotations: preserve guard, lookup, descriptor handling, references, " "dictionary creation, error handling, invalidation, and return in their original order" ) print("type_get_annotations annotations C89 declaration and unchanged executable body: PASS")