pythoncython

Py_INCREF in Cython accepts Python object but not pointer


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 a PyObject*, which is a pointer to a Python object, not a Python-level object. Therefore, you should pass a PyObject* (a pointer) to Py_INCREF.

The code seems so simple, I'm unsure why it doesn't work.


Solution

  • 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:

    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:

    1. Cast to object:

      Py_INCREF(<object>node.group)
      
    2. 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)