From 2f791743fcb84a0e3ff22d20a1b1b9f63aa83b07 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Sun, 6 Sep 2026 22:49:52 +0100 Subject: [PATCH 1/3] gh-157047: Support sharing of complex types in subinterpreters --- Doc/library/concurrent.interpreters.rst | 1 + Lib/test/test__interpreters.py | 2 ++ Lib/test/test_crossinterp.py | 7 +++++ Lib/test/test_interpreters/test_api.py | 2 ++ ...-09-06-22-43-15.gh-issue-157047.ecyJp1.rst | 1 + Python/crossinterp_data_lookup.h | 29 +++++++++++++++++++ 6 files changed, 42 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst diff --git a/Doc/library/concurrent.interpreters.rst b/Doc/library/concurrent.interpreters.rst index 2c509ab13a6acfd..bfbb12624e7d62c 100644 --- a/Doc/library/concurrent.interpreters.rst +++ b/Doc/library/concurrent.interpreters.rst @@ -189,6 +189,7 @@ objects are either directly shared or copied efficiently. For example: * :class:`str` * :class:`int` * :class:`float` +* :class:`complex` * :class:`tuple` (of similarly supported objects) There are a small number of Python types that actually share mutable diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index ec5a26bc1c05ad8..b37e2261079c878 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -104,6 +104,7 @@ def test_default_shareables(self): True, False, 100.0, + 1+2j, (1, ('spam', 'eggs')), ] for obj in shareables: @@ -603,6 +604,7 @@ def test_shareable_types(self): 'spam', b'spam', 42, + 1+2j, ] for obj in objects: with self.subTest(obj): diff --git a/Lib/test/test_crossinterp.py b/Lib/test/test_crossinterp.py index f4bf5a66ad21550..c298570373ddc44 100644 --- a/Lib/test/test_crossinterp.py +++ b/Lib/test/test_crossinterp.py @@ -124,6 +124,10 @@ def ignore_byteswarning(): -1.0, 0.12345678, -0.12345678, + # complex + 0j, + 1+2j, + -1-2j, ] TUPLE_EXCEPTION = (0, 1.0, EXCEPTION) TUPLE_OBJECT = (0, 1.0, OBJECT) @@ -1326,6 +1330,9 @@ def test_float(self): -0.12345678, ]) + def test_complex(self): + self.assert_roundtrip_equal([0j, 1+2j, -1-2j, 1.5+2.5j]) + def test_tuple(self): self.assert_roundtrip_equal([ (), diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py index aac3cdd717668ca..4b9d88f50647bde 100644 --- a/Lib/test/test_interpreters/test_api.py +++ b/Lib/test/test_interpreters/test_api.py @@ -1306,6 +1306,7 @@ def test_stateless_func_returns_arg(self): for arg in [ None, 10, + 1+2j, 'spam!', b'spam!', (1, 2, 'spam!'), @@ -1903,6 +1904,7 @@ def test_default_shareables(self): True, False, 100.0, + 1+2j, (), (1, ('spam', 'eggs'), True), ] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst new file mode 100644 index 000000000000000..7e15d4e9d63ad01 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst @@ -0,0 +1 @@ +Support sharing of :class:`complex` types in subinterpreters. diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index 54422ad2335cb62..e686d8e3ffd4f8e 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -555,6 +555,30 @@ _float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) return 0; } +// complex + +static PyObject * +_new_complex_object(_PyXIData_t *xidata) +{ + Py_complex * value_ptr = xidata->data; + return PyComplex_FromCComplex(*value_ptr); +} + +static int +_complex_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) +{ + if (_PyXIData_InitWithSize( + xidata, tstate->interp, sizeof(Py_complex), NULL, + _new_complex_object + ) < 0) + { + return -1; + } + Py_complex *shared = (Py_complex *)xidata->data; + *shared = PyComplex_AsCComplex(obj); + return 0; +} + // None static PyObject * @@ -825,6 +849,11 @@ _register_builtins_for_crossinterpreter_data(dlregistry_t *xidregistry) Py_FatalError("could not register float for cross-interpreter sharing"); } + // complex + if (REGISTER(&PyComplex_Type, _complex_shared) != 0) { + Py_FatalError("could not register complex for cross-interpreter sharing"); + } + // tuple if (REGISTER_FALLBACK(&PyTuple_Type, _tuple_shared) != 0) { Py_FatalError("could not register tuple for cross-interpreter sharing"); From 713afa0bb13dbf645d9061650dcd10614cf968a9 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 7 Sep 2026 08:58:21 +0100 Subject: [PATCH 2/3] Reword news entry --- .../2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst index 7e15d4e9d63ad01..2e987f6fb0573f3 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-06-22-43-15.gh-issue-157047.ecyJp1.rst @@ -1 +1 @@ -Support sharing of :class:`complex` types in subinterpreters. +Added support for sharing of :class:`complex` type with interpreters API. From c66bbf5b88ef1128aa1953a4119052b5df072867 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Mon, 7 Sep 2026 10:47:49 +0100 Subject: [PATCH 3/3] Update Python/crossinterp_data_lookup.h Co-authored-by: Pieter Eendebak --- Python/crossinterp_data_lookup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index e686d8e3ffd4f8e..06c7fcfdeb77b0d 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -560,7 +560,7 @@ _float_shared(PyThreadState *tstate, PyObject *obj, _PyXIData_t *xidata) static PyObject * _new_complex_object(_PyXIData_t *xidata) { - Py_complex * value_ptr = xidata->data; + Py_complex *value_ptr = xidata->data; return PyComplex_FromCComplex(*value_ptr); }