#!/usr/bin/env python3 """Compile update_arg's exact keys branch as C89; check evaluation/cleanup order.""" import re import subprocess import sys import tempfile from pathlib import Path source = Path(sys.argv[1]).read_text() function = source.split("mutablemapping_update_arg(PyObject *self, PyObject *arg)\n{", 1)[1] match = re.search( r" if \(func != NULL\) \{.*?(?= if \(_PyObject_LookupAttr\(arg, &_Py_ID\(items\))", function, re.S, ) assert match, "update_arg keys branch not found" harness = r''' #include #include #include typedef struct { int unused; } PyObject; static PyObject self_obj, arg_obj, func_obj, keys_obj, iter_obj, key_obj, value_obj; static int scenario, calls, error; static char events[64]; static void event(char c) { size_t n = strlen(events); assert(n + 1 < sizeof(events)); events[n] = c; events[n + 1] = '\0'; } static PyObject * _PyObject_CallNoArgs(PyObject *obj) { assert(obj == &func_obj); event('C'); return scenario == 0 ? NULL : &keys_obj; } static void Py_DECREF(PyObject *obj) { if (obj == &func_obj) event('f'); else if (obj == &keys_obj) event('k'); else if (obj == &iter_obj) event('i'); else if (obj == &key_obj) event('q'); else { assert(obj == &value_obj); event('v'); } } static PyObject *PyObject_GetIter(PyObject *obj) { assert(obj == &keys_obj); event('I'); return scenario == 1 ? NULL : &iter_obj; } static PyObject *PyIter_Next(PyObject *obj) { assert(obj == &iter_obj); event('N'); if (scenario == 2 || scenario == 3 || calls++ > 0) { error = scenario == 3 || scenario == 7; return NULL; } return &key_obj; } static PyObject *PyObject_GetItem(PyObject *obj, PyObject *key) { assert(obj == &arg_obj && key == &key_obj); event('G'); return scenario == 4 ? NULL : &value_obj; } static int PyObject_SetItem(PyObject *obj, PyObject *key, PyObject *value) { assert(obj == &self_obj && key == &key_obj && value == &value_obj); event('S'); return scenario == 5 ? -1 : 0; } static int PyErr_Occurred(void) { event('E'); return error; } static int run(PyObject *self, PyObject *arg, PyObject *func) { int res = 0; ''' harness += match.group(0) harness += r''' return 42; } int main(void) { static const char *expected[] = { "Cf", "CfIk", "CfIkNiE", "CfIkNiE", "CfIkNGqi", "CfIkNGSvqi", "CfIkNGSvqNiE", "CfIkNGSvqNiE" }; int result; for (scenario = 0; scenario < 8; ++scenario) { events[0] = '\0'; calls = error = 0; result = run(&self_obj, &arg_obj, &func_obj); assert(result == (scenario == 2 || scenario == 6 ? 0 : -1)); assert(strcmp(events, expected[scenario]) == 0); } return 0; } ''' with tempfile.TemporaryDirectory(prefix="vc6-odict-update-arg-iterator-") 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("update_arg keys branch C89 compile and evaluation/cleanup order: PASS (8 cases)")