#!/usr/bin/env python3 """Check C89 declaration placement in generated bytes Clinic wrappers.""" import re import sys from pathlib import Path if len(sys.argv) != 2: raise SystemExit("usage: vc6-bytes-clinic-c89.test.py bytesobject.c.h") source = Path(sys.argv[1]).read_text() checks = { "bytes_decode(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)": ( "encoding_length", "errors_length"), "bytes_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)": ( "encoding_length", "errors_length"), } for signature, names in checks.items(): start = source.index(signature) body_start = source.index("\n{\n", start) + 2 body_end = source.index("\n}", body_start) body = source[body_start:body_end] unpack = re.search(r"^\s*(?:args|fastargs) = _PyArg_UnpackKeywords", body, re.MULTILINE) if unpack is None: raise SystemExit(signature.split("(", 1)[0] + ": argument unpack statement missing") opening = body[:unpack.start()] execution = body[unpack.end():] for name in names: declaration = "Py_ssize_t " + name + ";" if opening.count(declaration) != 1 or body.count(declaration) != 1: raise SystemExit(signature.split("(", 1)[0] + ": " + name + " must be declared once before argument unpacking") if re.search(r"\bPy_ssize_t\s+" + re.escape(name) + r"\b", execution): raise SystemExit(signature.split("(", 1)[0] + ": late declaration remains for " + name) for name in names: if "&" + name not in execution: raise SystemExit(signature.split("(", 1)[0] + ": original UTF-8 length use missing for " + name) print("bytes Clinic C89 declaration placement: PASS")