#!/usr/bin/env python3 """Guard C89 declaration placement and reference order in iterobject.c.""" import sys from pathlib import Path from typing import NoReturn def fail(message: str) -> NoReturn: print(message, file=sys.stderr) raise SystemExit(1) def extract_function(source: str, signature: str) -> str: start = source.find(signature) if start < 0: fail(f"missing function signature: {signature}") brace = source.find("{", start + len(signature)) if brace < 0: fail("missing function body") depth = 0 for end in range(brace, len(source)): if source[end] == "{": depth += 1 elif source[end] == "}": depth -= 1 if depth == 0: return source[brace + 1:end] fail("unterminated function body") def ordered(body: str, tokens: tuple[str, ...], message: str) -> None: positions = [body.find(token) for token in tokens] if any(position < 0 for position in positions) or positions != sorted(positions): fail(message) source = Path(sys.argv[1]).read_text(encoding="utf-8") getiter = extract_function(source, "anextawaitable_getiter(anextawaitableobject *obj)") for declaration in ("PyObject *awaitable;", "unaryfunc getter;", "PyObject *new_awaitable;"): if declaration not in getiter: fail("anextawaitable_getiter local must be declared before use: " + declaration) for late in ("PyObject *awaitable =", "unaryfunc getter =", "PyObject *new_awaitable ="): if late in getiter: fail("anextawaitable_getiter must not initialize a local in a late declaration: " + late) branch = getiter.split("if (Py_TYPE(awaitable)->tp_iternext == NULL) {", 1)[1] if branch.find("unaryfunc getter;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"): fail("anextawaitable_getiter branch locals must be declared before statements") if branch.find("PyObject *new_awaitable;") > branch.find("getter = Py_TYPE(awaitable)->tp_as_async->am_await;"): fail("anextawaitable_getiter branch locals must be declared before statements") ordered(getiter, ( "assert(obj->wrapped != NULL);", "awaitable = _PyCoro_GetAwaitableIter(obj->wrapped);", "if (awaitable == NULL)", "if (Py_TYPE(awaitable)->tp_iternext == NULL)", "getter = Py_TYPE(awaitable)->tp_as_async->am_await;", "new_awaitable = getter(awaitable);", "if (new_awaitable == NULL)", "Py_SETREF(awaitable, new_awaitable);", "if (!PyIter_Check(awaitable))", "return awaitable;", ), "anextawaitable_getiter must preserve awaitable conversion and cleanup order") iternext = extract_function(source, "anextawaitable_iternext(anextawaitableobject *obj)") for declaration in ("PyObject *awaitable;", "PyObject *result;"): if declaration not in iternext: fail("anextawaitable_iternext local must be declared at function entry: " + declaration) if "PyObject *result =" in iternext: fail("anextawaitable_iternext result must not be declared after the NULL guard") ordered(iternext, ( "awaitable = anextawaitable_getiter(obj);", "if (awaitable == NULL)", "result = (*Py_TYPE(awaitable)->tp_iternext)(awaitable);", "Py_DECREF(awaitable);", "if (result != NULL)", "if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))", "_PyGen_SetStopIterationValue(obj->default_value);", "\n return NULL;", ), "anextawaitable_iternext must preserve call, decref, and StopAsyncIteration handling order") proxy = extract_function(source, "anextawaitable_proxy(anextawaitableobject *obj, char *meth, PyObject *arg)") for declaration in ("PyObject *awaitable;", "PyObject *ret;"): if declaration not in proxy: fail("anextawaitable_proxy local must be declared at function entry: " + declaration) if "PyObject *ret =" in proxy: fail("anextawaitable_proxy result must not be declared after the NULL guard") ordered(proxy, ( "awaitable = anextawaitable_getiter(obj);", "if (awaitable == NULL)", "ret = PyObject_CallMethod(awaitable, meth, \"O\", arg);", "Py_DECREF(awaitable);", "if (ret != NULL)", "if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration))", "_PyGen_SetStopIterationValue(obj->default_value);", "\n return NULL;", ), "anextawaitable_proxy must preserve call, decref, and exception handling order") print("iterobject anextawaitable C89 declarations and reference order: PASS")