From 0af6ff789e5acc4ab83bb8db75270711357fbb87 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 2 Jun 2025 16:43:32 +0200 Subject: [PATCH 1/6] [3.14] gh-123471: make concurrent iteration over `itertools.cycle` safe under free-threading (#131212) (cherry picked from commit 26a1cd4e8c0c9ea1a6683abd82547ddee656ff3d) Co-authored-by: Kumar Aditya Co-authored-by: Pieter Eendebak --- .../test_free_threading/test_itertools.py | 30 +++++++++++++++++++ ...-03-13-20-48-58.gh-issue-123471.cM4w4f.rst | 1 + Modules/itertoolsmodule.c | 27 ++++++++++------- 3 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index 2d2d4ff1ca1f6f1..ff131f26b4a705a 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -1,5 +1,7 @@ import unittest +from threading import Thread, Barrier from itertools import ( + cycle, tee, ) from test.support import threading_helper @@ -8,6 +10,34 @@ threading_helper.requires_working_threading(module=True) +class ItertoolsThreading(unittest.TestCase): + + @threading_helper.reap_threads + def test_cycle(self): + number_of_threads = 6 + number_of_iterations = 10 + number_of_cycles = 400 + + barrier = Barrier(number_of_threads) + def work(it): + barrier.wait() + for _ in range(number_of_cycles): + _ = next(it) + + data = (1, 2, 3, 4) + for it in range(number_of_iterations): + cycle_iterator = cycle(data) + worker_threads = [] + for ii in range(number_of_threads): + worker_threads.append( + Thread(target=work, args=[cycle_iterator])) + + with threading_helper.start_threads(worker_threads): + pass + + barrier.reset() + + class TestTeeConcurrent(unittest.TestCase): # itertools.tee branches share a linked list of internal data cells. # Concurrent iteration must not corrupt that shared state or crash the diff --git a/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst b/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst new file mode 100644 index 000000000000000..cfc783900de70fe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-13-20-48-58.gh-issue-123471.cM4w4f.rst @@ -0,0 +1 @@ +Make concurrent iterations over :class:`itertools.cycle` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index ffe44595ac59e77..6fae997fa4096f2 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1211,7 +1211,6 @@ typedef struct { PyObject *it; PyObject *saved; Py_ssize_t index; - int firstpass; } cycleobject; #define cycleobject_CAST(op) ((cycleobject *)(op)) @@ -1252,8 +1251,7 @@ itertools_cycle_impl(PyTypeObject *type, PyObject *iterable) } lz->it = it; lz->saved = saved; - lz->index = 0; - lz->firstpass = 0; + lz->index = -1; return (PyObject *)lz; } @@ -1286,11 +1284,11 @@ cycle_next(PyObject *op) cycleobject *lz = cycleobject_CAST(op); PyObject *item; - if (lz->it != NULL) { + Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(lz->index); + + if (index < 0) { item = PyIter_Next(lz->it); if (item != NULL) { - if (lz->firstpass) - return item; if (PyList_Append(lz->saved, item)) { Py_DECREF(item); return NULL; @@ -1300,15 +1298,22 @@ cycle_next(PyObject *op) /* Note: StopIteration is already cleared by PyIter_Next() */ if (PyErr_Occurred()) return NULL; + index = 0; + FT_ATOMIC_STORE_SSIZE_RELAXED(lz->index, 0); +#ifndef Py_GIL_DISABLED Py_CLEAR(lz->it); +#endif } if (PyList_GET_SIZE(lz->saved) == 0) return NULL; - item = PyList_GET_ITEM(lz->saved, lz->index); - lz->index++; - if (lz->index >= PyList_GET_SIZE(lz->saved)) - lz->index = 0; - return Py_NewRef(item); + item = PyList_GetItemRef(lz->saved, index); + assert(item); + index++; + if (index >= PyList_GET_SIZE(lz->saved)) { + index = 0; + } + FT_ATOMIC_STORE_SSIZE_RELAXED(lz->index, index); + return item; } static PyType_Slot cycle_slots[] = { From 42009b60e44164ebc1f1b282f5ab377ae1019036 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 30 Jun 2025 13:31:59 +0200 Subject: [PATCH 2/6] [3.14] gh-123471: Make itertools.product and itertools.combinations thread-safe (GH-132814) (cherry picked from commit 847d1c2cb4014f122df64e0db0b3b554c01779c6) Co-authored-by: Pieter Eendebak Co-authored-by: Kumar Aditya --- .../test_itertools_combinatoric.py | 51 +++++++++++++++++++ ...-04-22-21-00-23.gh-issue-123471.asOLA2.rst | 1 + Modules/itertoolsmodule.c | 24 ++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Lib/test/test_free_threading/test_itertools_combinatoric.py create mode 100644 Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst diff --git a/Lib/test/test_free_threading/test_itertools_combinatoric.py b/Lib/test/test_free_threading/test_itertools_combinatoric.py new file mode 100644 index 000000000000000..5b3b88deedd1210 --- /dev/null +++ b/Lib/test/test_free_threading/test_itertools_combinatoric.py @@ -0,0 +1,51 @@ +import unittest +from threading import Thread, Barrier +from itertools import combinations, product +from test.support import threading_helper + + +threading_helper.requires_working_threading(module=True) + +def test_concurrent_iteration(iterator, number_of_threads): + barrier = Barrier(number_of_threads) + def iterator_worker(it): + barrier.wait() + while True: + try: + _ = next(it) + except StopIteration: + return + + worker_threads = [] + for ii in range(number_of_threads): + worker_threads.append( + Thread(target=iterator_worker, args=[iterator])) + + with threading_helper.start_threads(worker_threads): + pass + + barrier.reset() + +class ItertoolsThreading(unittest.TestCase): + + @threading_helper.reap_threads + def test_combinations(self): + number_of_threads = 10 + number_of_iterations = 24 + + for it in range(number_of_iterations): + iterator = combinations((1, 2, 3, 4, 5), 2) + test_concurrent_iteration(iterator, number_of_threads) + + @threading_helper.reap_threads + def test_product(self): + number_of_threads = 10 + number_of_iterations = 24 + + for it in range(number_of_iterations): + iterator = product((1, 2, 3, 4, 5), (10, 20, 30)) + test_concurrent_iteration(iterator, number_of_threads) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst b/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst new file mode 100644 index 000000000000000..a4b4b6d2c23d495 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-22-21-00-23.gh-issue-123471.asOLA2.rst @@ -0,0 +1 @@ +Make concurrent iterations over :class:`itertools.combinations` and :class:`itertools.product` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 6fae997fa4096f2..e2e550edd1afe3e 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -2177,7 +2177,7 @@ product_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -product_next(PyObject *op) +product_next_lock_held(PyObject *op) { productobject *lz = productobject_CAST(op); PyObject *pool; @@ -2263,6 +2263,16 @@ product_next(PyObject *op) return NULL; } +static PyObject * +product_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = product_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef product_methods[] = { {"__sizeof__", product_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ @@ -2410,7 +2420,7 @@ combinations_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -combinations_next(PyObject *op) +combinations_next_lock_held(PyObject *op) { combinationsobject *co = combinationsobject_CAST(op); PyObject *elem; @@ -2495,6 +2505,16 @@ combinations_next(PyObject *op) return NULL; } +static PyObject * +combinations_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = combinations_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef combinations_methods[] = { {"__sizeof__", combinations_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ From 15c880e2c422797b58b2c1d563cb603611e610a9 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 30 Jun 2025 13:06:58 +0200 Subject: [PATCH 3/6] [3.14] gh-123471: Make itertools.chain thread-safe (#135689) (cherry picked from commit 0533c1faf27d1e50b062bb623dfad93288757f57) Co-authored-by: Pieter Eendebak --- .../test_free_threading/test_itertools.py | 29 +++++++++++++++++++ ...-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst | 1 + Modules/itertoolsmodule.c | 14 +++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index ff131f26b4a705a..f69e88142ace01a 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -1,6 +1,7 @@ import unittest from threading import Thread, Barrier from itertools import ( + chain, cycle, tee, ) @@ -37,6 +38,34 @@ def work(it): barrier.reset() + @threading_helper.reap_threads + def test_chain(self): + number_of_threads = 6 + number_of_iterations = 20 + + barrier = Barrier(number_of_threads) + def work(it): + barrier.wait() + while True: + try: + next(it) + except StopIteration: + break + + data = [(1, )] * 200 + for it in range(number_of_iterations): + chain_iterator = chain(*data) + worker_threads = [] + for ii in range(number_of_threads): + worker_threads.append( + Thread(target=work, args=[chain_iterator])) + + with threading_helper.start_threads(worker_threads): + pass + + barrier.reset() + + class TestTeeConcurrent(unittest.TestCase): # itertools.tee branches share a linked list of internal data cells. diff --git a/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst b/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst new file mode 100644 index 000000000000000..6f395024a9e1790 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-18-19-25-32.gh-issue-123471.lx1Xbt.rst @@ -0,0 +1 @@ +Make concurrent iterations over :class:`itertools.chain` safe under :term:`free threading`. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index e2e550edd1afe3e..0e8961e0bdad7d3 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1967,8 +1967,8 @@ chain_traverse(PyObject *op, visitproc visit, void *arg) return 0; } -static PyObject * -chain_next(PyObject *op) +static inline PyObject * +chain_next_lock_held(PyObject *op) { chainobject *lz = chainobject_CAST(op); PyObject *item; @@ -2006,6 +2006,16 @@ chain_next(PyObject *op) return NULL; } +static PyObject * +chain_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = chain_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + PyDoc_STRVAR(chain_doc, "chain(*iterables)\n\ --\n\ From 98af9225dd6d49f0450544e6e09ee56fbc9e9153 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Wed, 4 Feb 2026 19:38:45 +0100 Subject: [PATCH 4/6] [3.14] gh-123471: Make concurrent iteration over `itertools.permutations` and `itertools.combinations_with_replacement` thread-safe (gh-144402) (cherry picked from commit 009c8c052f5eb9f869c09029724ef194d8c161ca) Co-authored-by: Pieter Eendebak --- .../test_free_threading/test_itertools.py | 24 +++++++++++++++++++ ...-02-03-08-50-58.gh-issue-123471.yF1Gym.rst | 1 + Modules/itertoolsmodule.c | 24 +++++++++++++++++-- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-03-08-50-58.gh-issue-123471.yF1Gym.rst diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index f69e88142ace01a..1309353ac5aa267 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -2,7 +2,9 @@ from threading import Thread, Barrier from itertools import ( chain, + combinations_with_replacement, cycle, + permutations, tee, ) from test.support import threading_helper @@ -11,6 +13,14 @@ threading_helper.requires_working_threading(module=True) +def work_iterator(it): + while True: + try: + next(it) + except StopIteration: + break + + class ItertoolsThreading(unittest.TestCase): @threading_helper.reap_threads @@ -65,6 +75,20 @@ def work(it): barrier.reset() + @threading_helper.reap_threads + def test_combinations_with_replacement(self): + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = combinations_with_replacement(tuple(range(2)), 2) + threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + + @threading_helper.reap_threads + def test_permutations(self): + number_of_iterations = 6 + for _ in range(number_of_iterations): + it = permutations(tuple(range(2)), 2) + threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + class TestTeeConcurrent(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-02-03-08-50-58.gh-issue-123471.yF1Gym.rst b/Misc/NEWS.d/next/Library/2026-02-03-08-50-58.gh-issue-123471.yF1Gym.rst new file mode 100644 index 000000000000000..85e9a03426e1fca --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-03-08-50-58.gh-issue-123471.yF1Gym.rst @@ -0,0 +1 @@ +Make concurrent iteration over :class:`itertools.combinations_with_replacement` and :class:`itertools.permutations` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 0e8961e0bdad7d3..8ab7434a34dc9eb 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -2684,7 +2684,7 @@ cwr_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -cwr_next(PyObject *op) +cwr_next_lock_held(PyObject *op) { cwrobject *co = cwrobject_CAST(op); PyObject *elem; @@ -2763,6 +2763,16 @@ cwr_next(PyObject *op) return NULL; } +static PyObject * +cwr_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = cwr_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef cwr_methods[] = { {"__sizeof__", cwr_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ @@ -2943,7 +2953,7 @@ permutations_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -permutations_next(PyObject *op) +permutations_next_lock_held(PyObject *op) { permutationsobject *po = permutationsobject_CAST(op); PyObject *elem; @@ -3033,6 +3043,16 @@ permutations_next(PyObject *op) return NULL; } +static PyObject * +permutations_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = permutations_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyMethodDef permuations_methods[] = { {"__sizeof__", permutations_sizeof, METH_NOARGS, sizeof_doc}, {NULL, NULL} /* sentinel */ From c1708a9c6ab55139844d3928448b7b21a4ecfb46 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 16 Mar 2026 09:53:37 +0100 Subject: [PATCH 5/6] [3.14] gh-123471: make concurrent iteration over itertools.accumulate thread-safe (#144486) (cherry picked from commit 3a248564470075cb8c7b8a75fe7ba61f7ea341b2) Co-authored-by: Pieter Eendebak --- Lib/test/test_free_threading/test_itertools.py | 8 ++++++++ .../2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst | 1 + Modules/itertoolsmodule.c | 12 +++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index 1309353ac5aa267..f98503530745c44 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -1,6 +1,7 @@ import unittest from threading import Thread, Barrier from itertools import ( + accumulate, chain, combinations_with_replacement, cycle, @@ -89,6 +90,13 @@ def test_permutations(self): it = permutations(tuple(range(2)), 2) threading_helper.run_concurrently(work_iterator, nthreads=6, args=[it]) + @threading_helper.reap_threads + def test_accumulate(self): + number_of_iterations = 10 + for _ in range(number_of_iterations): + it = accumulate(tuple(range(40))) + threading_helper.run_concurrently(work_iterator, nthreads=10, args=[it]) + class TestTeeConcurrent(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst b/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst new file mode 100644 index 000000000000000..d650103e28ee686 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-02-04-20-30-59.gh-issue-123471.1dnPvs.rst @@ -0,0 +1 @@ +Make concurrent iteration over :class:`itertools.accumulate` safe under free-threading. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 8ab7434a34dc9eb..ea1d96e4278ebde 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3160,7 +3160,7 @@ accumulate_traverse(PyObject *op, visitproc visit, void *arg) } static PyObject * -accumulate_next(PyObject *op) +accumulate_next_lock_held(PyObject *op) { accumulateobject *lz = accumulateobject_CAST(op); PyObject *val, *newtotal; @@ -3192,6 +3192,16 @@ accumulate_next(PyObject *op) return newtotal; } +static PyObject * +accumulate_next(PyObject *op) +{ + PyObject *result; + Py_BEGIN_CRITICAL_SECTION(op); + result = accumulate_next_lock_held(op); + Py_END_CRITICAL_SECTION() + return result; +} + static PyType_Slot accumulate_slots[] = { {Py_tp_dealloc, accumulate_dealloc}, {Py_tp_getattro, PyObject_GenericGetAttr}, From 6935fd134768b2d7676cd7820688da0161e54674 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 27 Mar 2026 15:01:49 +0100 Subject: [PATCH 6/6] [3.14] gh-123471: Make `itertools.zip_longest` safe in the FT build (#146033) (cherry picked from commit 9214e3f33eeeb0ee862777378f98fdeb7b6944c6) Co-authored-by: Pieter Eendebak --- Lib/test/test_free_threading/test_itertools.py | 8 ++++++++ .../2026-03-17-19-51-05.gh-issue-123471.oY4UR5.rst | 1 + Modules/itertoolsmodule.c | 12 +++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-03-17-19-51-05.gh-issue-123471.oY4UR5.rst diff --git a/Lib/test/test_free_threading/test_itertools.py b/Lib/test/test_free_threading/test_itertools.py index f98503530745c44..0703c7e5e2916e8 100644 --- a/Lib/test/test_free_threading/test_itertools.py +++ b/Lib/test/test_free_threading/test_itertools.py @@ -7,6 +7,7 @@ cycle, permutations, tee, + zip_longest, ) from test.support import threading_helper @@ -97,6 +98,13 @@ def test_accumulate(self): it = accumulate(tuple(range(40))) threading_helper.run_concurrently(work_iterator, nthreads=10, 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]) + class TestTeeConcurrent(unittest.TestCase): 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 000000000000000..8d2e1b970e81711 --- /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 ea1d96e4278ebde..c307484d13377d4 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3960,7 +3960,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; @@ -4031,6 +4031,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\