gh-128509: Add PyUnstable_IsImmortal
for finding immortal objects (GH-129182)
Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
7ec17429d4
commit
3fb5f6eb9b
@ -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
|
||||
|
@ -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
|
||||
----------------------
|
||||
|
@ -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 *);
|
||||
|
@ -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):
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
Add :c:func:`PyUnstable_IsImmortal` for determining whether an object is
|
||||
:term:`immortal`.
|
@ -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},
|
||||
};
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user