Add an `actorc` test-driven-dev suite
Defining how an actor-nursery should emit an eg based on non-graceful cancellation in a new `test_actor_nursery` module. Obviously fails atm until the implementation is completed.actor_cancelled_exc_type
parent
dc806b8aba
commit
82b5bd52c8
|
@ -0,0 +1,98 @@
|
||||||
|
'''
|
||||||
|
Basic `ActorNursery` operations and closure semantics,
|
||||||
|
- basic remote error collection,
|
||||||
|
- basic multi-subactor cancellation.
|
||||||
|
|
||||||
|
'''
|
||||||
|
# import os
|
||||||
|
# import signal
|
||||||
|
# import platform
|
||||||
|
# import time
|
||||||
|
# from itertools import repeat
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import trio
|
||||||
|
import tractor
|
||||||
|
from tractor._exceptions import ActorCancelled
|
||||||
|
# from tractor._testing import (
|
||||||
|
# tractor_test,
|
||||||
|
# )
|
||||||
|
# from .conftest import no_windows
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'num_subs',
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_one_cancels_all(
|
||||||
|
start_method: str,
|
||||||
|
loglevel: str,
|
||||||
|
debug_mode: bool,
|
||||||
|
num_subs: int,
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Verify that ifa a single error bubbles to the an-scope the
|
||||||
|
nursery will be cancelled (just like in `trio`); this is a
|
||||||
|
one-cancels-all style strategy and are only supervision policy
|
||||||
|
at the moment.
|
||||||
|
|
||||||
|
'''
|
||||||
|
async def main():
|
||||||
|
try:
|
||||||
|
rte = RuntimeError('Uh oh something bad in parent')
|
||||||
|
async with tractor.open_nursery(
|
||||||
|
start_method=start_method,
|
||||||
|
loglevel=loglevel,
|
||||||
|
debug_mode=debug_mode,
|
||||||
|
) as an:
|
||||||
|
|
||||||
|
# spawn the same number of deamon actors which should be cancelled
|
||||||
|
dactor_portals = []
|
||||||
|
for i in range(num_subs):
|
||||||
|
name: str= f'sub_{i}'
|
||||||
|
ptl: tractor.Portal = await an.start_actor(
|
||||||
|
name=name,
|
||||||
|
enable_modules=[__name__],
|
||||||
|
)
|
||||||
|
dactor_portals.append(ptl)
|
||||||
|
|
||||||
|
# wait for booted
|
||||||
|
async with tractor.wait_for_actor(name):
|
||||||
|
print(f'{name!r} is up.')
|
||||||
|
|
||||||
|
# simulate uncaught exc
|
||||||
|
raise rte
|
||||||
|
|
||||||
|
# should error here with a ``RemoteActorError`` or ``MultiError``
|
||||||
|
|
||||||
|
except BaseExceptionGroup as _beg:
|
||||||
|
beg = _beg
|
||||||
|
|
||||||
|
# ?TODO? why can't we do `is` on beg?
|
||||||
|
assert (
|
||||||
|
beg.exceptions
|
||||||
|
==
|
||||||
|
an.maybe_error.exceptions
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(beg.exceptions) == (
|
||||||
|
num_subs
|
||||||
|
+
|
||||||
|
1 # rte from root
|
||||||
|
)
|
||||||
|
|
||||||
|
# all subactors should have been implicitly
|
||||||
|
# `Portal.cancel_actor()`ed.
|
||||||
|
excs = list(beg.exceptions)
|
||||||
|
excs.remove(rte)
|
||||||
|
for exc in excs:
|
||||||
|
assert isinstance(exc, ActorCancelled)
|
||||||
|
|
||||||
|
assert an._scope_error is rte
|
||||||
|
assert not an._children
|
||||||
|
assert an.cancelled is True
|
||||||
|
|
||||||
|
trio.run(main)
|
Loading…
Reference in New Issue