Skip to content

Commit 3fb5f6e

Browse files
ZeroIntensityvstinnerserhiy-storchakaencukou
authored
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]>
1 parent 7ec1742 commit 3fb5f6e

File tree

8 files changed

+51
-5
lines changed

8 files changed

+51
-5
lines changed

Doc/c-api/object.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,3 +613,14 @@ Object Protocol
613613
614614
.. versionadded:: 3.14
615615
616+
.. c:function:: int PyUnstable_IsImmortal(PyObject *obj)
617+
618+
This function returns non-zero if *obj* is :term:`immortal`, and zero
619+
otherwise. This function cannot fail.
620+
621+
.. note::
622+
623+
Objects that are immortal in one CPython version are not guaranteed to
624+
be immortal in another.
625+
626+
.. versionadded:: next

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,9 @@ New features
13301330
bit-packing Python version numbers.
13311331
(Contributed by Petr Viktorin in :gh:`128629`.)
13321332

1333+
* Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is :term:`immortal`,
1334+
for debugging purposes.
1335+
13331336

13341337
Porting to Python 3.14
13351338
----------------------

Include/cpython/object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,3 +541,6 @@ PyAPI_FUNC(PyRefTracer) PyRefTracer_GetTracer(void**);
541541
* 0 if the runtime ignored it. This function cannot fail.
542542
*/
543543
PyAPI_FUNC(int) PyUnstable_Object_EnableDeferredRefcount(PyObject *);
544+
545+
/* Check whether the object is immortal. This cannot fail. */
546+
PyAPI_FUNC(int) PyUnstable_IsImmortal(PyObject *);

Lib/test/test_capi/test_immortal.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
_testinternalcapi = import_helper.import_module('_testinternalcapi')
66

77

8-
class TestCAPI(unittest.TestCase):
9-
def test_immortal_builtins(self):
10-
_testcapi.test_immortal_builtins()
8+
class TestUnstableCAPI(unittest.TestCase):
9+
def test_immortal(self):
10+
# Not extensive
11+
known_immortals = (True, False, None, 0, ())
12+
for immortal in known_immortals:
13+
with self.subTest(immortal=immortal):
14+
self.assertTrue(_testcapi.is_immortal(immortal))
15+
16+
# Some arbitrary mutable objects
17+
non_immortals = (object(), self, [object()])
18+
for non_immortal in non_immortals:
19+
with self.subTest(non_immortal=non_immortal):
20+
self.assertFalse(_testcapi.is_immortal(non_immortal))
21+
22+
# CRASHES _testcapi.is_immortal(NULL)
1123

12-
def test_immortal_small_ints(self):
13-
_testcapi.test_immortal_small_ints()
1424

1525
class TestInternalCAPI(unittest.TestCase):
1626

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is
2+
:term:`immortal`.

Modules/_testcapi/immortal.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,16 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
3131
Py_RETURN_NONE;
3232
}
3333

34+
static PyObject *
35+
is_immortal(PyObject *self, PyObject *op)
36+
{
37+
return PyBool_FromLong(PyUnstable_IsImmortal(op));
38+
}
39+
3440
static PyMethodDef test_methods[] = {
3541
{"test_immortal_builtins", test_immortal_builtins, METH_NOARGS},
3642
{"test_immortal_small_ints", test_immortal_small_ints, METH_NOARGS},
43+
{"is_immortal", is_immortal, METH_O},
3744
{NULL},
3845
};
3946

Modules/_testcapi/object.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj)
131131
return PyLong_FromLong(result);
132132
}
133133

134+
134135
static PyMethodDef test_methods[] = {
135136
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
136137
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},

Objects/object.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,3 +3155,12 @@ Py_REFCNT(PyObject *ob)
31553155
{
31563156
return _Py_REFCNT(ob);
31573157
}
3158+
3159+
int
3160+
PyUnstable_IsImmortal(PyObject *op)
3161+
{
3162+
/* Checking a reference count requires a thread state */
3163+
_Py_AssertHoldsTstate();
3164+
assert(op != NULL);
3165+
return _Py_IsImmortal(op);
3166+
}

0 commit comments

Comments
 (0)