Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C API: Add PyTuple_GetItemRef(), similar to PyTuple_GetItem() but return a strong reference #117518

Closed
vstinner opened this issue Apr 3, 2024 · 11 comments
Labels
topic-C-API type-feature A feature request or enhancement

Comments

@vstinner
Copy link
Member

vstinner commented Apr 3, 2024

I propose to add a new PyTuple_GetItemRef() function, similar to PyTuple_GetItem() but return a strong reference instead of a borrowed reference.

Other functions were added recently to return a strong reference:

I already proposed adding PyTuple_GetItemRef() in 2020, but the idea was rejected: issue gh-86460. Since 2020, Free Threading (PEP 703) was added to Python and borrowed references don't play well with Free Threading.

Linked PRs

vstinner added a commit to vstinner/cpython that referenced this issue Apr 3, 2024
vstinner added a commit to vstinner/cpython that referenced this issue Apr 3, 2024
Replace Py_NewRef(PyTuple_GET_ITEM(tuple, pos)) with
PyTuple_GetItemRef(tuple, pos).
vstinner added a commit to vstinner/cpython that referenced this issue Apr 3, 2024
Replace Py_NewRef(PyTuple_GET_ITEM(tuple, pos)) with
PyTuple_GetItemRef(tuple, pos).
@erlend-aasland erlend-aasland added topic-C-API type-feature A feature request or enhancement labels Apr 3, 2024
@vstinner
Copy link
Member Author

vstinner commented Apr 4, 2024

@colesbury @erlend-aasland @serhiy-storchaka: What do you think of adding this function?

@erlend-aasland
Copy link
Contributor

@colesbury @erlend-aasland @serhiy-storchaka: What do you think of adding this function?

I'm not a part of the C API WG, but I'm +1 ;)

@serhiy-storchaka
Copy link
Member

I do not think it is needed. Adding it means that there is something wrong with PyTuple_GetItem(), but it is not. Tuples are immutable, so as far as we keep a strong reference to a tuple, weak references to its elements are valid. There is a lot of code that relies on this, not only directly, but function call conventions and PyArg_Parse* API.

Adding PyTuple_GetItemRef() will not solve any problem. Replacing PyTuple_GetItem() with PyTuple_GetItemRef() will have significant performance impact, and in many cases just impossible.

@vstinner
Copy link
Member Author

vstinner commented Apr 4, 2024

The problem is not the tuple data structure, the problem here is to promote usage of strong references, rather than borrowed references to prevent crashes caused by dangling pointers. Example where using a borrowed reference is dangerous:

PyObject *tuple = Py_BuildValue("(iii)", 2000, 3000, 4000);
PyObject *number = PyTuple_GetItem(tuple, 0);

Py_DECREF(tuple);
// number is now a dangling pointer

... code using 'number' variable ...

Correct code using only strong references:

PyObject *tuple = Py_BuildValue("(iii)", 2000, 3000, 4000);
PyObject *number = PyTuple_GetItemRef(tuple, 0);

Py_DECREF(tuple);
// number is now a dangling pointer

... code using 'number' variable ...
Py_DECREF(number);

My intent is to have a subset of the C API which does not use borrowed references, or at least, hide functions returning borrowed references, when alternativates using strong references exists. See also PEP 743 – Add Py_COMPAT_API_VERSION to the Python C API.

@vstinner
Copy link
Member Author

vstinner commented Apr 4, 2024

There is a lot of code that relies on this, not only directly, but function call conventions and PyArg_Parse* API.

I don't plan to deprecate PyTuple_GetItem(). You can still use it if you are sure that you are holding a strong reference to the container (the tuple) while using its items.

@erlend-aasland
Copy link
Contributor

I suggest opening an issue on the C API WG bug tracker.

@serhiy-storchaka
Copy link
Member

If you want to create a strong reference, just call Py_INCREF().

@erlend-aasland
Copy link
Contributor

After re-reading PEP-703 I'm +0. Quoting from the PEP:

Note that some APIs that return borrowed references, such as PyTuple_GetItem, are not problematic because tuples are immutable.

@vstinner
Copy link
Member Author

vstinner commented Apr 5, 2024

I already proposed adding PyTuple_GetItemRef() in 2020, but the idea was rejected: issue #86460. Since 2020, Free Threading (PEP 703) was added to Python and borrowed references don't play well with Free Threading.

See also this numpy issue: numpy/numpy#26159

@erlend-aasland
Copy link
Contributor

erlend-aasland commented Apr 5, 2024

I already proposed adding PyTuple_GetItemRef() in 2020, but the idea was rejected: issue #86460. Since 2020, Free Threading (PEP 703) was added to Python and borrowed references don't play well with Free Threading.

See also this numpy issue: numpy/numpy#26159

That issue does not mention PyTuple_GetItem at all ;) Consider me -0 now, since there appears to be no third party need for this API.

@vstinner
Copy link
Member Author

The documentation was completed to clarify that it returns a borrow reference.

vstinner added a commit that referenced this issue Apr 19, 2024
… doc (GH-117920) (#118087)

gh-117518: Clarify PyTuple_GetItem() borrowed reference in the doc (GH-117920)
(cherry picked from commit 4605a19)

Co-authored-by: Victor Stinner <vstinner@python.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic-C-API type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

3 participants