]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128509: Add `PyUnstable_IsImmortal` for finding immortal objects (GH-129182)
authorPeter Bierma <zintensitydev@gmail.com>
Mon, 27 Jan 2025 13:36:33 +0000 (08:36 -0500)
committerGitHub <noreply@github.com>
Mon, 27 Jan 2025 13:36:33 +0000 (14:36 +0100)
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Doc/c-api/object.rst
Doc/whatsnew/3.14.rst
Include/cpython/object.h
Lib/test/test_capi/test_immortal.py
Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst [new file with mode: 0644]
Modules/_testcapi/immortal.c
Modules/_testcapi/object.c
Objects/object.c

index 3a434a4173eafaae6195846a67ae843e2f4dbbd8..934b2ef06d3108cfc759fdb2ca5f02fccc307106 100644 (file)
@@ -613,3 +613,14 @@ Object Protocol
 
    .. versionadded:: 3.14
 
+.. c:function:: int PyUnstable_IsImmortal(PyObject *obj)
+
+   This function returns non-zero if *obj* is :term:`immortal`, and zero
+   otherwise. This function cannot fail.
+
+   .. note::
+
+      Objects that are immortal in one CPython version are not guaranteed to
+      be immortal in another.
+
+   .. versionadded:: next
index 7b9e5aca782d06b757c47073219c368a51043dfc..8d209f597eaf4db7e9adf3f31e82a127f0741120 100644 (file)
@@ -1330,6 +1330,9 @@ New features
   bit-packing Python version numbers.
   (Contributed by Petr Viktorin in :gh:`128629`.)
 
+* Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is :term:`immortal`,
+  for debugging purposes.
+
 
 Porting to Python 3.14
 ----------------------
index ba31e2464abf84e0a3ebb308eb00bafb30a211bb..4c9e4f6c6e043442f933a8960a73fafacc1a8a3f 100644 (file)
@@ -541,3 +541,6 @@ PyAPI_FUNC(PyRefTracer) PyRefTracer_GetTracer(void**);
  * 0 if the runtime ignored it. This function cannot fail.
  */
 PyAPI_FUNC(int) PyUnstable_Object_EnableDeferredRefcount(PyObject *);
+
+/* Check whether the object is immortal. This cannot fail. */
+PyAPI_FUNC(int) PyUnstable_IsImmortal(PyObject *);
index 3e36913ac301c3edf2a44a2b7b5770322eda0346..660e8a0e78936670788470fbe3c660084b977337 100644 (file)
@@ -5,12 +5,22 @@ _testcapi = import_helper.import_module('_testcapi')
 _testinternalcapi = import_helper.import_module('_testinternalcapi')
 
 
-class TestCAPI(unittest.TestCase):
-    def test_immortal_builtins(self):
-        _testcapi.test_immortal_builtins()
+class TestUnstableCAPI(unittest.TestCase):
+    def test_immortal(self):
+        # Not extensive
+        known_immortals = (True, False, None, 0, ())
+        for immortal in known_immortals:
+            with self.subTest(immortal=immortal):
+                self.assertTrue(_testcapi.is_immortal(immortal))
+
+        # Some arbitrary mutable objects
+        non_immortals = (object(), self, [object()])
+        for non_immortal in non_immortals:
+            with self.subTest(non_immortal=non_immortal):
+                self.assertFalse(_testcapi.is_immortal(non_immortal))
+
+        # CRASHES _testcapi.is_immortal(NULL)
 
-    def test_immortal_small_ints(self):
-        _testcapi.test_immortal_small_ints()
 
 class TestInternalCAPI(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst b/Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst
new file mode 100644 (file)
index 0000000..c4a048f
--- /dev/null
@@ -0,0 +1,2 @@
+Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is
+:term:`immortal`.
index 9f81389811c645614d8216045caabe2b0221c9ae..5bdae2e99d537525ac035473ba486bf7e0edb8ca 100644 (file)
@@ -31,9 +31,16 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
     Py_RETURN_NONE;
 }
 
+static PyObject *
+is_immortal(PyObject *self, PyObject *op)
+{
+    return PyBool_FromLong(PyUnstable_IsImmortal(op));
+}
+
 static PyMethodDef test_methods[] = {
     {"test_immortal_builtins",   test_immortal_builtins,     METH_NOARGS},
     {"test_immortal_small_ints", test_immortal_small_ints,   METH_NOARGS},
+    {"is_immortal",              is_immortal,                METH_O},
     {NULL},
 };
 
index 841410c52b3ce2116197a1de86c6dbe00907aaaf..1d0169b2af9469791bcd2a55cdefa651b0b45d07 100644 (file)
@@ -131,6 +131,7 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj)
     return PyLong_FromLong(result);
 }
 
+
 static PyMethodDef test_methods[] = {
     {"call_pyobject_print", call_pyobject_print, METH_VARARGS},
     {"pyobject_print_null", pyobject_print_null, METH_VARARGS},
index 51b6016b9c191c85fcdef7b0b3514367674fca30..eb1a7825c45450b392ead98a719ffed819970309 100644 (file)
@@ -3155,3 +3155,12 @@ Py_REFCNT(PyObject *ob)
 {
     return _Py_REFCNT(ob);
 }
+
+int
+PyUnstable_IsImmortal(PyObject *op)
+{
+    /* Checking a reference count requires a thread state */
+    _Py_AssertHoldsTstate();
+    assert(op != NULL);
+    return _Py_IsImmortal(op);
+}