mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
gh-91053: Add an optional callback that is invoked whenever a function is modified (#98175)
This commit is contained in:
parent
20d9749a0f
commit
3db0a21f73
9 changed files with 524 additions and 0 deletions
|
@ -3,10 +3,68 @@
|
|||
|
||||
#include "Python.h"
|
||||
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
|
||||
#include "pycore_function.h" // FUNC_MAX_WATCHERS
|
||||
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
|
||||
#include "pycore_pyerrors.h" // _PyErr_Occurred()
|
||||
#include "structmember.h" // PyMemberDef
|
||||
|
||||
static void
|
||||
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
|
||||
PyFunctionObject *func, PyObject *new_value)
|
||||
{
|
||||
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
|
||||
PyFunction_WatchCallback cb = interp->func_watchers[i];
|
||||
if ((cb != NULL) && (cb(event, func, new_value) < 0)) {
|
||||
PyErr_WriteUnraisable((PyObject *) func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
|
||||
PyObject *new_value)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
if (interp->active_func_watchers) {
|
||||
notify_func_watchers(interp, event, func, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
PyFunction_AddWatcher(PyFunction_WatchCallback callback)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
assert(interp->_initialized);
|
||||
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
|
||||
if (interp->func_watchers[i] == NULL) {
|
||||
interp->func_watchers[i] = callback;
|
||||
interp->active_func_watchers |= (1 << i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
PyErr_SetString(PyExc_RuntimeError, "no more func watcher IDs available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
PyFunction_ClearWatcher(int watcher_id)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
if (watcher_id < 0 || watcher_id >= FUNC_MAX_WATCHERS) {
|
||||
PyErr_Format(PyExc_ValueError, "invalid func watcher ID %d",
|
||||
watcher_id);
|
||||
return -1;
|
||||
}
|
||||
if (!interp->func_watchers[watcher_id]) {
|
||||
PyErr_Format(PyExc_ValueError, "no func watcher set for ID %d",
|
||||
watcher_id);
|
||||
return -1;
|
||||
}
|
||||
interp->func_watchers[watcher_id] = NULL;
|
||||
interp->active_func_watchers &= ~(1 << watcher_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyFunctionObject *
|
||||
_PyFunction_FromConstructor(PyFrameConstructor *constr)
|
||||
{
|
||||
|
@ -31,6 +89,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
|
|||
op->vectorcall = _PyFunction_Vectorcall;
|
||||
op->func_version = 0;
|
||||
_PyObject_GC_TRACK(op);
|
||||
handle_func_event(PyFunction_EVENT_CREATE, op, NULL);
|
||||
return op;
|
||||
}
|
||||
|
||||
|
@ -105,6 +164,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
|
|||
op->vectorcall = _PyFunction_Vectorcall;
|
||||
op->func_version = 0;
|
||||
_PyObject_GC_TRACK(op);
|
||||
handle_func_event(PyFunction_EVENT_CREATE, op, NULL);
|
||||
return (PyObject *)op;
|
||||
|
||||
error:
|
||||
|
@ -196,6 +256,8 @@ PyFunction_SetDefaults(PyObject *op, PyObject *defaults)
|
|||
PyErr_SetString(PyExc_SystemError, "non-tuple default args");
|
||||
return -1;
|
||||
}
|
||||
handle_func_event(PyFunction_EVENT_MODIFY_DEFAULTS,
|
||||
(PyFunctionObject *) op, defaults);
|
||||
((PyFunctionObject *)op)->func_version = 0;
|
||||
Py_XSETREF(((PyFunctionObject *)op)->func_defaults, defaults);
|
||||
return 0;
|
||||
|
@ -236,6 +298,8 @@ PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults)
|
|||
"non-dict keyword only default args");
|
||||
return -1;
|
||||
}
|
||||
handle_func_event(PyFunction_EVENT_MODIFY_KWDEFAULTS,
|
||||
(PyFunctionObject *) op, defaults);
|
||||
((PyFunctionObject *)op)->func_version = 0;
|
||||
Py_XSETREF(((PyFunctionObject *)op)->func_kwdefaults, defaults);
|
||||
return 0;
|
||||
|
@ -389,6 +453,7 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
|
|||
nclosure, nfree);
|
||||
return -1;
|
||||
}
|
||||
handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value);
|
||||
op->func_version = 0;
|
||||
Py_XSETREF(op->func_code, Py_NewRef(value));
|
||||
return 0;
|
||||
|
@ -468,6 +533,7 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored
|
|||
return -1;
|
||||
}
|
||||
|
||||
handle_func_event(PyFunction_EVENT_MODIFY_DEFAULTS, op, value);
|
||||
op->func_version = 0;
|
||||
Py_XSETREF(op->func_defaults, Py_XNewRef(value));
|
||||
return 0;
|
||||
|
@ -508,6 +574,7 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
|
|||
return -1;
|
||||
}
|
||||
|
||||
handle_func_event(PyFunction_EVENT_MODIFY_KWDEFAULTS, op, value);
|
||||
op->func_version = 0;
|
||||
Py_XSETREF(op->func_kwdefaults, Py_XNewRef(value));
|
||||
return 0;
|
||||
|
@ -687,6 +754,7 @@ func_clear(PyFunctionObject *op)
|
|||
static void
|
||||
func_dealloc(PyFunctionObject *op)
|
||||
{
|
||||
handle_func_event(PyFunction_EVENT_DESTROY, op, NULL);
|
||||
_PyObject_GC_UNTRACK(op);
|
||||
if (op->func_weakreflist != NULL) {
|
||||
PyObject_ClearWeakRefs((PyObject *) op);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue