caller must hold a :term:`strong reference` to *obj* when calling this.
.. versionadded:: 3.14
+
+.. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
+
+ Determine if *op* only has one reference.
+
+ On GIL-enabled builds, this function is equivalent to
+ :c:expr:`Py_REFCNT(op) == 1`.
+
+ On a :term:`free threaded <free threading>` build, this checks if *op*'s
+ :term:`reference count` is equal to one and additionally checks if *op*
+ is only used by this thread. :c:expr:`Py_REFCNT(op) == 1` is **not**
+ thread-safe on free threaded builds; prefer this function.
+
+ The caller must hold an :term:`attached thread state`, despite the fact
+ that this function doesn't call into the Python interpreter. This function
+ cannot fail.
+
+ .. versionadded:: 3.14
Use the :c:func:`Py_SET_REFCNT()` function to set an object reference count.
- See also the function :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary()`.
+ .. note::
+
+ On :term:`free threaded <free threading>` builds of Python, returning 1
+ isn't sufficient to determine if it's safe to treat *o* as having no
+ access by other threads. Use :c:func:`PyUnstable_Object_IsUniquelyReferenced`
+ for that instead.
+
+ See also the function :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary()`.
.. versionchanged:: 3.10
:c:func:`Py_REFCNT()` is changed to the inline static function.
be used in some cases as a replacement for checking if :c:func:`Py_REFCNT`
is ``1`` for Python objects passed as arguments to C API functions.
+* Add :c:func:`PyUnstable_Object_IsUniquelyReferenced` as a replacement for
+ ``Py_REFCNT(op) == 1`` on :term:`free threaded <free threading>` builds.
+ (Contributed by Peter Bierma in :gh:`133140`.)
+
Limited C API changes
---------------------
// before calling this function in order to avoid spurious failures.
PyAPI_FUNC(int) PyUnstable_TryIncRef(PyObject *);
PyAPI_FUNC(void) PyUnstable_EnableTryIncRef(PyObject *);
+
+PyAPI_FUNC(int) PyUnstable_Object_IsUniquelyReferenced(PyObject *);
self.assertTrue(_testinternalcapi.has_deferred_refcount(silly_list))
+class IsUniquelyReferencedTest(unittest.TestCase):
+ """Test PyUnstable_Object_IsUniquelyReferenced"""
+ def test_is_uniquely_referenced(self):
+ self.assertTrue(_testcapi.is_uniquely_referenced(object()))
+ self.assertTrue(_testcapi.is_uniquely_referenced([]))
+ # Immortals
+ self.assertFalse(_testcapi.is_uniquely_referenced("spanish inquisition"))
+ self.assertFalse(_testcapi.is_uniquely_referenced(42))
+ # CRASHES is_uniquely_referenced(NULL)
+
class CAPITest(unittest.TestCase):
def check_negative_refcount(self, code):
# bpo-35059: Check that Py_DECREF() reports the correct filename
--- /dev/null
+Add :c:func:`PyUnstable_Object_IsUniquelyReferenced` as a replacement for
+``Py_REFNCT(op) == 1`` on :term:`free threaded <free threading>`
+builds of Python.
}
+static PyObject *
+is_uniquely_referenced(PyObject *self, PyObject *op)
+{
+ return PyBool_FromLong(PyUnstable_Object_IsUniquelyReferenced(op));
+}
+
+
static PyMethodDef test_methods[] = {
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
{"test_py_is_macros", test_py_is_macros, METH_NOARGS},
{"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
+ {"is_uniquely_referenced", is_uniquely_referenced, METH_O},
{NULL},
};
assert(op != NULL);
return _Py_IsImmortal(op);
}
+
+int
+PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
+{
+ _Py_AssertHoldsTstate();
+ assert(op != NULL);
+ return _PyObject_IsUniquelyReferenced(op);
+}