#!/usr/bin/env python3 """Keep type_vectorcall's nargs declaration separate and evaluation first.""" import re import sys from pathlib import Path source = Path(sys.argv[1] if len(sys.argv) > 1 else "cpython/Objects/typeobject.c").read_text() match = re.search( r"\nstatic PyObject \*\ntype_vectorcall\(PyObject \*metatype, PyObject \*const \*args,\n" r"\s*size_t nargsf, PyObject \*kwnames\)\n\{(.*?)\n\}", source, re.S, ) assert match, "type_vectorcall definition missing" body = match.group(1) assert re.match(r"\s*Py_ssize_t nargs;\s*PyThreadState \*tstate;\s*nargs = PyVectorcall_NARGS\(nargsf\);", body), ( "type_vectorcall: declare nargs at block entry and assign at the original first statement position" ) assert len(re.findall(r"\bPy_ssize_t\s+nargs\b", body)) == 1, ( "type_vectorcall: declare nargs exactly once" ) assert re.findall(r"\bnargs\s*=(?!=)\s*([^;]+);", body) == ["PyVectorcall_NARGS(nargsf)"], ( "type_vectorcall: evaluate nargs exactly once" ) expected = """ Py_ssize_t nargs; PyThreadState *tstate; nargs = PyVectorcall_NARGS(nargsf); if (nargs == 1 && metatype == (PyObject *)&PyType_Type){ if (!_PyArg_NoKwnames("type", kwnames)) { return NULL; } return Py_NewRef(Py_TYPE(args[0])); } /* In other (much less common) cases, fall back to more flexible calling conventions. */ tstate = _PyThreadState_GET(); return _PyObject_MakeTpCall(tstate, metatype, args, nargs, kwnames); """ assert body.split() == expected.split(), ( "type_vectorcall: preserve evaluation order, fast path, and fallback unchanged" ) print("type_vectorcall nargs declaration and unchanged evaluation/control flow: PASS")