#!/usr/bin/env python3 """Compile the exact range_index body as C89 and check evaluation order.""" import re import subprocess import sys import tempfile from pathlib import Path source = Path(sys.argv[1]).read_text() match = re.search( r"static PyObject \*\nrange_index\([^\n]+\)\n\{.*?\n\}", source, re.S ) assert match, "range_index not found" harness = r''' #include #include #include typedef long Py_ssize_t; typedef struct { int unused; } PyObject; typedef struct { PyObject *start, *step; } rangeobject; #define PY_ITERSEARCH_INDEX 2 static int PyExc_ValueError = 1; static int scenario; static char events[16]; static PyObject ob, start, step, one, idx, quotient, converted; static rangeobject range; static void event(char c) { size_t n = strlen(events); assert(n + 1 < sizeof(events)); events[n] = c; events[n + 1] = '\0'; } static int PyLong_CheckExact(PyObject *obj) { assert(obj == &ob); return scenario != 0 && scenario != 1 && scenario != 8; } static int PyBool_Check(PyObject *obj) { assert(obj == &ob); return scenario == 8; } static Py_ssize_t _PySequence_IterSearch(PyObject *obj, PyObject *value, int op) { assert(obj == (PyObject *)&range && value == &ob && op == PY_ITERSEARCH_INDEX); event('I'); return scenario == 0 ? -1 : 3; } static PyObject *PyLong_FromSsize_t(Py_ssize_t value) { assert(value == 3); event('L'); return &converted; } static int range_contains_long(rangeobject *r, PyObject *value) { assert(r == &range && value == &ob); event('C'); return scenario == 2 ? -1 : scenario != 3; } static PyObject *PyNumber_Subtract(PyObject *value, PyObject *first) { assert(value == &ob && first == &start); event('S'); return scenario == 4 ? NULL : &idx; } static PyObject *_PyLong_GetOne(void) { event('O'); return &one; } static PyObject *PyNumber_FloorDivide(PyObject *value, PyObject *divisor) { assert(value == &idx && divisor == &step); event('F'); return scenario == 6 ? NULL : "ient; } static void Py_DECREF(PyObject *value) { assert(value == &idx); event('D'); } static void PyErr_Format(int exc, const char *format, PyObject *value) { assert(exc == PyExc_ValueError && value == &ob); assert(strcmp(format, "%R is not in range") == 0); event('E'); } ''' harness += match.group(0) harness += r''' int main(void) { static const char *expected[] = { "I", "IL", "C", "CE", "CS", "CSO", "CSOFD", "CSOFD", "CSOFD" }; PyObject *result; for (scenario = 0; scenario < 9; ++scenario) { range.start = &start; range.step = scenario == 5 ? &one : &step; events[0] = '\0'; result = range_index(&range, &ob); assert(strcmp(events, expected[scenario]) == 0); if (scenario == 1) assert(result == &converted); else if (scenario == 5) assert(result == &idx); else if (scenario == 7 || scenario == 8) assert(result == "ient); else assert(result == NULL); } return 0; } ''' with tempfile.TemporaryDirectory(prefix="vc6-rangeobject-index-") as directory: path = Path(directory) (path / "test.c").write_text(harness) result = subprocess.run([ "cc", "-std=c89", "-pedantic-errors", "-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter", str(path / "test.c"), "-o", str(path / "test"), ]) if result.returncode: sys.exit(result.returncode) subprocess.run([str(path / "test")], check=True) print("range_index C89 compile and evaluation/cleanup order: PASS (9 cases)")