bpo-36842: Implement PEP 578 by zooba · Pull Request #12613 · python/cpython (original) (raw)

Patch to use METH_FASTCALL:

diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index f987bb2016..fd1408c748 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -22,6 +22,7 @@ Data members:
 #include "pycore_pymem.h"
 #include "pycore_pathconfig.h"
 #include "pycore_pystate.h"
+#include "pycore_tupleobject.h"
 #include "pythread.h"
 #include "pydtrace.h"
 
@@ -360,20 +361,14 @@ PyDoc_STRVAR(audit_doc,
 Passes the event to any audit hooks that are attached.");
 
 static PyObject *
-sys_audit(PyObject *self, PyObject *args)
+sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc)
 {
-    if (!PyTuple_Check(args)) {
-        PyErr_Format(PyExc_TypeError, "expected tuple, not %.200s", Py_TYPE(args)->tp_name);
-        return NULL;
-    }
-
-    Py_ssize_t argc = PyTuple_GET_SIZE(args);
     if (argc == 0) {
         PyErr_SetString(PyExc_TypeError, "audit() missing 1 required positional argument: 'event'");
         return NULL;
     }
 
-    PyObject *auditEvent = PyTuple_GET_ITEM(args, 0);
+    PyObject *auditEvent = args[0];
     if (!auditEvent) {
         PyErr_SetString(PyExc_TypeError, "expected str for argument 'event'");
         return NULL;
@@ -388,16 +383,10 @@ sys_audit(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    PyObject *auditArgs = PyTuple_New(argc - 1);
+    PyObject *auditArgs = _PyTuple_FromArray(args + 1, argc - 1);
     if (!auditArgs) {
         return NULL;
     }
-    for (int i = 0; i < argc - 1; ++i) {
-        PyObject *o = PyTuple_GET_ITEM(args, i + 1);
-        Py_INCREF(o);
-        PyTuple_SET_ITEM(auditArgs, i, o);
-    }
-
     int res = PySys_Audit(event, "O", auditArgs);
     Py_DECREF(auditArgs);
 
@@ -1924,7 +1913,7 @@ sys_getandroidapilevel_impl(PyObject *module)
 static PyMethodDef sys_methods[] = {
     /* Might as well keep this in alphabetic order */
     SYS_ADDAUDITHOOK_METHODDEF
-    {"audit",           sys_audit, METH_VARARGS, audit_doc },
+    {"audit",           (PyCFunction)(void(*)(void))sys_audit, METH_FASTCALL, audit_doc },
     {"breakpointhook",  (PyCFunction)(void(*)(void))sys_breakpointhook,
      METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc},
     SYS_CALLSTATS_METHODDEF

By the way, I might be useful to extract the code from PySys_Audit() to decide if audit is used or not: create a subfunction, and call it from sys_audit() to do nothing if audit is not used (common case).

Calling PyUnicode_AsUTF8() and _PyTuple_FromArray() is not free :-) (I know that they are fast, but many function calls take less than 100 ns overall.)