55 lines
2.0 KiB
Python
55 lines
2.0 KiB
Python
#!/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")
|