mirror of https://github.com/python/cpython
gh-85283: Add PySys_AuditTuple() function (#108965)
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().
This commit is contained in:
parent
aaf297c048
commit
bb057b3370
|
@ -291,19 +291,24 @@ accessible to C code. They all work with the current interpreter thread's
|
||||||
Raise an auditing event with any active hooks. Return zero for success
|
Raise an auditing event with any active hooks. Return zero for success
|
||||||
and non-zero with an exception set on failure.
|
and non-zero with an exception set on failure.
|
||||||
|
|
||||||
|
The *event* string argument must not be *NULL*.
|
||||||
|
|
||||||
If any hooks have been added, *format* and other arguments will be used
|
If any hooks have been added, *format* and other arguments will be used
|
||||||
to construct a tuple to pass. Apart from ``N``, the same format characters
|
to construct a tuple to pass. Apart from ``N``, the same format characters
|
||||||
as used in :c:func:`Py_BuildValue` are available. If the built value is not
|
as used in :c:func:`Py_BuildValue` are available. If the built value is not
|
||||||
a tuple, it will be added into a single-element tuple. (The ``N`` format
|
a tuple, it will be added into a single-element tuple.
|
||||||
option consumes a reference, but since there is no way to know whether
|
|
||||||
arguments to this function will be consumed, using it may cause reference
|
The ``N`` format option must not be used. It consumes a reference, but since
|
||||||
leaks.)
|
there is no way to know whether arguments to this function will be consumed,
|
||||||
|
using it may cause reference leaks.
|
||||||
|
|
||||||
Note that ``#`` format characters should always be treated as
|
Note that ``#`` format characters should always be treated as
|
||||||
:c:type:`Py_ssize_t`, regardless of whether ``PY_SSIZE_T_CLEAN`` was defined.
|
:c:type:`Py_ssize_t`, regardless of whether ``PY_SSIZE_T_CLEAN`` was defined.
|
||||||
|
|
||||||
:func:`sys.audit` performs the same function from Python code.
|
:func:`sys.audit` performs the same function from Python code.
|
||||||
|
|
||||||
|
See also :c:func:`PySys_AuditTuple`.
|
||||||
|
|
||||||
.. versionadded:: 3.8
|
.. versionadded:: 3.8
|
||||||
|
|
||||||
.. versionchanged:: 3.8.2
|
.. versionchanged:: 3.8.2
|
||||||
|
@ -312,6 +317,14 @@ accessible to C code. They all work with the current interpreter thread's
|
||||||
unavoidable deprecation warning was raised.
|
unavoidable deprecation warning was raised.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PySys_AuditTuple(const char *event, PyObject *args)
|
||||||
|
|
||||||
|
Similar to :c:func:`PySys_Audit`, but pass arguments as a Python object.
|
||||||
|
*args* must be a :class:`tuple`. To pass no arguments, *args* can be *NULL*.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: int PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
|
.. c:function:: int PySys_AddAuditHook(Py_AuditHookFunction hook, void *userData)
|
||||||
|
|
||||||
Append the callable *hook* to the list of active auditing hooks.
|
Append the callable *hook* to the list of active auditing hooks.
|
||||||
|
|
|
@ -1013,6 +1013,10 @@ New Features
|
||||||
``_PyThreadState_UncheckedGet()``.
|
``_PyThreadState_UncheckedGet()``.
|
||||||
(Contributed by Victor Stinner in :gh:`108867`.)
|
(Contributed by Victor Stinner in :gh:`108867`.)
|
||||||
|
|
||||||
|
* Add :c:func:`PySys_AuditTuple` function: similar to :c:func:`PySys_Audit`,
|
||||||
|
but pass event arguments as a Python :class:`tuple` object.
|
||||||
|
(Contributed by Victor Stinner in :gh:`85283`.)
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@ typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *);
|
||||||
|
|
||||||
PyAPI_FUNC(int) PySys_Audit(
|
PyAPI_FUNC(int) PySys_Audit(
|
||||||
const char *event,
|
const char *event,
|
||||||
const char *argFormat,
|
const char *format,
|
||||||
...);
|
...);
|
||||||
PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*);
|
PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PySys_AuditTuple(
|
||||||
|
const char *event,
|
||||||
|
PyObject *args);
|
||||||
|
|
|
@ -1716,6 +1716,9 @@ class AuditingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
def test_audit(self):
|
def test_audit(self):
|
||||||
self.run_embedded_interpreter("test_audit")
|
self.run_embedded_interpreter("test_audit")
|
||||||
|
|
||||||
|
def test_audit_tuple(self):
|
||||||
|
self.run_embedded_interpreter("test_audit_tuple")
|
||||||
|
|
||||||
def test_audit_subinterpreter(self):
|
def test_audit_subinterpreter(self):
|
||||||
self.run_embedded_interpreter("test_audit_subinterpreter")
|
self.run_embedded_interpreter("test_audit_subinterpreter")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add :c:func:`PySys_AuditTuple` function: similar to :c:func:`PySys_Audit`,
|
||||||
|
but pass event arguments as a Python :class:`tuple` object. Patch by Victor
|
||||||
|
Stinner.
|
|
@ -1278,11 +1278,16 @@ static int _test_audit(Py_ssize_t setValue)
|
||||||
printf("Set event failed");
|
printf("Set event failed");
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
printf("Exception raised");
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
if (sawSet != 42) {
|
if (sawSet != 42) {
|
||||||
printf("Failed to see *userData change\n");
|
printf("Failed to see *userData change\n");
|
||||||
return 5;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,6 +1301,57 @@ static int test_audit(void)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_audit_tuple(void)
|
||||||
|
{
|
||||||
|
#define ASSERT(TEST, EXITCODE) \
|
||||||
|
if (!(TEST)) { \
|
||||||
|
printf("ERROR test failed at %s:%i\n", __FILE__, __LINE__); \
|
||||||
|
return (EXITCODE); \
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t sawSet = 0;
|
||||||
|
|
||||||
|
// we need at least one hook, otherwise code checking for
|
||||||
|
// PySys_AuditTuple() is skipped.
|
||||||
|
PySys_AddAuditHook(_audit_hook, &sawSet);
|
||||||
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
|
ASSERT(!PyErr_Occurred(), 0);
|
||||||
|
|
||||||
|
// pass Python tuple object
|
||||||
|
PyObject *tuple = Py_BuildValue("(i)", 444);
|
||||||
|
if (tuple == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ASSERT(PySys_AuditTuple("_testembed.set", tuple) == 0, 10);
|
||||||
|
ASSERT(!PyErr_Occurred(), 11);
|
||||||
|
ASSERT(sawSet == 444, 12);
|
||||||
|
Py_DECREF(tuple);
|
||||||
|
|
||||||
|
// pass Python int object
|
||||||
|
PyObject *int_arg = PyLong_FromLong(555);
|
||||||
|
if (int_arg == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
ASSERT(PySys_AuditTuple("_testembed.set", int_arg) == -1, 20);
|
||||||
|
ASSERT(PyErr_ExceptionMatches(PyExc_TypeError), 21);
|
||||||
|
PyErr_Clear();
|
||||||
|
Py_DECREF(int_arg);
|
||||||
|
|
||||||
|
// NULL is accepted and means "no arguments"
|
||||||
|
ASSERT(PySys_AuditTuple("_testembed.test_audit_tuple", NULL) == 0, 30);
|
||||||
|
ASSERT(!PyErr_Occurred(), 31);
|
||||||
|
|
||||||
|
Py_Finalize();
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
PyErr_Print();
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
#undef ASSERT
|
||||||
|
}
|
||||||
|
|
||||||
static volatile int _audit_subinterpreter_interpreter_count = 0;
|
static volatile int _audit_subinterpreter_interpreter_count = 0;
|
||||||
|
|
||||||
static int _audit_subinterpreter_hook(const char *event, PyObject *args, void *userdata)
|
static int _audit_subinterpreter_hook(const char *event, PyObject *args, void *userdata)
|
||||||
|
@ -2140,6 +2196,7 @@ static struct TestCase TestCases[] = {
|
||||||
// Audit
|
// Audit
|
||||||
{"test_open_code_hook", test_open_code_hook},
|
{"test_open_code_hook", test_open_code_hook},
|
||||||
{"test_audit", test_audit},
|
{"test_audit", test_audit},
|
||||||
|
{"test_audit_tuple", test_audit_tuple},
|
||||||
{"test_audit_subinterpreter", test_audit_subinterpreter},
|
{"test_audit_subinterpreter", test_audit_subinterpreter},
|
||||||
{"test_audit_run_command", test_audit_run_command},
|
{"test_audit_run_command", test_audit_run_command},
|
||||||
{"test_audit_run_file", test_audit_run_file},
|
{"test_audit_run_file", test_audit_run_file},
|
||||||
|
|
|
@ -191,9 +191,7 @@ static int
|
||||||
sys_audit_tstate(PyThreadState *ts, const char *event,
|
sys_audit_tstate(PyThreadState *ts, const char *event,
|
||||||
const char *argFormat, va_list vargs)
|
const char *argFormat, va_list vargs)
|
||||||
{
|
{
|
||||||
/* N format is inappropriate, because you do not know
|
assert(event != NULL);
|
||||||
whether the reference is consumed by the call.
|
|
||||||
Assert rather than exception for perf reasons */
|
|
||||||
assert(!argFormat || !strchr(argFormat, 'N'));
|
assert(!argFormat || !strchr(argFormat, 'N'));
|
||||||
|
|
||||||
if (!ts) {
|
if (!ts) {
|
||||||
|
@ -338,6 +336,21 @@ PySys_Audit(const char *event, const char *argFormat, ...)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PySys_AuditTuple(const char *event, PyObject *args)
|
||||||
|
{
|
||||||
|
if (args == NULL) {
|
||||||
|
return PySys_Audit(event, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyTuple_Check(args)) {
|
||||||
|
PyErr_Format(PyExc_TypeError, "args must be tuple, got %s",
|
||||||
|
Py_TYPE(args)->tp_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return PySys_Audit(event, "O", args);
|
||||||
|
}
|
||||||
|
|
||||||
/* We expose this function primarily for our own cleanup during
|
/* We expose this function primarily for our own cleanup during
|
||||||
* finalization. In general, it should not need to be called,
|
* finalization. In general, it should not need to be called,
|
||||||
* and as such the function is not exported.
|
* and as such the function is not exported.
|
||||||
|
@ -509,6 +522,9 @@ sys_audit(PyObject *self, PyObject *const *args, Py_ssize_t argc)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(args[0] != NULL);
|
||||||
|
assert(PyUnicode_Check(args[0]));
|
||||||
|
|
||||||
if (!should_audit(tstate->interp)) {
|
if (!should_audit(tstate->interp)) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue