https://github.com/python/cpython/commit/3fb5f6eb9b2b966142b576610210d2b491c1eebb
commit: 3fb5f6eb9b2b966142b576610210d2b491c1eebb
branch: main
author: Peter Bierma <[email protected]>
committer: encukou <[email protected]>
date: 2025-01-27T14:36:33+01:00
summary:

gh-128509: Add `PyUnstable_IsImmortal` for finding immortal objects (GH-129182)


Co-authored-by: Victor Stinner <[email protected]>
Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Petr Viktorin <[email protected]>

files:
A Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst
M Doc/c-api/object.rst
M Doc/whatsnew/3.14.rst
M Include/cpython/object.h
M Lib/test/test_capi/test_immortal.py
M Modules/_testcapi/immortal.c
M Modules/_testcapi/object.c
M Objects/object.c

diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 3a434a4173eafa..934b2ef06d3108 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -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
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 7b9e5aca782d06..8d209f597eaf4d 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -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
 ----------------------
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index ba31e2464abf84..4c9e4f6c6e0434 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -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 *);
diff --git a/Lib/test/test_capi/test_immortal.py 
b/Lib/test/test_capi/test_immortal.py
index 3e36913ac301c3..660e8a0e789366 100644
--- a/Lib/test/test_capi/test_immortal.py
+++ b/Lib/test/test_capi/test_immortal.py
@@ -5,12 +5,22 @@
 _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
index 00000000000000..c4a048fe3195d1
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst
@@ -0,0 +1,2 @@
+Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is
+:term:`immortal`.
diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c
index 9f81389811c645..5bdae2e99d5375 100644
--- a/Modules/_testcapi/immortal.c
+++ b/Modules/_testcapi/immortal.c
@@ -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},
 };
 
diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c
index 841410c52b3ce2..1d0169b2af9469 100644
--- a/Modules/_testcapi/object.c
+++ b/Modules/_testcapi/object.c
@@ -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},
diff --git a/Objects/object.c b/Objects/object.c
index 51b6016b9c191c..eb1a7825c45450 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -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);
+}

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to