Add multierror cancellation tests

improved_errors
Tyler Goodlet 2018-11-19 14:16:42 -05:00
parent 82fcf025cc
commit 9102c48810
1 changed files with 80 additions and 21 deletions

View File

@ -14,22 +14,36 @@ async def assert_err():
assert 0 assert 0
def test_remote_error(arb_addr): @pytest.mark.parametrize(
"""Verify an error raises in a subactor is propagated to the parent. 'args_err',
[
# expected to be thrown in assert_err
({}, AssertionError),
# argument mismatch raised in _invoke()
({'unexpected': 10}, TypeError)
],
ids=['no_args', 'unexpected_args'],
)
def test_remote_error(arb_addr, args_err):
"""Verify an error raised in a subactor that is propagated
to the parent nursery, contains underlying builtin erorr type
infot and causes cancellation and reraising.
""" """
args, errtype = args_err
async def main(): async def main():
async with tractor.open_nursery() as nursery: async with tractor.open_nursery() as nursery:
portal = await nursery.run_in_actor('errorer', assert_err) portal = await nursery.run_in_actor('errorer', assert_err, **args)
# get result(s) from main task # get result(s) from main task
try: try:
return await portal.result() await portal.result()
except tractor.RemoteActorError: except tractor.RemoteActorError as err:
assert err.type == errtype
print("Look Maa that actor failed hard, hehh") print("Look Maa that actor failed hard, hehh")
raise raise
except Exception: else:
pass
assert 0, "Remote error was not raised?" assert 0, "Remote error was not raised?"
with pytest.raises(tractor.RemoteActorError): with pytest.raises(tractor.RemoteActorError):
@ -37,14 +51,42 @@ def test_remote_error(arb_addr):
tractor.run(main, arbiter_addr=arb_addr) tractor.run(main, arbiter_addr=arb_addr)
def test_multierror(arb_addr):
"""Verify we raise a ``trio.MultiError`` out of a nursery where
more then one actor errors.
"""
async def main():
async with tractor.open_nursery() as nursery:
await nursery.run_in_actor('errorer1', assert_err)
portal2 = await nursery.run_in_actor('errorer2', assert_err)
# get result(s) from main task
try:
await portal2.result()
except tractor.RemoteActorError as err:
assert err.type == AssertionError
print("Look Maa that first actor failed hard, hehh")
raise
# here we should get a `trio.MultiError` containing exceptions
# from both subactors
with pytest.raises(trio.MultiError):
tractor.run(main, arbiter_addr=arb_addr)
def do_nothing(): def do_nothing():
pass pass
def test_cancel_single_subactor(arb_addr): def test_cancel_single_subactor(arb_addr):
"""Ensure a ``ActorNursery.start_actor()`` spawned subactor
async def main(): cancels when the nursery is cancelled.
"""
async def spawn_actor():
"""Spawn an actor that blocks indefinitely.
"""
async with tractor.open_nursery() as nursery: async with tractor.open_nursery() as nursery:
portal = await nursery.start_actor( portal = await nursery.start_actor(
@ -55,7 +97,7 @@ def test_cancel_single_subactor(arb_addr):
# would hang otherwise # would hang otherwise
await nursery.cancel() await nursery.cancel()
tractor.run(main, arbiter_addr=arb_addr) tractor.run(spawn_actor, arbiter_addr=arb_addr)
async def stream_forever(): async def stream_forever():
@ -87,13 +129,22 @@ async def test_cancel_infinite_streamer():
assert n.cancelled assert n.cancelled
@pytest.mark.parametrize(
'num_actors_and_errs',
[
(1, tractor.RemoteActorError, AssertionError),
(2, tractor.MultiError, AssertionError)
],
ids=['one_actor', 'two_actors'],
)
@tractor_test @tractor_test
async def test_one_cancels_all(): async def test_some_cancels_all(num_actors_and_errs):
"""Verify one failed actor causes all others in the nursery """Verify one failed actor causes all others in the nursery
to be cancelled just like in trio. to be cancelled just like in trio.
This is the first and only supervisory strategy at the moment. This is the first and only supervisory strategy at the moment.
""" """
num, first_err, err_type = num_actors_and_errs
try: try:
async with tractor.open_nursery() as n: async with tractor.open_nursery() as n:
real_actors = [] real_actors = []
@ -103,13 +154,21 @@ async def test_one_cancels_all():
rpc_module_paths=[__name__], rpc_module_paths=[__name__],
)) ))
for i in range(num):
# start one actor that will fail immediately # start one actor that will fail immediately
await n.run_in_actor('extra', assert_err) await n.run_in_actor(f'extra_{i}', assert_err)
# should error here with a ``RemoteActorError`` containing # should error here with a ``RemoteActorError`` or
# an ``AssertionError` # ``MultiError`` containing an ``AssertionError`
except first_err as err:
if isinstance(err, trio.MultiError):
assert len(err.exceptions) == num
for exc in err.exceptions:
assert exc.type == err_type
else:
assert err.type == err_type
except tractor.RemoteActorError:
assert n.cancelled is True assert n.cancelled is True
assert not n._children assert not n._children
else: else: