53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Statically check type_new ctx declaration and post-parse initialization."""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
source = Path("cpython/Objects/typeobject.c").read_text()
|
|
function = re.search(
|
|
r"\ntype_new\(PyTypeObject \*metatype, PyObject \*args, PyObject \*kwds\)\n\{(.*?)\n\}",
|
|
source, re.S,
|
|
)
|
|
assert function, "type_new definition must exist"
|
|
body = function.group(1)
|
|
opening = re.match(
|
|
r"\s*PyObject \*name, \*bases, \*orig_dict;\s*type_new_ctx ctx;\s*"
|
|
r"PyObject \*type;\s*int res;\s*(?=assert\()",
|
|
body,
|
|
)
|
|
assert opening, "type_new: type_new_ctx ctx must be in the opening declaration block"
|
|
assert len(re.findall(r"\btype_new_ctx\s+ctx\b", body)) == 1
|
|
expected_setup = """
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
ctx.metatype = metatype;
|
|
ctx.args = args;
|
|
ctx.kwds = kwds;
|
|
ctx.orig_dict = orig_dict;
|
|
ctx.name = name;
|
|
ctx.bases = bases;
|
|
"""
|
|
expected_parse = """
|
|
assert(args != NULL && PyTuple_Check(args));
|
|
assert(kwds == NULL || PyDict_Check(kwds));
|
|
|
|
/* Parse arguments: (name, bases, dict) */
|
|
if (!PyArg_ParseTuple(args, "UO!O!:type.__new__",
|
|
&name,
|
|
&PyTuple_Type, &bases,
|
|
&PyDict_Type, &orig_dict))
|
|
{
|
|
return NULL;
|
|
}
|
|
"""
|
|
prefix, separator, tail = body[opening.end():].partition("type = NULL;")
|
|
assert separator, "base-resolution frontier must remain"
|
|
assert prefix.split() == (expected_parse + expected_setup).split(), (
|
|
"preserve parsing and failure return, then zero all ctx fields and assign inputs"
|
|
)
|
|
assert tail.lstrip().startswith("res = type_new_get_bases(&ctx, &type);"), (
|
|
"base resolution must still follow ctx setup"
|
|
)
|
|
assert "(type_new_ctx)" not in body, "no C99 compound literal"
|
|
assert "# include <string.h>" in Path("cpython/Include/Python.h").read_text()
|
|
print("type_new ctx opening declaration and post-parse zero/field setup: PASS (static only)")
|