76 lines
3.3 KiB
Python
76 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Check NotImplemented's positional number slots against the CPython header."""
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1]).read_text()
|
|
header = Path(sys.argv[2]).read_text()
|
|
layout = re.search(r"typedef struct \{([^{}]*)\} PyNumberMethods;", header, re.S)
|
|
assert layout, "PyNumberMethods definition not found"
|
|
fields = re.findall(r"\b(nb_\w+)\s*;", layout.group(1))
|
|
assert len(fields) == 36 and fields.index("nb_bool") == 9, (
|
|
"expected 36 PyNumberMethods slots with nb_bool in slot 10"
|
|
)
|
|
initializer = re.search(
|
|
r"static PyNumberMethods notimplemented_as_number\s*=\s*\{([^{}]*)\};",
|
|
source,
|
|
re.S,
|
|
)
|
|
assert initializer, "notimplemented_as_number initializer not found"
|
|
body = re.sub(r"/\*.*?\*/", "", initializer.group(1), flags=re.S)
|
|
assert not re.search(r"\.\s*\w+\s*=", body), (
|
|
"notimplemented_as_number must use a VC6-compatible positional initializer"
|
|
)
|
|
slots = [value.strip() for value in body.strip().rstrip(",").split(",")]
|
|
assert len(slots) == len(fields), "every PyNumberMethods slot must be explicit"
|
|
for field, value in zip(fields, slots):
|
|
expected = "notimplemented_bool" if field == "nb_bool" else "0"
|
|
assert value == expected, f"{field}: expected {expected}, got {value}"
|
|
print("object NotImplemented C89 positional initializer: PASS (36 slots; nb_bool slot 10)")
|
|
|
|
start = source.index("_PyTypes_InitTypes(PyInterpreterState *interp)\n{")
|
|
opening = source.index("{", start)
|
|
end = source.index("\n}\n", opening)
|
|
init_types = source[opening:end]
|
|
assert re.match(r"\{\s*size_t i;\s*if \(!_Py_IsMainInterpreter\(interp\)\)", init_types), (
|
|
"_PyTypes_InitTypes must declare size_t i before the interpreter guard"
|
|
)
|
|
assert len(re.findall(r"\bsize_t\s+i\b", init_types)) == 1, (
|
|
"_PyTypes_InitTypes must declare its loop counter exactly once"
|
|
)
|
|
assert "for (i=0; i < Py_ARRAY_LENGTH(static_types); i++) {" in init_types, (
|
|
"_PyTypes_InitTypes must use the C89 counter with unchanged loop bounds/traversal"
|
|
)
|
|
assert init_types.index("_Py_GenericAlias_InitTypes();") < init_types.index("for (i=0;"), (
|
|
"_PyTypes_InitTypes loop initialization must remain after VC6 type initialization"
|
|
)
|
|
print("object _PyTypes_InitTypes C89 loop counter declaration and initialization order: PASS")
|
|
|
|
start = source.index("_PyTypes_FiniTypes(PyInterpreterState *interp)\n{")
|
|
opening = source.index("{", start)
|
|
end = source.index("\n}\n", opening)
|
|
fini_types = source[opening:end]
|
|
assert re.match(r"\{\s*Py_ssize_t i;\s*if \(!_Py_IsMainInterpreter\(interp\)\)", fini_types), (
|
|
"_PyTypes_FiniTypes must declare Py_ssize_t i before the interpreter guard"
|
|
)
|
|
assert len(re.findall(r"\bPy_ssize_t\s+i\b", fini_types)) == 1, (
|
|
"_PyTypes_FiniTypes must declare its signed loop counter exactly once"
|
|
)
|
|
expected = '''{
|
|
Py_ssize_t i;
|
|
if (!_Py_IsMainInterpreter(interp)) {
|
|
return;
|
|
}
|
|
for (i=Py_ARRAY_LENGTH(static_types)-1; i>=0; i--) {
|
|
PyTypeObject *type = static_types[i];
|
|
_PyStaticType_Dealloc(type);
|
|
}
|
|
'''
|
|
assert re.sub(r"//[^\n]*", "", fini_types).split() == expected.split(), (
|
|
"_PyTypes_FiniTypes must preserve the interpreter guard, reverse loop bounds/traversal, "
|
|
"and block-opening type lookup before each deallocation"
|
|
)
|
|
print("object _PyTypes_FiniTypes C89 loop counter and reverse per-item deallocation order: PASS")
|