I have a small implementation of a linked list in Cython, and I wish to add a value to the list.
For some reason, when I try to Py_INCREF
(to make sure I keep a strong reference in each node), Cython does not accept a pointer to the Python object, but only the object itself:
ctypedef struct Node:
PyObject* group
PyObject* value # (group, value)
Node* next
cdef bint _put(object group, object value) except 1:
cdef:
Node* node = <Node*>PyMem_Malloc(sizeof(Node))
PyObject* _group = <PyObject*> group
PyObject* _value = <PyObject*> value
if not node:
raise MemoryError("Failed to allocate memory for new node")
node.group = _group
Py_INCREF(node.group) # Cannot convert 'PyObject *' to Python object
Py_INCREF(_group) # Cannot convert 'PyObject *' to Python object
Py_INCREF(<PyObject*> group) # Cannot convert 'PyObject *' to Python object
Py_INCREF(group) # Works
Supposedly, Py_INCREF
is meant to work with PyObject*
.
Both looking up around the web and chatgpt directly suggested that:
Py_INCREF
in Cython works with aPyObject*
, which is a pointer to a Python object, not a Python-level object. Therefore, you should pass aPyObject*
(a pointer) toPy_INCREF
.
The code seems so simple, I'm unsure why it doesn't work.
Presumably you've cimported it from cpython.ref
?
That's largely just because of how it's defined:
void Py_INCREF(object o)
The convention that Cython generally uses is:
object
if it cannot be NULLPyObject*
if it can be NULL.Since INCREF
doesn't accept NULL arguments then it's object
. XINCREF
which does accept NULL arguments is PyObject*
.
Even if you don't like that convention, it'd be very hard to change at this stage because there's a large amount of code that uses the current definition.
You've got two options:
Cast to object:
Py_INCREF(<object>node.group)
Write your own definition that fits how you want it (the C API definitions that Cython exposes don't do anything that you can't do yourself):
cdef extern from "Python.h":
void Py_INCREF(PyObject* o)