#!/usr/bin/env python3 """Compile the exact odict number table as C89 and check every named slot.""" import re import subprocess import sys import tempfile from pathlib import Path source_path = Path(sys.argv[1]) source = source_path.read_text() match = re.search(r"static PyNumberMethods odict_as_number = \{.*?\n\};", source, re.S) assert match, "odict_as_number initializer not found" header = (source_path.parent.parent / "Include/cpython/object.h").read_text() layout = re.search(r"typedef struct \{([^{}]*)\} PyNumberMethods;", header, re.S) assert layout, "PyNumberMethods layout not found" # Use the actual header's order and types; check slots by name, not position. body = re.sub(r"/\*.*?\*/", "", layout.group(1), flags=re.S) fields = re.findall(r"\b(nb_\w+)\s*;", body) assert len(fields) == 36 and len(set(fields)) == 36 harness = r''' #include typedef struct { int value; } PyObject; typedef PyObject *(*binaryfunc)(PyObject *, PyObject *); typedef PyObject *(*ternaryfunc)(PyObject *, PyObject *, PyObject *); typedef PyObject *(*unaryfunc)(PyObject *); typedef int (*inquiry)(PyObject *); static PyObject *odict_or(PyObject *a, PyObject *b) { (void)b; return a; } static PyObject *odict_inplace_or(PyObject *a, PyObject *b) { (void)a; return b; } ''' harness += "typedef struct {" + body + "} PyNumberMethods;\n" harness += match.group(0) + "\nint main(void) {\n" for field in fields: expected = {"nb_or": "odict_or", "nb_inplace_or": "odict_inplace_or"}.get(field, "0") harness += f" assert(odict_as_number.{field} == {expected});\n" harness += " return 0;\n}\n" with tempfile.TemporaryDirectory(prefix="vc6-odict-number-") as directory: path = Path(directory) (path / "test.c").write_text(harness) result = subprocess.run([ "cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror", str(path / "test.c"), "-o", str(path / "test"), ]) if result.returncode: sys.exit(result.returncode) subprocess.run([str(path / "test")], check=True) print("odict_as_number C89 compile and all 36 semantic field mappings: PASS")