8000 gh-85283: Add PySys_AuditTuple() function · python/cpython@0ca0240 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0ca0240

Browse files
committed
gh-85283: Add PySys_AuditTuple() function
sys.audit() now has assertions to check that the event argument is not NULL and that the format argument does not use the "N" format. Add tests on PySys_AuditTuple().
1 parent fb6c4ed commit 0ca0240

File tree

7 files changed

+109
-9
lines changed

7 files changed

+109
-9
lines changed

Doc/c-api/sys.rst

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,19 +291,24 @@ accessible to C code. They all work with the current interpreter thread's
291291
Raise an auditing event with any active hooks. Return zero for success
292292
and non-zero with an exception set on failure.
293293
294+
The *event* string argument must not be *NULL*.
295+
294296
If any hooks have been added, *format* and other arguments will be used
295297
to construct a tuple to pass. Apart from ``N``, the same format characters
296298
as used in :c:func:`Py_BuildValue` are available. If the built value is not
297-
a tuple, it will be added into a single-element tuple. (The ``N`` format
298-
option consumes a reference, but since there is no way to know whether
299-
arguments to this function will be consumed, using it may cause reference
300-
leaks.)
299+
a tuple, it will be added into a single-element tuple.
300+
301+
The ``N`` format option must not be used. It consumes a reference, but since
302+
there is no way to know whether arguments to this function will be consumed,
303+
using it may cause reference leaks.
301304
302305
Note that ``#`` format characters should always be treated as
303306
:c:type:`Py_ssize_t`, regardless of whether ``PY_SSIZE_T_CLEAN`` was defined.
304307
305308
:func:`sys.audit` performs the same function from Python code.
306309
310+
See also :c:func:`PySys_AuditTuple`.
311+
307312
.. versionadded:: 3.8
308313
309314
.. versionchanged:: 3.8.2
@@ -312,6 +317,14 @@ accessible to C code. They all work with the current interpreter thread's
312317
unavoidable deprecation warning was raised.
313318
314319
320+
.. c:function:: int PySys_AuditTuple(const char *event, PyObject *args)
321+
322+
Similar to :c:func:`PySys_Audit`, but pass arguments as a Python object.
323+
*args* must be a :class:`tuple`. To pass no arguments, *args* can be *NULL*.
324+
325+
.. versionadded:: 3.13
326+
327+
315328
.. c:function:: int PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
316329
317330
Append the callable *hook* to the list of active auditing hooks.

Doc/whatsnew/3.13.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,10 @@ New Features
10131013
``_PyThreadState_UncheckedGet()``.
10141014
(Contributed by Victor Stinner in :gh:`108867`.)
10151015

1016+
* Add :c:func:`PySys_AuditTuple` function: similar to :c:func:`PySys_Audit`,
1017+
but pass event arguments as a Python :class:`tuple` object.
1018+
(Contributed by Victor Stinner in :gh:`85283`.)
1019+
10161020
Porting to Python 3.13
10171021
----------------------
10181022

Include/cpython/sysmodule.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *);
66

77
PyAPI_FUNC(int) PySys_Audit(
88
const char *event,
9-
const char *argFormat,
9+
const char *format,
1010
...);
1111
PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*);
12+
13+
PyAPI_FUNC(int) PySys_AuditTuple(
14+
const char *event,
15+
PyObject *args);

Lib/test/test_embed.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,9 @@ def test_open_code_hook(self):
17161716
def test_audit(self):
17171717
self.run_embedded_interpreter("test_audit")
17181718

