#!/usr/bin/env python3 """Compile the exact picklebuf_raw body as C89 and check its error 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 \*\npicklebuf_raw\([^\n]+\)\n\{.*?\n\}", source, re.S ) assert match, "picklebuf_raw not found" harness = r''' #include #include #include typedef struct { int unused; } PyObject; typedef struct { PyObject *obj; long *suboffsets; const char *format; int ndim; long itemsize, len; long *shape, *strides; } Py_buffer; typedef struct { Py_buffer view; } PyPickleBufferObject; typedef struct { Py_buffer view; int flags; } PyMemoryViewObject; #define Py_UNUSED(name) name #define _Py_MEMORYVIEW_C 1 #define _Py_MEMORYVIEW_FORTRAN 2 static int PyExc_ValueError = 1, PyExc_BufferError = 2; static int scenario; static char events[16]; static PyPickleBufferObject self; static PyMemoryViewObject memory; static PyObject base; static void event(char c) { size_t n = strlen(events); assert(n + 1 < sizeof(events)); events[n] = c; events[n + 1] = '\0'; } static void PyErr_SetString(int exc, const char *message) { assert(exc == (scenario == 0 ? PyExc_ValueError : PyExc_BufferError)); assert(strcmp(message, scenario == 0 ? "operation forbidden on released PickleBuffer object" : "cannot extract raw buffer from non-contiguous buffer") == 0); event('E'); } static int PyBuffer_IsContiguous(Py_buffer *view, char order) { assert(view == &self.view && order == 'A'); event('C'); return scenario != 2; } static PyObject *PyMemoryView_FromObject(PyObject *obj) { assert(obj == (PyObject *)&self); event('M'); return scenario == 3 ? NULL : (PyObject *)&memory; } ''' harness += match.group(0) harness += r''' int main(void) { static const char *expected[] = {"E", "E", "CE", "CM", "CM"}; PyObject *result; PyMemoryViewObject before; Py_buffer original; long offset = 0; for (scenario = 0; scenario < 5; ++scenario) { memset(&memory, 0, sizeof(memory)); memory.view.obj = &base; memory.view.format = "i"; memory.view.ndim = 2; memory.view.itemsize = 4; memory.view.len = 24; memory.flags = 8; memcpy(&before, &memory, sizeof(memory)); self.view = memory.view; self.view.obj = scenario == 0 ? NULL : &base; self.view.suboffsets = scenario == 1 ? &offset : NULL; memcpy(&original, &self.view, sizeof(original)); events[0] = '\0'; result = picklebuf_raw(&self, NULL); assert(strcmp(events, expected[scenario]) == 0); assert(memcmp(&original, &self.view, sizeof(original)) == 0); if (scenario < 4) { assert(result == NULL); assert(memcmp(&before, &memory, sizeof(memory)) == 0); } else { assert(result == (PyObject *)&memory); assert(memory.view.obj == &base && memory.view.len == 24); assert(memory.view.suboffsets == NULL); assert(strcmp(memory.view.format, "B") == 0); assert(memory.view.ndim == 1 && memory.view.itemsize == 1); assert(memory.view.shape == &memory.view.len); assert(memory.view.strides == &memory.view.itemsize); assert(memory.flags == (_Py_MEMORYVIEW_C | _Py_MEMORYVIEW_FORTRAN)); } } return 0; } ''' with tempfile.TemporaryDirectory(prefix="vc6-picklebuf-raw-") 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("picklebuf_raw C89 compile and validation/allocation order: PASS (5 cases)")