From 58ac265e9299195a5f8f2117befb61a66c3e594d Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 09:20:55 -0500 Subject: [PATCH 01/16] Add PyUnstable_IsImmortal --- Doc/c-api/object.rst | 16 ++++++++++++++++ Objects/object.c | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 3a434a4173eafa..a9e66d4dc456d4 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -613,3 +613,19 @@ Object Protocol .. versionadded:: 3.14 +.. c:function:: int PyUnstable_IsImmortal(PyObject *obj) + + This function returns ``1`` if *obj* is :term:`immortal`, and ``0`` otherwise. + + Immortal objects don't care about reference counting, thus they no-op calls to :c:func:`Py_INCREF` + and :c:func:`Py_DECREF`. Some immutable objects such as literal strings, small integers, or special tuples + might be made immortal as an optimization by the interpreter. + + On the :term:`free-threaded ` build, some objects that support deferred reference counting + (see :c:func:`PyUnstable_Object_EnableDeferredRefcount`) might also be immortalized. + + .. note:: + + Objects that are immortal in one version are not guarunteed to be immortal in another. + + .. versionadded:: next diff --git a/Objects/object.c b/Objects/object.c index 0a15c1e1de5499..05366df91c191d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -3155,3 +3155,11 @@ Py_REFCNT(PyObject *ob) { return _Py_REFCNT(ob); } + +int +PyUnstable_IsImmortal(PyObject *op) +{ + /* Checking a reference count requires a thread state */ + _Py_AssertHoldsTstate(); + return _Py_IsImmortal(op); +} From 8a64537e0694aa0ead5ecc7230c86cd1a2645c44 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 09:24:26 -0500 Subject: [PATCH 02/16] Add a test. --- Doc/c-api/object.rst | 2 +- Include/cpython/object.h | 3 +++ Lib/test/test_capi/test_immortal.py | 14 ++++++++++++++ Modules/_testcapimodule.c | 7 +++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index a9e66d4dc456d4..bf985e4b0c3790 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -615,7 +615,7 @@ Object Protocol .. c:function:: int PyUnstable_IsImmortal(PyObject *obj) - This function returns ``1`` if *obj* is :term:`immortal`, and ``0`` otherwise. + This function returns ``1`` if *obj* is :term:`immortal`, and ``0`` otherwise. This function cannot fail. Immortal objects don't care about reference counting, thus they no-op calls to :c:func:`Py_INCREF` and :c:func:`Py_DECREF`. Some immutable objects such as literal strings, small integers, or special tuples 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..d6059b39e8e248 100644 --- a/Lib/test/test_capi/test_immortal.py +++ b/Lib/test/test_capi/test_immortal.py @@ -27,6 +27,20 @@ def test_immortal_builtins(self): for obj in ([], {}, set()): self.assertFalse(_testinternalcapi.is_static_immortal(obj)) + 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)) + + if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 996b96bc000450..1d7d37e9168741 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3487,6 +3487,12 @@ tracemalloc_track_race(PyObject *self, PyObject *args) #undef NTHREAD } +static PyObject * +is_immortal(PyObject *self, PyObject *op) +{ + return PyUnstable_IsImmortal(op); +} + static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -3630,6 +3636,7 @@ static PyMethodDef TestMethods[] = { {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, + {"is_immortal", is_immortal, METH_O} {NULL, NULL} /* sentinel */ }; From 4b55134b37da63056f3ad0a449357c1a36454d08 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 09:26:10 -0500 Subject: [PATCH 03/16] Fix test. --- Modules/_testcapimodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 1d7d37e9168741..537453c294daee 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3490,7 +3490,7 @@ tracemalloc_track_race(PyObject *self, PyObject *args) static PyObject * is_immortal(PyObject *self, PyObject *op) { - return PyUnstable_IsImmortal(op); + return PyBool_FromLong(PyUnstable_IsImmortal(op)); } static PyMethodDef TestMethods[] = { @@ -3636,7 +3636,7 @@ static PyMethodDef TestMethods[] = { {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, - {"is_immortal", is_immortal, METH_O} + {"is_immortal", is_immortal, METH_O}, {NULL, NULL} /* sentinel */ }; From 6bddf33222207880a3abed1728d22fa8d8194613 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 09:27:34 -0500 Subject: [PATCH 04/16] Add a whatsnew entry. --- Doc/whatsnew/3.14.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 685a09dc70eec2..d004fc7ee0bc01 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -1321,6 +1321,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 ---------------------- From 412a42ba7ee2e3e560b61bef1c642ea9a6e94981 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 09:28:08 -0500 Subject: [PATCH 05/16] Add blurb --- .../next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/C_API/2025-01-22-09-28-04.gh-issue-128509.gqQ36L.rst 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`. From 21cf2c4dcd6e650c47eb599f1108d0f4baa0900a Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 12:06:50 -0500 Subject: [PATCH 06/16] Update object.rst Co-authored-by: Victor Stinner --- Doc/c-api/object.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index bf985e4b0c3790..894b88087481d4 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -626,6 +626,6 @@ Object Protocol .. note:: - Objects that are immortal in one version are not guarunteed to be immortal in another. + Objects that are immortal in one Python version are not guarunteed to be immortal in another. .. versionadded:: next From 8d20c00e5fbb37e582615e51d5b7af08859a3148 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 12:06:55 -0500 Subject: [PATCH 07/16] Update object.rst Co-authored-by: Victor Stinner --- Doc/c-api/object.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 894b88087481d4..badd4b8a1083ca 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -615,7 +615,7 @@ Object Protocol .. c:function:: int PyUnstable_IsImmortal(PyObject *obj) - This function returns ``1`` if *obj* is :term:`immortal`, and ``0`` otherwise. This function cannot fail. + This function returns non-zero if *obj* is :term:`immortal`, and zero otherwise. This function cannot fail. Immortal objects don't care about reference counting, thus they no-op calls to :c:func:`Py_INCREF` and :c:func:`Py_DECREF`. Some immutable objects such as literal strings, small integers, or special tuples From 831626cec7042570e29a6ae6272413faffc3fa6d Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 16:14:54 -0500 Subject: [PATCH 08/16] Remove some information. --- Doc/c-api/object.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index badd4b8a1083ca..bd29b45bb2f3f0 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -615,14 +615,8 @@ Object Protocol .. c:function:: int PyUnstable_IsImmortal(PyObject *obj) - This function returns non-zero if *obj* is :term:`immortal`, and zero otherwise. This function cannot fail. - - Immortal objects don't care about reference counting, thus they no-op calls to :c:func:`Py_INCREF` - and :c:func:`Py_DECREF`. Some immutable objects such as literal strings, small integers, or special tuples - might be made immortal as an optimization by the interpreter. - - On the :term:`free-threaded ` build, some objects that support deferred reference counting - (see :c:func:`PyUnstable_Object_EnableDeferredRefcount`) might also be immortalized. + This function returns non-zero if *obj* is :term:`immortal`, and zero + otherwise. This function cannot fail. .. note:: From 422edf9b86ad107655855253ffe870646ace5120 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Wed, 22 Jan 2025 16:16:18 -0500 Subject: [PATCH 09/16] Move test function. --- Modules/_testcapi/object.c | 9 +++++++++ Modules/_testcapimodule.c | 7 ------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 841410c52b3ce2..9c11892238a7ea 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -131,6 +131,14 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) return PyLong_FromLong(result); } + +static PyObject * +is_immortal(PyObject *self, PyObject *op) +{ + return PyBool_FromLong(PyUnstable_IsImmortal(op)); +} + + static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, @@ -138,6 +146,7 @@ static PyMethodDef test_methods[] = { {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, + {"is_immortal", is_immortal, METH_O}, {NULL}, }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 537453c294daee..996b96bc000450 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3487,12 +3487,6 @@ tracemalloc_track_race(PyObject *self, PyObject *args) #undef NTHREAD } -static PyObject * -is_immortal(PyObject *self, PyObject *op) -{ - return PyBool_FromLong(PyUnstable_IsImmortal(op)); -} - static PyMethodDef TestMethods[] = { {"set_errno", set_errno, METH_VARARGS}, {"test_config", test_config, METH_NOARGS}, @@ -3636,7 +3630,6 @@ static PyMethodDef TestMethods[] = { {"test_atexit", test_atexit, METH_NOARGS}, {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS}, - {"is_immortal", is_immortal, METH_O}, {NULL, NULL} /* sentinel */ }; From 6014587cc04949f797b6406df87738a68c18a48a Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 23 Jan 2025 08:03:56 -0500 Subject: [PATCH 10/16] Update object.c Co-authored-by: Serhiy Storchaka --- Modules/_testcapi/object.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 9c11892238a7ea..02fd9443342315 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -135,7 +135,8 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) static PyObject * is_immortal(PyObject *self, PyObject *op) { - return PyBool_FromLong(PyUnstable_IsImmortal(op)); + NULLABLE(op) + return PyLong_FromLong(PyUnstable_IsImmortal(op)); } From 48c974cc30eaeda054be84e06ec017b5f10170db Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 23 Jan 2025 08:04:24 -0500 Subject: [PATCH 11/16] Update object.rst Co-authored-by: Petr Viktorin --- Doc/c-api/object.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index bd29b45bb2f3f0..934b2ef06d3108 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -620,6 +620,7 @@ Object Protocol .. note:: - Objects that are immortal in one Python version are not guarunteed to be immortal in another. + Objects that are immortal in one CPython version are not guaranteed to + be immortal in another. .. versionadded:: next From dd19a02b76813dd397eb24e6c604af603cddc9a5 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 23 Jan 2025 08:17:23 -0500 Subject: [PATCH 12/16] Add non-null assertion --- Objects/object.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/object.c b/Objects/object.c index 05366df91c191d..adc376e74eec25 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -3161,5 +3161,6 @@ PyUnstable_IsImmortal(PyObject *op) { /* Checking a reference count requires a thread state */ _Py_AssertHoldsTstate(); + assert(op != NULL); return _Py_IsImmortal(op); } From 99e897f6f98aab69dd387890e485cf9d61cce4cc Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 23 Jan 2025 08:19:01 -0500 Subject: [PATCH 13/16] Revert "Update object.c" This reverts commit 6014587cc04949f797b6406df87738a68c18a48a. --- Modules/_testcapi/object.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 02fd9443342315..9c11892238a7ea 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -135,8 +135,7 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) static PyObject * is_immortal(PyObject *self, PyObject *op) { - NULLABLE(op) - return PyLong_FromLong(PyUnstable_IsImmortal(op)); + return PyBool_FromLong(PyUnstable_IsImmortal(op)); } From 0448dafbba46ceed50462a7c7309b5e6a567e4c7 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Thu, 23 Jan 2025 08:55:06 -0500 Subject: [PATCH 14/16] Update Lib/test/test_capi/test_immortal.py Co-authored-by: Serhiy Storchaka --- Lib/test/test_capi/test_immortal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_capi/test_immortal.py b/Lib/test/test_capi/test_immortal.py index d6059b39e8e248..7dc1344f2f4f20 100644 --- a/Lib/test/test_capi/test_immortal.py +++ b/Lib/test/test_capi/test_immortal.py @@ -40,6 +40,7 @@ def test_immortal(self): with self.subTest(non_immortal=non_immortal): self.assertFalse(_testcapi.is_immortal(non_immortal)) + # CRASHES _testcapi.is_immortal(NULL) if __name__ == "__main__": From 5b949b1b93f665dd27bd06b27fbba8a147cd1bb5 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 24 Jan 2025 13:23:27 +0000 Subject: [PATCH 15/16] Prevent tests from running twice and move is_immortal() --- Lib/test/test_capi/test_immortal.py | 35 +++++++++++++---------------- Modules/_testcapi/immortal.c | 7 ++++++ Modules/_testcapi/object.c | 8 ------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/Lib/test/test_capi/test_immortal.py b/Lib/test/test_capi/test_immortal.py index 7dc1344f2f4f20..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): @@ -27,21 +37,6 @@ def test_immortal_builtins(self): for obj in ([], {}, set()): self.assertFalse(_testinternalcapi.is_static_immortal(obj)) - 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) - if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index 9f81389811c645..c16834dfab5731 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 9c11892238a7ea..1d0169b2af9469 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -132,13 +132,6 @@ pyobject_enable_deferred_refcount(PyObject *self, PyObject *obj) } -static PyObject * -is_immortal(PyObject *self, PyObject *op) -{ - return PyBool_FromLong(PyUnstable_IsImmortal(op)); -} - - static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, @@ -146,7 +139,6 @@ static PyMethodDef test_methods[] = { {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, - {"is_immortal", is_immortal, METH_O}, {NULL}, }; From d1c9b1fccb3414dda311cbd4a2df9650fda3ac7d Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Fri, 24 Jan 2025 13:29:00 +0000 Subject: [PATCH 16/16] Fix missing comma. --- Modules/_testcapi/immortal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index c16834dfab5731..5bdae2e99d5375 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -40,7 +40,7 @@ is_immortal(PyObject *self, PyObject *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} + {"is_immortal", is_immortal, METH_O}, {NULL}, };