4

There are some Python C-API functions that steal a reference for one passed argument for example PyList_SetItem, while others increment the reference counts of the argument, for example PyList_Append.

Can I tell Cython that the reference will be stolen? Or do I need to use Py_INCREF manually?

from cpython.object cimport PyObject
from cpython.ref cimport Py_INCREF

cdef extern from "Python.h":
    void PyList_SetItem(object list, Py_ssize_t i, object o)

cpdef void func(list lst, object item):
    Py_INCREF(item)
    PyList_SetItem(lst, 0, item)

I know that if a function returns a borrowed references one can change the return type (PyObject * for borrowed references, object for not-borrowed ones):

from cpython.object cimport PyObject
from cpython.ref cimport Py_INCREF

cdef extern from "Python.h":
    PyObject* PyList_GetItem(object list, Py_ssize_t index)
    object PyObject_GetItem(object o, object key)

But that doesn't seem to work for stolen references (or was my thinking just wrong?):

from cpython.object cimport PyObject

cdef extern from "Python.h":
    void PyList_SetItem(object list, Py_ssize_t i, PyObject *o)

cpdef void func(list lst, object item):
    PyList_SetItem(lst, 0, <PyObject*>item)
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • 1
    This is a great question. Am also interested to learn how this should be solved. Have raised [GH-2722]( https://github.com/cython/cython/issues/2722 ) to figure out how to solve this. – jakirkham Nov 16 '18 at 16:05

1 Answers1

0

There is no automatic way for handling "stealing" in Cython (yet?), so one has to fall back to somewhat awkward manual handling (as pointed out in the question itself):

%%cython
from cpython.ref cimport Py_INCREF
from cpython.list cimport PyList_SetItem

def replace(list lst, object item):
    Py_INCREF(item) # prepare for stealing 
    PyList_SetItem(lst, 0, item)

With behavior as expected:

import sys
a, b= float(), float()
lst = [a]
print(sys.getrefcount(a), sys.getrefcount(b))
# refcount(a)=3 and refcount(b)=2
replace(lst, b)
print(sys.getrefcount(a), sys.getrefcount(b))
# refcount(a)=3 and refcount(b)=2
ead
  • 32,758
  • 6
  • 90
  • 153