Skip to content

Commit 90c06a4

Browse files
authored
Add PyUnstable_Object_IsUniquelyReferenced (#149)
1 parent 718e12e commit 90c06a4

File tree

4 files changed

+42
-0
lines changed

4 files changed

+42
-0
lines changed

docs/api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ Python 3.14
208208

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

211+
.. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
212+
213+
See `PyUnstable_Object_IsUniquelyReferenced() documentation <https://docs.python.org/dev/c-api/object.html#c.PyUnstable_Object_IsUniquelyReferenced>`__.
211214

212215
Not supported:
213216

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2025-09-01: Add ``PyUnstable_Object_IsUniquelyReferenced()`` function.
45
* 2025-06-09: Add ``PyUnicodeWriter_WriteASCII()`` function.
56
* 2025-06-03: Add functions:
67

pythoncapi_compat.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,6 +2211,24 @@ PyConfig_GetInt(const char *name, int *value)
22112211
}
22122212
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
22132213

2214+
// gh-133144 added PyUnstable_Object_IsUniquelyReferenced() to Python 3.14.0b1.
2215+
// Adapted from _PyObject_IsUniquelyReferenced() implementation.
2216+
#if PY_VERSION_HEX < 0x030E00B0
2217+
static inline int PyUnstable_Object_IsUniquelyReferenced(PyObject *obj)
2218+
{
2219+
#if !defined(Py_GIL_DISABLED)
2220+
return Py_REFCNT(obj) == 1;
2221+
#else
2222+
// NOTE: the entire ob_ref_shared field must be zero, including flags, to
2223+
// ensure that other threads cannot concurrently create new references to
2224+
// this object.
2225+
return (_Py_IsOwnedByCurrentThread(obj) &&
2226+
_Py_atomic_load_uint32_relaxed(&obj->ob_ref_local) == 1 &&
2227+
_Py_atomic_load_ssize_relaxed(&obj->ob_ref_shared) == 0);
2228+
#endif
2229+
}
2230+
#endif
2231+
22142232

22152233
#if PY_VERSION_HEX < 0x030F0000
22162234
static inline PyObject*

tests/test_pythoncapi_compat_cext.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,25 @@ test_unicodewriter_format(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
19721972
}
19731973
#endif
19741974

1975+
static PyObject *
1976+
test_uniquely_referenced(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
1977+
{
1978+
PyObject *obj = Py_BuildValue("(s, s)", "hello", "world");
1979+
if (obj == NULL) {
1980+
return NULL;
1981+
}
1982+
1983+
assert(PyUnstable_Object_IsUniquelyReferenced(obj));
1984+
1985+
Py_INCREF(obj);
1986+
1987+
assert(!PyUnstable_Object_IsUniquelyReferenced(obj));
1988+
1989+
Py_DECREF(obj);
1990+
Py_DECREF(obj);
1991+
1992+
Py_RETURN_NONE;
1993+
}
19751994

19761995
static PyObject *
19771996
test_bytes(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
@@ -2328,6 +2347,7 @@ static struct PyMethodDef methods[] = {
23282347
{"test_config", test_config, METH_NOARGS, _Py_NULL},
23292348
#endif
23302349
{"test_sys", test_sys, METH_NOARGS, _Py_NULL},
2350+
{"test_uniquely_referenced", test_uniquely_referenced, METH_NOARGS, _Py_NULL},
23312351
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
23322352
};
23332353

0 commit comments

Comments
 (0)