Вопрос

Say I define a container object (list, in our case), automatically tracked by the CPython's GC. Then decide to untrack it using ctypes's pythonapi function. The documentation says that PyObject_GC_UnTrack returns void, yet, when calling it via pythonapi, it returns an integer which is not the memory address of the object being untracked nor its pointer.

>>>import ctypes
>>>from ctypes import pythonapi, py_object
>>>import gc
>>>a = [1,2,3,4,5]
>>>pythonapi.PyObject_GC_UnTrack(py_object(a))
-396907592
>>>gc.is_tracked(a)
False
Это было полезно?

Решение

From the ctypes docs:

ctypes.pythonapi

An instance of PyDLL that exposes Python C API functions as attributes. Note that all these functions are assumed to return C int, which is of course not always the truth, so you have to assign the correct restype attribute to use these functions.

And it's generally true (not just specific to pythonapi) that ctypes assumes functions return C ints.

>>> pythonapi.PyObject_GC_UnTrack.restype
<class 'ctypes.c_long'>

(On this box, int is the same as long).

If that matters, you have to set restype yourself:

>>> pythonapi.PyObject_GC_UnTrack.restype = None # which means "void"
>>> a = [1,2,3,4,5]
>>> pythonapi.PyObject_GC_UnTrack(py_object(a))
[no output]

It's true that PyObject_GC_UnTrack returns void; so long as ctypes believes it returns an int, ctypes returns garbage bytes. Here from Modules/gcmodule.c:

void
PyObject_GC_UnTrack(void *op)
{
    /* Obscure:  the Py_TRASHCAN mechanism requires that we be able to
     * call PyObject_GC_UnTrack twice on an object.
     */
    if (IS_TRACKED(op))
        _PyObject_GC_UNTRACK(op);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top