Skip to content

Commit 90a553d

Browse files
committed
Merge pull request #44 from radix/parallel-all-errors-alternative
Add a `parallel_all_errors` function which provides all errors instead of just FirstError
2 parents c1fa994 + a542a3c commit 90a553d

2 files changed

Lines changed: 66 additions & 5 deletions

File tree

effect/_intents.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def parallel(effects):
5555
their results, in the same order as the input to this function. If any
5656
child effect fails, the first such failure will be propagated as a
5757
:obj:`FirstError` exception. If additional error information is desired,
58-
error handler callbacks can be attached to each child effect.
58+
use :func:`parallel_all_errors`.
5959
6060
:param effects: Effects which should be performed in parallel.
6161
:return: An Effect that results in a list of results, or which fails with
@@ -64,6 +64,25 @@ def parallel(effects):
6464
return Effect(ParallelEffects(list(effects)))
6565

6666

67+
def parallel_all_errors(effects):
68+
"""
69+
Given multiple Effects, return one Effect that represents the aggregate of
70+
all of their effects. The result of the aggregate Effect will be a list of
71+
their results, in the same order as the input to this function.
72+
73+
:param effects: Effects which should be performed in parallel.
74+
:return: An Effect that results in a list of ``(is_error, result)`` tuples,
75+
where ``is_error`` is True if the child effect raised an exception, in
76+
which case ``result`` will be an exc_info tuple. If ``is_error`` is
77+
False, then ``result`` will just be the result as provided by the child
78+
effect.
79+
"""
80+
effects = [effect.on(success=lambda r: (False, r),
81+
error=lambda e: (True, e))
82+
for effect in effects]
83+
return Effect(ParallelEffects(list(effects)))
84+
85+
6786
@attributes(['exc_info', 'index'])
6887
class FirstError(Exception):
6988
"""

effect/test_intents.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
from __future__ import print_function, absolute_import
22

3+
from functools import partial
4+
5+
import six
6+
37
from testtools import TestCase
8+
from testtools.matchers import Equals, MatchesListwise
49

510
from ._base import Effect
6-
from ._sync import sync_perform
7-
from ._dispatcher import TypeDispatcher
8-
11+
from ._dispatcher import ComposedDispatcher, TypeDispatcher
912
from ._intents import (
13+
base_dispatcher,
1014
Constant, perform_constant,
1115
Error, perform_error,
1216
Func, perform_func,
13-
FirstError)
17+
FirstError,
18+
ParallelEffects, parallel_all_errors)
19+
from ._sync import sync_perform
20+
from ._test_utils import MatchesReraisedExcInfo, get_exc_info
21+
from .async import perform_parallel_async
22+
from .test_parallel_performers import EquitableException
1423

1524

1625
class IntentTests(TestCase):
@@ -58,3 +67,36 @@ def test_first_error_str(self):
5867
self.assertEqual(
5968
str(fe),
6069
'(index=150) ValueError: foo')
70+
71+
72+
class ParallelAllErrorsTests(TestCase):
73+
"""Tests for :func:`parallel_all_errors`."""
74+
75+
def test_parallel_all_errors(self):
76+
"""
77+
Exceptions raised from child effects get turned into (True, exc_info)
78+
results.
79+
"""
80+
exc_info1 = get_exc_info(EquitableException(message='foo'))
81+
reraise1 = partial(six.reraise, *exc_info1)
82+
exc_info2 = get_exc_info(EquitableException(message='bar'))
83+
reraise2 = partial(six.reraise, *exc_info2)
84+
85+
dispatcher = ComposedDispatcher([
86+
TypeDispatcher({
87+
ParallelEffects: perform_parallel_async,
88+
}),
89+
base_dispatcher])
90+
es = [Effect(Func(reraise1)),
91+
Effect(Constant(1)),
92+
Effect(Func(reraise2))]
93+
eff = parallel_all_errors(es)
94+
self.assertThat(
95+
sync_perform(dispatcher, eff),
96+
MatchesListwise([
97+
MatchesListwise([Equals(True),
98+
MatchesReraisedExcInfo(exc_info1)]),
99+
Equals((False, 1)),
100+
MatchesListwise([Equals(True),
101+
MatchesReraisedExcInfo(exc_info2)]),
102+
]))

0 commit comments

Comments
 (0)