1719+
def test_audit_tuple(self):
1720+
self.run_embedded_interpreter("test_audit_tuple")
1721+
17191722
def test_audit_subinterpreter(self):
17201723
self.run_embedded_interpreter("test_audit_subinterpreter")
17211724

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PySys_AuditTuple` function: similar to :c:func:`PySys_Audit`,
2+
but pass event arguments as a Python :class:`tuple` object. Patch by Victor
3+
Stinner.

Programs/_testembed.c

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,11 +1278,16 @@ static int _test_audit(Py_ssize_t setValue)
12781278
printf("Set event failed");
12791279
return 4;
12801280
}
1281+
if (PyErr_Occurred()) {
1282+
printf("Exception raised");
1283+
return 5;
1284+
}
12811285

12821286
if (sawSet != 42) {
12831287
printf("Failed to see *userData change\n");
1284-
return 5;
1288+
return 6;
12851289
}
1290+
12861291
return 0;
12871292
}
12881293

@@ -1296,6 +1301,57 @@ static int test_audit(void)
12961301
return result;
12971302
}
12981303

1304+
static int test_audit_tuple(void)
1305+
{
1306+
#define ASSERT(TEST, EXITCODE) \
1307+
if (!(TEST)) { \
1308+
printf("ERROR test failed at %s:%i\n", __FILE__, __LINE__); \
1309+
return (EXITCODE); \
1310+
}
1311+
1312+
Py_ssize_t sawSet = 0;
1313+
1314+
// we need at least one hook, otherwise code checking for
1315+
// PySys_AuditTuple() is skipped.
1316+
PySys_AddAuditHook(_audit_hook, &sawSet);
1317+
_testembed_Py_InitializeFromConfig();
1318+
1319+
ASSERT(!PyErr_Occurred(), 0);
1320+
1321+
// pass Python tuple object
1322+
PyObject *tuple = Py_BuildValue("(i)", 444);
1323+
if (tuple == NULL) {
1324+
goto error;
1325+
}
1326+
ASSERT(PySys_AuditTuple("_testembed.set", tuple) == 0, 10);
1327+
ASSERT(!PyErr_Occurred(), 11);
1328+
ASSERT(sawSet == 444, 12);
1329+
Py_DECREF(tuple);
1330+
1331+
// pass Python int object
1332+
PyObject *int_arg = PyLong_FromLong(555);
1333+
if (int_arg == NULL) {
1334+
goto error;
1335+
}
1336+
ASSERT(PySys_AuditTuple("_testembed.set", int_arg) == -1, 20);
1337+
ASSERT(PyErr_ExceptionMatches(PyExc_TypeError), 21);
1338+
PyErr_Clear();
1339+
Py_DECREF(int_arg);
1340+
1341+
// NULL is accepted and means "no arguments"
1342+
ASSERT(PySys_AuditTuple("_testembed.test_audit_tuple", NULL) == 0, 30);
1343+
ASSERT(!PyErr_Occurred(), 31);
1344+
1345+
Py_Finalize();
1346+
return 0;
1347+
1348+
error:
1349+
PyErr_Print();
1350+
return 1;
1351+
1352+
#undef ASSERT
1353+
}
1354+
12991355
static volatile int _audit_subinterpreter_interpreter_count = 0;
13001356

13011357
static int _audit_subinterpreter_hook(const char *event, PyObject *args, void *userdata)
@@ -2140,6 +2196,7 @@ static struct TestCase TestCases[] = {
21402196
// Audit
21412197
{"test_open_code_hook", test_open_code_hook},
21422198
{"test_audit", test_audit},
2199+
{"test_audit_tuple", test_audit_tuple},
21432200
{"test_audit_subinterpreter", test_audit_subinterpreter},
21442201
{"test_audit_run_command", test_audit_run_command},
21452202
{"test_audit_run_file", test_audit_run_file},

Python/sysmodule.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,7 @@ static int
191191
sys_audit_tstate(PyThreadState *ts, const char *event,
192192
const char *argFormat, va_list vargs)
193193
{
194-
/* N format is inappropriate, because you do not know
195-
whether the reference is consumed by the call.
196-
Assert rather than exception for perf reasons */
194+
assert(event != NULL);
197195
assert(!argFormat || !strchr(argFormat, 'N'));
198196

199197
if (!ts) {
@@ -338,6 +336,21 @@ PySys_Audit(const char *event, const char *argFormat, ...)
338336
return res;
339337
}
340338

339+
int
340+
PySys_AuditTuple(const char *event, PyObject *args)
341+
{
342+
if (args == NULL) {
343+
return PySys_Audit(event, NULL);
344+
}
345+
346+
if (!PyTuple_Check(args)) {
347+
PyErr_Format(PyExc_TypeError, "args must be tuple, got %s",
348+
Py_TYPE(args)->tp_name);
349+
return -1;
350+
}
351+
return PySys_Audit(event, "O", args);
352+
}
353+
341354
/* We expose this function primarily for our own cleanup during
342355
* finalization. In general, it should not need to be called,
343356
* and as such the function is not exported.
@@ -509,6 +522,9 @@ sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc)
509522
return NULL;
510523
}
511524

525+
assert(args[0] != NULL);
526+
assert(PyUnicode_Check(args[0]));
527+
512528
if (!should_audit(tstate->interp)) {
513529
Py_RETURN_NONE;
514530
}

0 commit comments

Comments
 (0)
0