Skip to content

Commit

Permalink
pythongh-128509: Add PyUnstable_IsImmortal for finding immortal obj…
Browse files Browse the repository at this point in the history
…ects (pythonGH-129182)

Co-authored-by: Victor Stinner <[email protected]>
Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Petr Viktorin <[email protected]>
  • Loading branch information
4 people authored Jan 27, 2025
1 parent 7ec1742 commit 3fb5f6e
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 5 deletions.
11 changes: 11 additions & 0 deletions Doc/c-api/object.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------
Expand Down
3 changes: 3 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
20 changes: 15 additions & 5 deletions Lib/test/test_capi/test_immortal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is
:term:`immortal`.
7 changes: 7 additions & 0 deletions Modules/_testcapi/immortal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down
1 change: 1 addition & 0 deletions Modules/_testcapi/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
9 changes: 9 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

0 comments on commit 3fb5f6e

Please sign in to comment.