Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 79fbeee

Browse files
Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now
handle exceptions raised by an iterator. Patch by Alon Diamant and Davin Potts.
1 parent f0f14f7 commit 79fbeee

4 files changed

Lines changed: 75 additions & 14 deletions

File tree

Lib/multiprocessing/pool.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -374,25 +374,34 @@ def _handle_tasks(taskqueue, put, outqueue, pool, cache):
374374
thread = threading.current_thread()
375375

376376
for taskseq, set_length in iter(taskqueue.get, None):
377+
task = None
377378
i = -1
378-
for i, task in enumerate(taskseq):
379-
if thread._state:
380-
util.debug('task handler found thread._state != RUN')
381-
break
382-
try:
383-
put(task)
384-
except Exception as e:
385-
job, ind = task[:2]
379+
try:
380+
for i, task in enumerate(taskseq):
381+
if thread._state:
382+
util.debug('task handler found thread._state != RUN')
383+
break
386384
try:
387-
cache[job]._set(ind, (False, e))
388-
except KeyError:
389-
pass
390-
else:
385+
put(task)
386+
except Exception as e:
387+
job, ind = task[:2]
388+
try:
389+
cache[job]._set(ind, (False, e))
390+
except KeyError:
391+
pass
392+
else:
393+
if set_length:
394+
util.debug('doing set_length()')
395+
set_length(i+1)
396+
continue
397+
break
398+
except Exception as ex:
399+
job, ind = task[:2] if task else (0, 0)
400+
if job in cache:
401+
cache[job]._set(ind + 1, (False, ex))
391402
if set_length:
392403
util.debug('doing set_length()')
393404
set_length(i+1)
394-
continue
395-
break
396405
else:
397406
util.debug('task handler got sentinel')
398407

Lib/test/_test_multiprocessing.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,14 @@ def sqr(x, wait=0.0):
16601660
def mul(x, y):
16611661
return x*y
16621662

1663+
class SayWhenError(ValueError): pass
1664+
1665+
def exception_throwing_generator(total, when):
1666+
for i in range(total):
1667+
if i == when:
1668+
raise SayWhenError("Somebody said when")
1669+
yield i
1670+
16631671
class _TestPool(BaseTestCase):
16641672

16651673
@classmethod
@@ -1758,13 +1766,51 @@ def test_imap(self):
17581766
self.assertEqual(next(it), i*i)
17591767
self.assertRaises(StopIteration, it.__next__)
17601768

1769+
def test_imap_handle_iterable_exception(self):
1770+
if self.TYPE == 'manager':
1771+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
1772+
1773+
it = self.pool.imap(sqr, exception_throwing_generator(10, 3), 1)
1774+
for i in range(3):
1775+
self.assertEqual(next(it), i*i)
1776+
self.assertRaises(SayWhenError, it.__next__)
1777+
1778+
# SayWhenError seen at start of problematic chunk's results
1779+
it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 2)
1780+
for i in range(6):
1781+
self.assertEqual(next(it), i*i)
1782+
self.assertRaises(SayWhenError, it.__next__)
1783+
it = self.pool.imap(sqr, exception_throwing_generator(20, 7), 4)
1784+
for i in range(4):
1785+
self.assertEqual(next(it), i*i)
1786+
self.assertRaises(SayWhenError, it.__next__)
1787+
17611788
def test_imap_unordered(self):
17621789
it = self.pool.imap_unordered(sqr, list(range(1000)))
17631790
self.assertEqual(sorted(it), list(map(sqr, list(range(1000)))))
17641791

17651792
it = self.pool.imap_unordered(sqr, list(range(1000)), chunksize=53)
17661793
self.assertEqual(sorted(it), list(map(sqr, list(range(1000)))))
17671794

1795+
def test_imap_unordered_handle_iterable_exception(self):
1796+
if self.TYPE == 'manager':
1797+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
1798+
1799+
it = self.pool.imap_unordered(sqr,
1800+
exception_throwing_generator(10, 3),
1801+
1)
1802+
with self.assertRaises(SayWhenError):
1803+
# imap_unordered makes it difficult to anticipate the SayWhenError
1804+
for i in range(10):
1805+
self.assertEqual(next(it), i*i)
1806+
1807+
it = self.pool.imap_unordered(sqr,
1808+
exception_throwing_generator(20, 7),
1809+
2)
1810+
with self.assertRaises(SayWhenError):
1811+
for i in range(20):
1812+
self.assertEqual(next(it), i*i)
1813+
17681814
def test_make_pool(self):
17691815
self.assertRaises(ValueError, multiprocessing.Pool, -1)
17701816
self.assertRaises(ValueError, multiprocessing.Pool, 0)

Misc/ACKS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ Raghuram Devarakonda
332332
Caleb Deveraux
333333
Catherine Devlin
334334
Scott Dial
335+
Alon Diamant
335336
Toby Dickenson
336337
Mark Dickinson
337338
Jack Diederich
@@ -1081,6 +1082,7 @@ Martin Pool
10811082
Iustin Pop
10821083
Claudiu Popa
10831084
John Popplewell
1085+
Davin Potts
10841086
Guillaume Pratte
10851087
Amrit Prem
10861088
Paul Prescod

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #23051: multiprocessing.Pool methods imap() and imap_unordered() now
22+
handle exceptions raised by an iterator. Patch by Alon Diamant and Davin
23+
Potts.
24+
2125
- Issue #22928: Disabled HTTP header injections in http.client.
2226
Original patch by Demian Brecht.
2327

0 commit comments

Comments
 (0)