99 lines
3.6 KiB
Python
99 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile the exact update body as C89 and check evaluation/dispatch 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 \*\nmutablemapping_update\(PyObject \*self, PyObject \*args, PyObject \*kwargs\)\n\{.*?\n\}",
|
|
source, re.S,
|
|
)
|
|
assert match, "mutablemapping_update not found"
|
|
harness = r'''
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
typedef long Py_ssize_t;
|
|
typedef struct { int unused; } PyObject;
|
|
static PyObject self_obj, args_obj, kwargs_obj, other_obj, items_obj, none_obj;
|
|
static int scenario, PyExc_TypeError;
|
|
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 int PyTuple_Check(PyObject *obj) {
|
|
assert(obj == &args_obj); event('T'); return 1;
|
|
}
|
|
static Py_ssize_t PyTuple_GET_SIZE(PyObject *obj) {
|
|
assert(obj == &args_obj && strcmp(events, "T") == 0);
|
|
event('L'); return scenario == 2 ? 2 : scenario == 1 ? 0 : 1;
|
|
}
|
|
static void PyErr_Format(int exc, const char *msg, Py_ssize_t len) {
|
|
assert(exc == PyExc_TypeError && len == 2);
|
|
assert(strcmp(msg, "update() takes at most 1 positional argument (%zd given)") == 0);
|
|
event('E');
|
|
}
|
|
static PyObject *PyTuple_GET_ITEM(PyObject *obj, int index) {
|
|
assert(obj == &args_obj && index == 0); event('G'); return &other_obj;
|
|
}
|
|
static void Py_INCREF(PyObject *obj) { assert(obj == &other_obj); event('I'); }
|
|
static void Py_DECREF(PyObject *obj) {
|
|
assert(obj == &other_obj || obj == &items_obj);
|
|
event(obj == &other_obj ? 'D' : 'd');
|
|
}
|
|
static int mutablemapping_update_arg(PyObject *self, PyObject *other) {
|
|
assert(self == &self_obj && other == &other_obj); event('U');
|
|
return scenario == 3 ? -1 : 0;
|
|
}
|
|
static int PyDict_Check(PyObject *obj) {
|
|
assert(obj == &kwargs_obj); event('K'); return 1;
|
|
}
|
|
static int PyDict_GET_SIZE(PyObject *obj) {
|
|
assert(obj == &kwargs_obj); event('S'); return scenario != 5;
|
|
}
|
|
static PyObject *PyDict_Items(PyObject *obj) {
|
|
assert(obj == &kwargs_obj); event('M'); return scenario == 6 ? NULL : &items_obj;
|
|
}
|
|
static int mutablemapping_add_pairs(PyObject *self, PyObject *items) {
|
|
assert(self == &self_obj && items == &items_obj); event('A');
|
|
return scenario == 7 ? -1 : 0;
|
|
}
|
|
#define Py_RETURN_NONE return &none_obj
|
|
'''
|
|
harness += match.group(0)
|
|
harness += r'''
|
|
int main(void) {
|
|
static const char *expected[] = {
|
|
"", "TL", "TLE", "TLGIUD", "TLGIUD", "TLGIUDKS",
|
|
"TLGIUDKSM", "TLGIUDKSMAd", "TLGIUDKSMAd", "KSMAd"
|
|
};
|
|
PyObject *result;
|
|
for (scenario = 0; scenario < 10; ++scenario) {
|
|
events[0] = '\0';
|
|
result = mutablemapping_update(&self_obj,
|
|
scenario == 0 || scenario == 9 ? NULL : &args_obj,
|
|
scenario >= 5 ? &kwargs_obj : NULL);
|
|
assert(result == (scenario == 2 || scenario == 3 || scenario == 6 ||
|
|
scenario == 7 ? NULL : &none_obj));
|
|
assert(strcmp(events, expected[scenario]) == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix="vc6-odict-update-len-") 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("mutablemapping_update C89 compile and evaluation/dispatch order: PASS (10 cases)")
|