is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer
that was provided when :c:func:`PyRefTracer_SetTracer` was called.
+ If a new tracing function is registered replacing the current a call to the
+ trace function will be made with the object set to **NULL** and **event** set to
+ :c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new
+ function is registered.
+
.. versionadded:: 3.13
.. c:var:: int PyRefTracer_CREATE
The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python
object has been destroyed.
+.. c:var:: int PyRefTracer_TRACKER_REMOVED
+
+ The value for the *event* parameter to :c:type:`PyRefTracer` functions when the
+ current tracer is about to be replaced by a new one.
+
+ .. versionadded:: 3.14
+
.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data)
Register a reference tracer function. The function will be called when a new
There must be an :term:`attached thread state` when calling this function.
+ If another tracer function was already registered, the old function will be
+ called with **event** set to :c:data:`PyRefTracer_TRACKER_REMOVED` just before
+ the new function is registered.
+
.. versionadded:: 3.13
.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data)
typedef enum {
PyRefTracer_CREATE = 0,
PyRefTracer_DESTROY = 1,
+ PyRefTracer_TRACKER_REMOVED = 2,
} PyRefTracerEvent;
typedef int (*PyRefTracer)(PyObject *, PyRefTracerEvent event, void *);
--- /dev/null
+When a new tracing function is registered with
+:c:func:`PyRefTracer_SetTracer`, replacing the current a call to the trace
+function will be made with the object set to **NULL** and **event** set to
+:c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new
+function is registered. Patch by Pablo Galindo
struct simpletracer_data {
int create_count;
int destroy_count;
+ int tracker_removed;
void* addresses[10];
};
struct simpletracer_data* the_data = (struct simpletracer_data*)data;
assert(the_data->create_count + the_data->destroy_count < (int)Py_ARRAY_LENGTH(the_data->addresses));
the_data->addresses[the_data->create_count + the_data->destroy_count] = obj;
- if (event == PyRefTracer_CREATE) {
- the_data->create_count++;
- } else {
- the_data->destroy_count++;
+ switch (event) {
+ case PyRefTracer_CREATE:
+ the_data->create_count++;
+ break;
+ case PyRefTracer_DESTROY:
+ the_data->destroy_count++;
+ break;
+ case PyRefTracer_TRACKER_REMOVED:
+ the_data->tracker_removed++;
+ break;
+ default:
+ return -1;
}
return 0;
}
PyErr_SetString(PyExc_ValueError, "The object destruction was not correctly traced");
goto failed;
}
+ if (tracer_data.tracker_removed != 1) {
+ PyErr_SetString(PyExc_ValueError, "The tracker removal was not correctly traced");
+ goto failed;
+ }
PyRefTracer_SetTracer(current_tracer, current_data);
Py_RETURN_NONE;
failed:
static int
_reftrace_printer(PyObject *obj, PyRefTracerEvent event, void *counter_data)
{
- if (event == PyRefTracer_CREATE) {
- printf("CREATE %s\n", Py_TYPE(obj)->tp_name);
- }
- else { // PyRefTracer_DESTROY
- printf("DESTROY %s\n", Py_TYPE(obj)->tp_name);
+ switch (event) {
+ case PyRefTracer_CREATE:
+ printf("CREATE %s\n", Py_TYPE(obj)->tp_name);
+ break;
+ case PyRefTracer_DESTROY:
+ printf("DESTROY %s\n", Py_TYPE(obj)->tp_name);
+ break;
+ case PyRefTracer_TRACKER_REMOVED:
+ return 0;
}
return 0;
}
int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) {
_Py_AssertHoldsTstate();
+ if (_PyRuntime.ref_tracer.tracer_func != NULL) {
+ _PyReftracerTrack(NULL, PyRefTracer_TRACKER_REMOVED);
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+ }
_PyRuntime.ref_tracer.tracer_func = tracer;
_PyRuntime.ref_tracer.tracer_data = data;
return 0;