forked from goodboy/tractor
Add a multi-subactor test with nesting
parent
a2151cdd4d
commit
e387e8b322
|
@ -1,39 +0,0 @@
|
||||||
import tractor
|
|
||||||
import trio
|
|
||||||
|
|
||||||
|
|
||||||
async def bubble():
|
|
||||||
print('IN BUBBLE')
|
|
||||||
while True:
|
|
||||||
await trio.sleep(.1)
|
|
||||||
await tractor.breakpoint()
|
|
||||||
|
|
||||||
|
|
||||||
async def name_error():
|
|
||||||
getattr(doggy)
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
"""The main ``tractor`` routine.
|
|
||||||
"""
|
|
||||||
async with tractor.open_nursery() as n:
|
|
||||||
|
|
||||||
portal1 = await n.run_in_actor('bubble', bubble)
|
|
||||||
portal = await n.run_in_actor('name_error', name_error)
|
|
||||||
await portal1.result()
|
|
||||||
await portal.result()
|
|
||||||
|
|
||||||
# The ``async with`` will unblock here since the 'some_linguist'
|
|
||||||
# actor has completed its main task ``cellar_door``.
|
|
||||||
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# - recurrent entry from single actor
|
|
||||||
# - recurrent entry to breakpoint() from single actor *after* and an
|
|
||||||
# error
|
|
||||||
# - root error alongside child errors
|
|
||||||
# - recurrent root errors
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
tractor.run(main, loglevel='info', debug_mode=True)
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import tractor
|
||||||
|
import trio
|
||||||
|
|
||||||
|
|
||||||
|
async def breakpoint_forever():
|
||||||
|
"Indefinitely re-enter debugger in child actor."
|
||||||
|
while True:
|
||||||
|
await trio.sleep(0.1)
|
||||||
|
await tractor.breakpoint()
|
||||||
|
|
||||||
|
|
||||||
|
async def name_error():
|
||||||
|
"Raise a ``NameError``"
|
||||||
|
getattr(doggypants)
|
||||||
|
|
||||||
|
|
||||||
|
async def spawn_error():
|
||||||
|
""""A nested nursery that triggers another ``NameError``.
|
||||||
|
"""
|
||||||
|
async with tractor.open_nursery() as n:
|
||||||
|
portal = await n.run_in_actor('name_error_1', name_error)
|
||||||
|
return await portal.result()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""The main ``tractor`` routine.
|
||||||
|
|
||||||
|
The process tree should look as approximately as follows:
|
||||||
|
|
||||||
|
-python examples/debugging/multi_subactors.py
|
||||||
|
|-python -m tractor._child --uid ('name_error', 'a7caf490 ...)
|
||||||
|
|-python -m tractor._child --uid ('bp_forever', '1f787a7e ...)
|
||||||
|
`-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...)
|
||||||
|
`-python -m tractor._child --uid ('name_error', '3391222c ...)
|
||||||
|
"""
|
||||||
|
async with tractor.open_nursery() as n:
|
||||||
|
|
||||||
|
# spawn both actors
|
||||||
|
portal1 = await n.run_in_actor('bp_forever', breakpoint_forever)
|
||||||
|
portal = await n.run_in_actor('name_error', name_error)
|
||||||
|
portal2 = await n.run_in_actor('spawn_error', spawn_error)
|
||||||
|
|
||||||
|
# attempt to collect results (which raises error in parent)
|
||||||
|
# still has some issues where the parent seems to get stuck
|
||||||
|
# await portal.result()
|
||||||
|
# await portal1.result()
|
||||||
|
# await portal2.result()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tractor.run(main, debug_mode=True)
|
|
@ -9,6 +9,17 @@ import pexpect
|
||||||
from .test_docs_examples import repodir
|
from .test_docs_examples import repodir
|
||||||
|
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# - recurrent entry from single actor
|
||||||
|
# - recurrent entry to breakpoint() from single actor *after* and an
|
||||||
|
# error
|
||||||
|
# - root error before child errors
|
||||||
|
# - root error after child errors
|
||||||
|
# - root error before child breakpoint
|
||||||
|
# - root error after child breakpoint
|
||||||
|
# - recurrent root errors
|
||||||
|
|
||||||
|
|
||||||
def examples_dir():
|
def examples_dir():
|
||||||
"""Return the abspath to the examples directory.
|
"""Return the abspath to the examples directory.
|
||||||
"""
|
"""
|
||||||
|
@ -45,7 +56,7 @@ def spawn(
|
||||||
('c', 'AssertionError'),
|
('c', 'AssertionError'),
|
||||||
('q', 'AssertionError'),
|
('q', 'AssertionError'),
|
||||||
],
|
],
|
||||||
ids=lambda item: item[1],
|
ids=lambda item: f'{item[0]} -> {item[1]}',
|
||||||
)
|
)
|
||||||
def test_root_actor_error(spawn, user_in_out):
|
def test_root_actor_error(spawn, user_in_out):
|
||||||
"""Demonstrate crash handler entering pdbpp from basic error in root actor.
|
"""Demonstrate crash handler entering pdbpp from basic error in root actor.
|
||||||
|
@ -55,7 +66,7 @@ def test_root_actor_error(spawn, user_in_out):
|
||||||
child = spawn('root_actor_error')
|
child = spawn('root_actor_error')
|
||||||
|
|
||||||
# scan for the pdbpp prompt
|
# scan for the pdbpp prompt
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
# make sure expected logging and error arrives
|
# make sure expected logging and error arrives
|
||||||
assert 'TTY lock acquired' in str(child.before)
|
assert 'TTY lock acquired' in str(child.before)
|
||||||
|
@ -86,7 +97,7 @@ def test_root_actor_bp(spawn, user_in_out):
|
||||||
child = spawn('root_actor_breakpoint')
|
child = spawn('root_actor_breakpoint')
|
||||||
|
|
||||||
# scan for the pdbpp prompt
|
# scan for the pdbpp prompt
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
assert 'Error' not in str(child.before)
|
assert 'Error' not in str(child.before)
|
||||||
|
|
||||||
|
@ -104,10 +115,12 @@ def test_root_actor_bp(spawn, user_in_out):
|
||||||
|
|
||||||
|
|
||||||
def test_subactor_error(spawn):
|
def test_subactor_error(spawn):
|
||||||
|
"Single subactor raising an error"
|
||||||
|
|
||||||
child = spawn('subactor_error')
|
child = spawn('subactor_error')
|
||||||
|
|
||||||
# scan for the pdbpp prompt
|
# scan for the pdbpp prompt
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert "Attaching to pdb in crashed actor: ('name_error'" in before
|
assert "Attaching to pdb in crashed actor: ('name_error'" in before
|
||||||
|
@ -120,7 +133,7 @@ def test_subactor_error(spawn):
|
||||||
# the debugger should enter a second time in the nursery
|
# the debugger should enter a second time in the nursery
|
||||||
# creating actor
|
# creating actor
|
||||||
|
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
|
|
||||||
|
@ -138,10 +151,12 @@ def test_subactor_error(spawn):
|
||||||
|
|
||||||
|
|
||||||
def test_subactor_breakpoint(spawn):
|
def test_subactor_breakpoint(spawn):
|
||||||
|
"Single subactor with an infinite breakpoint loop"
|
||||||
|
|
||||||
child = spawn('subactor_breakpoint')
|
child = spawn('subactor_breakpoint')
|
||||||
|
|
||||||
# scan for the pdbpp prompt
|
# scan for the pdbpp prompt
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
|
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
|
||||||
|
@ -150,12 +165,12 @@ def test_subactor_breakpoint(spawn):
|
||||||
# entries
|
# entries
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
child.sendline('next')
|
child.sendline('next')
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
# now run some "continues" to show re-entries
|
# now run some "continues" to show re-entries
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
child.sendline('continue')
|
child.sendline('continue')
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
|
assert "Attaching pdb to actor: ('breakpoint_forever'" in before
|
||||||
|
|
||||||
|
@ -163,7 +178,7 @@ def test_subactor_breakpoint(spawn):
|
||||||
child.sendline('q')
|
child.sendline('q')
|
||||||
|
|
||||||
# child process should exit but parent will capture pdb.BdbQuit
|
# child process should exit but parent will capture pdb.BdbQuit
|
||||||
child.expect("\(Pdb\+\+\)")
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert "RemoteActorError: ('breakpoint_forever'" in before
|
assert "RemoteActorError: ('breakpoint_forever'" in before
|
||||||
|
@ -178,3 +193,65 @@ def test_subactor_breakpoint(spawn):
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert "RemoteActorError: ('breakpoint_forever'" in before
|
assert "RemoteActorError: ('breakpoint_forever'" in before
|
||||||
assert 'bdb.BdbQuit' in before
|
assert 'bdb.BdbQuit' in before
|
||||||
|
|
||||||
|
|
||||||
|
def test_multi_subactors(spawn):
|
||||||
|
"""Multiple subactors, both erroring and breakpointing as well as
|
||||||
|
a nested subactor erroring.
|
||||||
|
"""
|
||||||
|
child = spawn(r'multi_subactors')
|
||||||
|
|
||||||
|
# scan for the pdbpp prompt
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "Attaching pdb to actor: ('bp_forever'" in before
|
||||||
|
|
||||||
|
# do some "next" commands to demonstrate recurrent breakpoint
|
||||||
|
# entries
|
||||||
|
for _ in range(10):
|
||||||
|
child.sendline('next')
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
|
# continue to next error
|
||||||
|
child.sendline('c')
|
||||||
|
|
||||||
|
# first name_error failure
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "NameError" in before
|
||||||
|
|
||||||
|
# continue again
|
||||||
|
child.sendline('c')
|
||||||
|
|
||||||
|
# 2nd name_error failure
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "NameError" in before
|
||||||
|
|
||||||
|
# breakpoint loop should re-engage
|
||||||
|
child.sendline('c')
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "Attaching pdb to actor: ('bp_forever'" in before
|
||||||
|
|
||||||
|
# now run some "continues" to show re-entries
|
||||||
|
for _ in range(5):
|
||||||
|
child.sendline('c')
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
|
||||||
|
# quit the loop and expect parent to attach
|
||||||
|
child.sendline('q')
|
||||||
|
child.expect(r"\(Pdb\+\+\)")
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "Attaching to pdb in crashed actor: ('arbiter'" in before
|
||||||
|
assert "RemoteActorError: ('bp_forever'" in before
|
||||||
|
assert 'bdb.BdbQuit' in before
|
||||||
|
|
||||||
|
# process should exit
|
||||||
|
child.sendline('c')
|
||||||
|
child.expect(pexpect.EOF)
|
||||||
|
|
||||||
|
before = str(child.before.decode())
|
||||||
|
assert "RemoteActorError: ('bp_forever'" in before
|
||||||
|
assert 'bdb.BdbQuit' in before
|
||||||
|
|
Loading…
Reference in New Issue