From: Pieter Eendebak Date: Fri, 27 Mar 2026 14:01:49 +0000 (+0100) Subject: gh-123471: Make `itertools.zip_longest` safe in the FT build (#146033) X-Git-Tag: v3.15.0a8~146 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9214e3f33eeeb0ee862777378f98fdeb7b6944c6;p=thirdparty%2FPython%2Fcpython.git gh-123471: Make `itertools.zip_longest` safe in the FT build (#146033) --- diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index 20135dd3165a..670d4ca8835e 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -1,5 +1,5 @@ import unittest -from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, permutations +from itertools import accumulate, batched, chain, combinations_with_replacement, cycle, permutations, zip_longest from test.support import threading_helper @@ -62,6 +62,13 @@ class ItertoolsThreading(unittest.TestCase): it = permutations(tuple(range(4)), 2) threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + @threading_helper.reap_threads + def test_zip_longest(self): + number_of_iterations = 10 + for _ in range(number_of_iterations): + it = zip_longest(list(range(4)), list(range(8)), fillvalue=0) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2026-03-17-19-51-05.gh-issue-123471.oY4UR5.rst b/Misc/NEWS.d/next/Library/2026-03-17-19-51-05.gh-issue-123471.oY4UR5.rst new file mode 100644 index 000000000000..8d2e1b970e81 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-17-19-51-05.gh-issue-123471.oY4UR5.rst @@ -0,0 +1 @@ +Make concurrent iteration over :class:`itertools.zip_longest` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 48f1d1c7fde1..cf49724b8861 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3876,7 +3876,7 @@ zip_longest_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -zip_longest_next(PyObject *op) +zip_longest_next_lock_held(PyObject *op) { ziplongestobject *lz = ziplongestobject_CAST(op); Py_ssize_t i; @@ -3947,6 +3947,16 @@ zip_longest_next(PyObject *op) return result; } +static PyObject * +zip_longest_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = zip_longest_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + PyDoc_STRVAR(zip_longest_doc, "zip_longest(*iterables, fillvalue=None)\n\ --\n\