From 1fd879d4c1fd31c94f3d45c1bc431303a4b0ea04 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 20 Oct 2026 15:18:41 -0700 Subject: [PATCH 1/2] gh-159739: Do not intern complex subclasses in code constants Backport to 3.15 and 3.14. --- Lib/test/test_code.py | 11 ++++++++++- .../2026-10-20-00-00-00.gh-issue-159739.d76e6d.rst | 2 ++ Objects/codeobject.c | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-20-00-00-00.gh-issue-159739.d76e6d.rst diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index 631e5fd2499b13e..c89de1e98b43942 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -1251,10 +1251,19 @@ def __hash__(self): class MyInt(int): pass + hash_calls = [] + + class MyComplex(complex): + def __hash__(self): + hash_calls.append(self) + return super().__hash__() + code = compile("a = 1", "", "exec") - code = code.replace(co_consts=(1, Unhashable(), MyInt(1), MyInt(1))) + code = code.replace(co_consts=(1, Unhashable(), MyInt(1), MyInt(1), + MyComplex(1j))) self.assertIsInstance(code.co_consts[1], Unhashable) self.assertEqual(code.co_consts[2], code.co_consts[3]) + self.assertEqual(hash_calls, []) class CodeWeakRefTest(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-20-00-00-00.gh-issue-159739.d76e6d.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-20-00-00-00.gh-issue-159739.d76e6d.rst new file mode 100644 index 000000000000000..a027883426be196 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-20-00-00-00.gh-issue-159739.d76e6d.rst @@ -0,0 +1,2 @@ +Prevent free-threaded builds from interning and immortalizing complex +subclasses in code object constants. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 724243d2b8cfae1..eb390ab24ec13e6 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -174,7 +174,7 @@ should_immortalize_constant(PyObject *v) _Py_IsImmortal(slice->step)); } return (PyLong_CheckExact(v) || PyFloat_CheckExact(v) || - PyComplex_Check(v) || PyBytes_CheckExact(v)); + PyComplex_CheckExact(v) || PyBytes_CheckExact(v)); } #endif From 9979b6e71ab1d8293084d2ce45f48c28139708a7 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 21 Oct 2026 11:01:13 -0700 Subject: [PATCH 2/2] gh-159739: Check complex constant immortality directly --- Lib/test/test_code.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index c89de1e98b43942..f54703cf37b2de5 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -1251,19 +1251,15 @@ def __hash__(self): class MyInt(int): pass - hash_calls = [] - class MyComplex(complex): - def __hash__(self): - hash_calls.append(self) - return super().__hash__() + pass code = compile("a = 1", "", "exec") code = code.replace(co_consts=(1, Unhashable(), MyInt(1), MyInt(1), MyComplex(1j))) self.assertIsInstance(code.co_consts[1], Unhashable) self.assertEqual(code.co_consts[2], code.co_consts[3]) - self.assertEqual(hash_calls, []) + self.assertFalse(sys._is_immortal(code.co_consts[4])) class CodeWeakRefTest(unittest.TestCase):