Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ Python 3.14

See `PyConfig_GetInt() documentation <https://docs.python.org/dev/c-api/init_config.html#c.PyConfig_GetInt>`__.

.. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op)

See `PyUnstable_Object_IsUniquelyReferenced() documentation <https://docs.python.org/dev/c-api/object.html#c.PyUnstable_Object_IsUniquelyReferenced>`__.

Not supported:

Expand Down
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Changelog
=========

* 2025-09-01: Add ``PyUnstable_Object_IsUniquelyReferenced`` function.
* 2025-06-09: Add ``PyUnicodeWriter_WriteASCII()`` function.
* 2025-06-03: Add functions:

Expand Down
18 changes: 18 additions & 0 deletions pythoncapi_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -2211,6 +2211,24 @@ PyConfig_GetInt(const char *name, int *value)
}
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)

// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1.
// Adapted from _PyObject_IsUniquelyReferenced() implementation.
#if PY_VERSION_HEX < 0x030E00B0
static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj)
{
#if !defined(Py_GIL_DISABLED)
return Py_REFCNT(obj) == 1;
#else
// NOTE: the entire ob_ref_shared field must be zero, including flags, to
// ensure that other threads cannot concurrently create new references to
// this object.
return (_Py_IsOwnedByCurrentThread(obj) &&
_Py_atomic_load_uint32_relaxed(&obj->ob_ref_local) == 1 &&
_Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared) == 0);
#endif
}
#endif


#if PY_VERSION_HEX < 0x030F0000
static inline PyObject*
Expand Down
20 changes: 20 additions & 0 deletions tests/test_pythoncapi_compat_cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,25 @@ test_unicodewriter_format(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
}
#endif

static PyObject *
test_uniquely_referenced(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
{
PyObject *obj = Py_BuildValue("(s, s)", "hello", "world");
if (obj == NULL) {
return NULL;
}

assert(PyUnstable_Object_IsUniquelyReferenced(obj));

Py_INCREF(obj);

assert(!PyUnstable_Object_IsUniquelyReferenced(obj));

Py_DECREF(obj);
Py_DECREF(obj);

Py_RETURN_NONE;
}

static PyObject *
test_bytes(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
Expand Down Expand Up @@ -2328,6 +2347,7 @@ static struct PyMethodDef methods[] = {
{"test_config", test_config, METH_NOARGS, _Py_NULL},
#endif
{"test_sys", test_sys, METH_NOARGS, _Py_NULL},
{"test_uniquely_referenced", test_uniquely_referenced, METH_NOARGS, _Py_NULL},
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
};

Expand Down
Loading