diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 631e5fd2499b13e..90bf49df65151d2 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -1606,6 +1606,12 @@ def test_free_called(self): gc_collect() # For free-threaded build self.assertEqual(LAST_FREED, 100) + f = self.get_func() + SetExtra(f.__code__, FREE_INDEX, None) + del f + gc_collect() + self.assertEqual(LAST_FREED, 100) + def test_get_set(self): # Test basic get/set round tripping. f = self.get_func() diff --git a/Misc/NEWS.d/next/C_API/2026-10-19-20-38-17.gh-issue-159739.b7c351.rst b/Misc/NEWS.d/next/C_API/2026-10-19-20-38-17.gh-issue-159739.b7c351.rst new file mode 100644 index 000000000000000..b123ce7022ca04b --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-10-19-20-38-17.gh-issue-159739.b7c351.rst @@ -0,0 +1,3 @@ +Stop invoking code-extra cleanup callbacks registered with +:c:func:`PyUnstable_Eval_RequestCodeExtraIndex` for ``NULL`` data, as +documented. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 724243d2b8cfae1..3a86b9b08fb19ad 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2415,7 +2415,7 @@ code_dealloc(PyObject *self) for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) { freefunc free_extra = interp->co_extra_freefuncs[i]; - if (free_extra != NULL) { + if (free_extra != NULL && co_extra->ce_extras[i] != NULL) { free_extra(co_extra->ce_extras[i]); } }