Files
Python3-Win9x/scripts/vc6-typeobject-static-initializers-c89.test.py

69 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Check only the two static type initializer mappings at the VC6 frontier."""
import re
from pathlib import Path
source = Path('cpython/Objects/typeobject.c').read_text()
header = Path('cpython/Include/cpython/object.h').read_text()
def uncomment(text):
return re.sub(r'/\*.*?\*/|//[^\n]*', '', text, flags=re.S)
def check(name, declaration, layout, prefix, expected, head=''):
match = re.search(declaration + ' ' + name + r' = \{(.*?)\n\};', source, re.S)
assert match, name + ': initializer missing'
body = uncomment(match.group(1)).strip()
assert not re.search(r'\.\w+\s*=', body), name + ': designated initializer is not VC6/C89 compatible'
if head:
assert body.startswith(head), name + ': object header changed'
body = body[len(head):]
fields = re.findall(r'\b(' + prefix + r'\w+)\b', uncomment(layout))
assert len(fields) == len(set(fields)) == (36 if prefix == 'nb_' else 48)
# Split only at top-level commas (offsetof arguments contain commas).
values = [v.strip() for v in re.split(r',\s*(?![^()]*\))', body) if v.strip()]
assert len(values) <= len(fields), name + ': excess initializer entries'
values += ['0'] * (len(fields) - len(values))
normalize = lambda value: re.sub(r'\s+', '', value)
actual = dict(zip(fields, map(normalize, values)))
wanted = {field: normalize(expected.get(field, '0')) for field in fields}
assert expected.keys() <= actual.keys(), name + ': expected fields absent from header'
assert actual == wanted, name + ': field mappings changed: ' + repr({k: (actual[k], wanted[k]) for k in fields if actual[k] != wanted[k]})
print(f'{name}: C89 positional structure and all {len(fields)} field mappings PASS')
number_layout = re.search(r'typedef struct \{([^{}]*)\} PyNumberMethods;', header, re.S)
assert number_layout, 'PyNumberMethods layout missing'
check('type_as_number', 'static PyNumberMethods', number_layout.group(1), 'nb_', {
'nb_or': '_Py_union_type_or',
})
type_layout = re.search(r'struct _typeobject \{(.*?)\n\};', header, re.S)
assert type_layout, 'PyTypeObject layout missing'
check('PyType_Type', 'PyTypeObject', type_layout.group(1), 'tp_', {
'tp_name': '"type"',
'tp_basicsize': 'sizeof(PyHeapTypeObject)',
'tp_itemsize': 'sizeof(PyMemberDef)',
'tp_dealloc': '(destructor)type_dealloc',
'tp_vectorcall_offset': 'offsetof(PyTypeObject, tp_vectorcall)',
'tp_repr': '(reprfunc)type_repr',
'tp_as_number': '&type_as_number',
'tp_call': '(ternaryfunc)type_call',
'tp_getattro': '(getattrofunc)type_getattro',
'tp_setattro': '(setattrofunc)type_setattro',
'tp_flags': 'Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS | Py_TPFLAGS_HAVE_VECTORCALL',
'tp_doc': 'type_doc',
'tp_traverse': '(traverseproc)type_traverse',
'tp_clear': '(inquiry)type_clear',
'tp_weaklistoffset': 'offsetof(PyTypeObject, tp_weaklist)',
'tp_methods': 'type_methods',
'tp_members': 'type_members',
'tp_getset': 'type_getsets',
'tp_dictoffset': 'offsetof(PyTypeObject, tp_dict)',
'tp_init': 'type_init',
'tp_new': 'type_new',
'tp_free': 'PyObject_GC_Del',
'tp_is_gc': '(inquiry)type_is_gc',
'tp_vectorcall': 'type_vectorcall',
}, 'PyVarObject_HEAD_INIT(&PyType_Type, 0)')