#!/usr/bin/env python3 """Verify legacy MSVC gets runtime-assigned classobject type fields.""" import sys from pathlib import Path if len(sys.argv) != 3: raise SystemExit("usage: vc6-classobject-typeinit.test.py classobject.c object.c") class_source = Path(sys.argv[1]).read_text() object_source = Path(sys.argv[2]).read_text() legacy_condition = "!defined(MS_WINDOWS) || !defined(_MSC_VER) || _MSC_VER >= 1300" for symbol in ("PyMethod_Type", "PyInstanceMethod_Type"): start = class_source.index("PyTypeObject " + symbol + " = {") end = class_source.index("\n};", start) initializer = class_source[start:end] if "#if " + legacy_condition not in initializer: raise SystemExit(symbol + " designated initializer must be excluded for VC6 Windows") start = object_source.index("_PyTypes_InitTypes(PyInterpreterState *interp)") body_start = object_source.index("\n{\n", start) + 2 body_end = object_source.index("\n}\n", body_start) body = object_source[body_start:body_end] ready = body.index("PyType_Ready(type)") call = body.index("_PyClassObject_InitTypes();") if call > ready: raise SystemExit("legacy type fields must be initialized before PyType_Ready") helper = class_source.index("_PyClassObject_InitTypes(void)") helper_body_start = class_source.index("\n{\n", helper) + 2 helper_body_end = class_source.index("\n}\n", helper_body_start) helper_body = class_source[helper_body_start:helper_body_end] assignments = { "PyMethod_Type": ( "tp_name", "tp_basicsize", "tp_dealloc", "tp_vectorcall_offset", "tp_repr", "tp_hash", "tp_call", "tp_getattro", "tp_setattro", "tp_flags", "tp_doc", "tp_traverse", "tp_richcompare", "tp_weaklistoffset", "tp_methods", "tp_members", "tp_getset", "tp_new", ), "PyInstanceMethod_Type": ( "tp_name", "tp_basicsize", "tp_dealloc", "tp_repr", "tp_call", "tp_getattro", "tp_setattro", "tp_flags", "tp_doc", "tp_traverse", "tp_richcompare", "tp_members", "tp_getset", "tp_descr_get", "tp_new", ), } for symbol, fields in assignments.items(): for field in fields: assignment = symbol + "." + field + " =" if assignment not in helper_body: raise SystemExit("missing legacy setup assignment " + assignment) print("classobject VC6 type initialization coverage: PASS")