Add multierror cancellation tests
parent
82fcf025cc
commit
9102c48810
|
@ -14,22 +14,36 @@ async def assert_err():
|
|||
assert 0
|
||||
|
||||
|
||||
def test_remote_error(arb_addr):
|
||||
"""Verify an error raises in a subactor is propagated to the parent.
|
||||
@pytest.mark.parametrize(
|
||||
'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 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
|
||||
try:
|
||||
return await portal.result()
|
||||
except tractor.RemoteActorError:
|
||||
await portal.result()
|
||||
except tractor.RemoteActorError as err:
|
||||
assert err.type == errtype
|
||||
print("Look Maa that actor failed hard, hehh")
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
assert 0, "Remote error was not raised?"
|
||||
|
||||
with pytest.raises(tractor.RemoteActorError):
|
||||
|
@ -37,14 +51,42 @@ def test_remote_error(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():
|
||||
pass
|
||||
|
||||
|
||||
def test_cancel_single_subactor(arb_addr):
|
||||
|
||||
async def main():
|
||||
|
||||
"""Ensure a ``ActorNursery.start_actor()`` spawned subactor
|
||||
cancels when the nursery is cancelled.
|
||||
"""
|
||||
async def spawn_actor():
|
||||
"""Spawn an actor that blocks indefinitely.
|
||||
"""
|
||||
async with tractor.open_nursery() as nursery:
|
||||
|
||||
portal = await nursery.start_actor(
|
||||
|
@ -55,7 +97,7 @@ def test_cancel_single_subactor(arb_addr):
|
|||
# would hang otherwise
|
||||
await nursery.cancel()
|
||||
|
||||
tractor.run(main, arbiter_addr=arb_addr)
|
||||
tractor.run(spawn_actor, arbiter_addr=arb_addr)
|
||||
|
||||
|
||||
async def stream_forever():
|
||||
|
@ -87,13 +129,22 @@ async def test_cancel_infinite_streamer():
|
|||
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
|
||||
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
|
||||
to be cancelled just like in trio.
|
||||
|
||||
This is the first and only supervisory strategy at the moment.
|
||||
"""
|
||||
num, first_err, err_type = num_actors_and_errs
|
||||
try:
|
||||
async with tractor.open_nursery() as n:
|
||||
real_actors = []
|
||||
|
@ -103,13 +154,21 @@ async def test_one_cancels_all():
|
|||
rpc_module_paths=[__name__],
|
||||
))
|
||||
|
||||
for i in range(num):
|
||||
# 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
|
||||
# an ``AssertionError`
|
||||
# should error here with a ``RemoteActorError`` or
|
||||
# ``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 not n._children
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue