Michael Carilli
Michael Carilli

Reputation: 411

INCREF needed when returning argument from Python C Extension function?

This question is pretty simple but will help cement my understanding. I know that arguments to C extension functions are guaranteed to be live references for the duration of the C code (unless manually DECREFed). However, if I have a C extension code that returns a PyObject* that was present in its argument list, do I need to INCREF the argument before returning it? I.e., which of the following two is correct:

static PyObject return_item(PyObject *self, PyObject *item)
{
    // manipulate item
    return item;
}

or

static PyObject return_item(PyObject *self, PyObject *item)
{
    // manipulate item
    Py_INCREF(item);
    return item;
}

Based on https://docs.python.org/3/extending/extending.html#ownership-rules, which says

The object reference returned from a C function that is called from Python must be an owned reference — ownership is transferred from the function to its caller.

and Returning objects to Python from C I assume it's the latter (INCREFing is the way to go) but I want to be sure.

Upvotes: 4

Views: 864

Answers (2)

Robin Betz
Robin Betz

Reputation: 65

You don't want to increment the reference count of the object before you return it. Doing so creates a memory leak that will prevent the object from being garbage collected.

Think of incrementing the reference count as saying "I am using this memory. Please don't free it." When you enter the C code, you "borrow" the reference from Python, but when you exit the C code, you're done with the object and don't need the reference anymore.

Variables and the underlying memory in Python are separate, which lets it be somewhat efficient with memory (read for more). The other answer misses the fact that assigning to something_else increments the reference count for the underlying memory. You can verify this yourself with sys.getrefcount.

import sys
something = "hello"
print(sys.getrefcount(something))       # 2 (getrefcount uses a reference)

something_else = something
print(sys.getrefcount(something_else))  # 3 (same memory as something)

del something
print(sys.getrefcount(something_else))  # 2
print(something_else)                   # "hello"

Both something and something_else referred to the same memory (a string containing the text "hello"). Deleting one doesn't affect the other. Even though your code uses a C function, the underlying principle is the same. Try printing out the reference count with both versions of your C function and it will be more clear.

What you don't want to do is call Py_DECREF before returning an object. In that case the reference count can drop to zero and the returned thing is totally invalid.

Upvotes: -3

zvone
zvone

Reputation: 19352

If someone is calling the return_item function from Python, they might do this:

something = Something()
something_else = return_item(something)
del something

If return_item did not return the argument which was passed in, but something else, you would expect that at this point the something which was passed in should be freed from memory, because its reference count falls to zero.

If you don't Py_INCREF and return the same object, that will still happen - the object's reference count will fall to 0 and you will have an invalid object in something_else.

TL;DR: Yes, you should Py_INCREF, because you created another reference to that object by returning it from the function.

Upvotes: 5

Related Questions