forked from goodboy/tractor
				
			Fix multi-daemon debug test `break` signal..
It was expecting `AssertionError` as a proceed-in-test signal (by breaking from a continue loop), but `in_prompt_msg(raise_on_err=True)` was changed to raise `ValueError`; so instead just use as a predicate for the `break`. Also rework `in_prompt_msg()` to accept the `child: BaseSpawn` as input instead of `before: str` remove the casting boilerplate, and adjust all usage to match.multihost_exs
							parent
							
								
									f7469442e3
								
							
						
					
					
						commit
						1f1a3f19d5
					
				| 
						 | 
				
			
			@ -25,7 +25,8 @@ async def main():
 | 
			
		|||
    """
 | 
			
		||||
    async with tractor.open_nursery(
 | 
			
		||||
        debug_mode=True,
 | 
			
		||||
        loglevel='cancel',
 | 
			
		||||
        # loglevel='cancel',
 | 
			
		||||
        # loglevel='devx',
 | 
			
		||||
    ) as n:
 | 
			
		||||
 | 
			
		||||
        p0 = await n.start_actor('bp_forever', enable_modules=[__name__])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ import pytest
 | 
			
		|||
from pexpect.exceptions import (
 | 
			
		||||
    TIMEOUT,
 | 
			
		||||
)
 | 
			
		||||
from pexpect.spawnbase import SpawnBase
 | 
			
		||||
from tractor._testing import (
 | 
			
		||||
    mk_cmd,
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +108,7 @@ def expect(
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def in_prompt_msg(
 | 
			
		||||
    prompt: str,
 | 
			
		||||
    child: SpawnBase,
 | 
			
		||||
    parts: list[str],
 | 
			
		||||
 | 
			
		||||
    pause_on_false: bool = False,
 | 
			
		||||
| 
						 | 
				
			
			@ -125,18 +126,20 @@ def in_prompt_msg(
 | 
			
		|||
    '''
 | 
			
		||||
    __tracebackhide__: bool = False
 | 
			
		||||
 | 
			
		||||
    before: str = str(child.before.decode())
 | 
			
		||||
    for part in parts:
 | 
			
		||||
        if part not in prompt:
 | 
			
		||||
        if part not in before:
 | 
			
		||||
            if pause_on_false:
 | 
			
		||||
                import pdbp
 | 
			
		||||
                pdbp.set_trace()
 | 
			
		||||
 | 
			
		||||
            if print_prompt_on_false:
 | 
			
		||||
                print(prompt)
 | 
			
		||||
                print(before)
 | 
			
		||||
 | 
			
		||||
            if err_on_false:
 | 
			
		||||
                raise ValueError(
 | 
			
		||||
                    f'Could not find pattern: {part!r} in `before` output?'
 | 
			
		||||
                    f'Could not find pattern in `before` output?\n'
 | 
			
		||||
                    f'part: {part!r}\n'
 | 
			
		||||
                )
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +150,7 @@ def in_prompt_msg(
 | 
			
		|||
# against call stack frame output from the the 'll' command the like!
 | 
			
		||||
# -[ ] SO answer for stipping ANSI codes: https://stackoverflow.com/a/14693789
 | 
			
		||||
def assert_before(
 | 
			
		||||
    child,
 | 
			
		||||
    child: SpawnBase,
 | 
			
		||||
    patts: list[str],
 | 
			
		||||
 | 
			
		||||
    **kwargs,
 | 
			
		||||
| 
						 | 
				
			
			@ -155,10 +158,8 @@ def assert_before(
 | 
			
		|||
) -> None:
 | 
			
		||||
    __tracebackhide__: bool = False
 | 
			
		||||
 | 
			
		||||
    # as in before the prompt end
 | 
			
		||||
    before: str = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        prompt=before,
 | 
			
		||||
        child=child,
 | 
			
		||||
        parts=patts,
 | 
			
		||||
 | 
			
		||||
        # since this is an "assert" helper ;)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,14 +96,15 @@ def test_root_actor_error(
 | 
			
		|||
    # scan for the prompt
 | 
			
		||||
    expect(child, PROMPT)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
 | 
			
		||||
    # make sure expected logging and error arrives
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_crash_msg, "('root'"]
 | 
			
		||||
        child,
 | 
			
		||||
        [
 | 
			
		||||
            _crash_msg,
 | 
			
		||||
            "('root'",
 | 
			
		||||
            'AssertionError',
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
    assert 'AssertionError' in before
 | 
			
		||||
 | 
			
		||||
    # send user command
 | 
			
		||||
    child.sendline(user_input)
 | 
			
		||||
| 
						 | 
				
			
			@ -243,10 +244,12 @@ def test_subactor_error(
 | 
			
		|||
    # scan for the prompt
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_crash_msg, "('name_error'"]
 | 
			
		||||
        child,
 | 
			
		||||
        [
 | 
			
		||||
            _crash_msg,
 | 
			
		||||
            "('name_error'",
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if do_next:
 | 
			
		||||
| 
						 | 
				
			
			@ -265,17 +268,15 @@ def test_subactor_error(
 | 
			
		|||
        child.sendline('continue')
 | 
			
		||||
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        child,
 | 
			
		||||
        [
 | 
			
		||||
            _crash_msg,
 | 
			
		||||
            # root actor gets debugger engaged
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_crash_msg, "('root'"]
 | 
			
		||||
    )
 | 
			
		||||
            "('root'",
 | 
			
		||||
            # error is a remote error propagated from the subactor
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_crash_msg, "('name_error'"]
 | 
			
		||||
            "('name_error'",
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # another round
 | 
			
		||||
| 
						 | 
				
			
			@ -296,14 +297,11 @@ def test_subactor_breakpoint(
 | 
			
		|||
    "Single subactor with an infinite breakpoint loop"
 | 
			
		||||
 | 
			
		||||
    child = spawn('subactor_breakpoint')
 | 
			
		||||
 | 
			
		||||
    # scan for the prompt
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_pause_msg, "('breakpoint_forever'"]
 | 
			
		||||
        child,
 | 
			
		||||
        [_pause_msg,
 | 
			
		||||
         "('breakpoint_forever'",]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # do some "next" commands to demonstrate recurrent breakpoint
 | 
			
		||||
| 
						 | 
				
			
			@ -319,9 +317,8 @@ def test_subactor_breakpoint(
 | 
			
		|||
    for _ in range(5):
 | 
			
		||||
        child.sendline('continue')
 | 
			
		||||
        child.expect(PROMPT)
 | 
			
		||||
        before = str(child.before.decode())
 | 
			
		||||
        assert in_prompt_msg(
 | 
			
		||||
            before,
 | 
			
		||||
            child,
 | 
			
		||||
            [_pause_msg, "('breakpoint_forever'"]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -334,9 +331,8 @@ def test_subactor_breakpoint(
 | 
			
		|||
    # child process should exit but parent will capture pdb.BdbQuit
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        ['RemoteActorError:',
 | 
			
		||||
         "('breakpoint_forever'",
 | 
			
		||||
         'bdb.BdbQuit',]
 | 
			
		||||
| 
						 | 
				
			
			@ -351,9 +347,8 @@ def test_subactor_breakpoint(
 | 
			
		|||
    # process should exit
 | 
			
		||||
    child.expect(EOF)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        ['RemoteActorError:',
 | 
			
		||||
         "('breakpoint_forever'",
 | 
			
		||||
         'bdb.BdbQuit',]
 | 
			
		||||
| 
						 | 
				
			
			@ -377,7 +372,7 @@ def test_multi_subactors(
 | 
			
		|||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        [_pause_msg, "('breakpoint_forever'"]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -398,12 +393,14 @@ def test_multi_subactors(
 | 
			
		|||
 | 
			
		||||
    # first name_error failure
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        [_crash_msg, "('name_error'"]
 | 
			
		||||
        child,
 | 
			
		||||
        [
 | 
			
		||||
            _crash_msg,
 | 
			
		||||
            "('name_error'",
 | 
			
		||||
            "NameError",
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
    assert "NameError" in before
 | 
			
		||||
 | 
			
		||||
    if ctlc:
 | 
			
		||||
        do_ctlc(child)
 | 
			
		||||
| 
						 | 
				
			
			@ -427,9 +424,8 @@ def test_multi_subactors(
 | 
			
		|||
    # breakpoint loop should re-engage
 | 
			
		||||
    child.sendline('c')
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        [_pause_msg, "('breakpoint_forever'"]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -522,25 +518,28 @@ def test_multi_daemon_subactors(
 | 
			
		|||
    # the root's tty lock first so anticipate either crash
 | 
			
		||||
    # message on the first entry.
 | 
			
		||||
 | 
			
		||||
    bp_forev_parts = [_pause_msg, "('bp_forever'"]
 | 
			
		||||
    bp_forev_parts = [
 | 
			
		||||
        _pause_msg,
 | 
			
		||||
        "('bp_forever'",
 | 
			
		||||
    ]
 | 
			
		||||
    bp_forev_in_msg = partial(
 | 
			
		||||
        in_prompt_msg,
 | 
			
		||||
        parts=bp_forev_parts,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    name_error_msg = "NameError: name 'doggypants' is not defined"
 | 
			
		||||
    name_error_parts = [name_error_msg]
 | 
			
		||||
    name_error_msg: str = "NameError: name 'doggypants' is not defined"
 | 
			
		||||
    name_error_parts: list[str] = [name_error_msg]
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
 | 
			
		||||
    if bp_forev_in_msg(prompt=before):
 | 
			
		||||
    if bp_forev_in_msg(child=child):
 | 
			
		||||
        next_parts = name_error_parts
 | 
			
		||||
 | 
			
		||||
    elif name_error_msg in before:
 | 
			
		||||
        next_parts = bp_forev_parts
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        raise ValueError("Neither log msg was found !?")
 | 
			
		||||
        raise ValueError('Neither log msg was found !?')
 | 
			
		||||
 | 
			
		||||
    if ctlc:
 | 
			
		||||
        do_ctlc(child)
 | 
			
		||||
| 
						 | 
				
			
			@ -609,14 +608,12 @@ def test_multi_daemon_subactors(
 | 
			
		|||
    # wait for final error in root
 | 
			
		||||
    # where it crashs with boxed error
 | 
			
		||||
    while True:
 | 
			
		||||
        try:
 | 
			
		||||
        child.sendline('c')
 | 
			
		||||
        child.expect(PROMPT)
 | 
			
		||||
            assert_before(
 | 
			
		||||
        if not in_prompt_msg(
 | 
			
		||||
            child,
 | 
			
		||||
            bp_forev_parts
 | 
			
		||||
            )
 | 
			
		||||
        except AssertionError:
 | 
			
		||||
        ):
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
    assert_before(
 | 
			
		||||
| 
						 | 
				
			
			@ -797,10 +794,13 @@ def test_root_nursery_cancels_before_child_releases_tty_lock(
 | 
			
		|||
    child = spawn('root_cancelled_but_child_is_in_tty_lock')
 | 
			
		||||
 | 
			
		||||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert "NameError: name 'doggypants' is not defined" in before
 | 
			
		||||
    assert "tractor._exceptions.RemoteActorError: ('name_error'" not in before
 | 
			
		||||
    assert_before(
 | 
			
		||||
        child,
 | 
			
		||||
        [
 | 
			
		||||
            "NameError: name 'doggypants' is not defined",
 | 
			
		||||
            "tractor._exceptions.RemoteActorError: ('name_error'",
 | 
			
		||||
        ],
 | 
			
		||||
    )
 | 
			
		||||
    time.sleep(0.5)
 | 
			
		||||
 | 
			
		||||
    if ctlc:
 | 
			
		||||
| 
						 | 
				
			
			@ -891,9 +891,8 @@ def test_different_debug_mode_per_actor(
 | 
			
		|||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    # only one actor should enter the debugger
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        [_crash_msg, "('debugged_boi'", "RuntimeError"],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -903,8 +902,6 @@ def test_different_debug_mode_per_actor(
 | 
			
		|||
    child.sendline('c')
 | 
			
		||||
    child.expect(EOF)
 | 
			
		||||
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
 | 
			
		||||
    # NOTE: this debugged actor error currently WON'T show up since the
 | 
			
		||||
    # root will actually cancel and terminate the nursery before the error
 | 
			
		||||
    # msg reported back from the debug mode actor is processed.
 | 
			
		||||
| 
						 | 
				
			
			@ -956,9 +953,8 @@ def test_pause_from_sync(
 | 
			
		|||
    child.expect(PROMPT)
 | 
			
		||||
 | 
			
		||||
    # XXX shouldn't see gb loaded message with PDB loglevel!
 | 
			
		||||
    before = str(child.before.decode())
 | 
			
		||||
    assert not in_prompt_msg(
 | 
			
		||||
        before,
 | 
			
		||||
        child,
 | 
			
		||||
        ['`greenback` portal opened!'],
 | 
			
		||||
    )
 | 
			
		||||
    # should be same root task
 | 
			
		||||
| 
						 | 
				
			
			@ -1039,7 +1035,7 @@ def test_pause_from_sync(
 | 
			
		|||
        # at the same time as the one that was detected above.
 | 
			
		||||
        for key, other_patts in attach_patts.copy().items():
 | 
			
		||||
            assert not in_prompt_msg(
 | 
			
		||||
                before,
 | 
			
		||||
                child,
 | 
			
		||||
                other_patts,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -291,7 +291,7 @@ def test_context_spawns_aio_task_that_errors(
 | 
			
		|||
 | 
			
		||||
        err = excinfo.value
 | 
			
		||||
        assert isinstance(err, expect)
 | 
			
		||||
        assert err.boxed_type == AssertionError
 | 
			
		||||
        assert err.boxed_type is AssertionError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def aio_cancel():
 | 
			
		||||
| 
						 | 
				
			
			@ -497,7 +497,7 @@ def test_trio_error_cancels_intertask_chan(reg_addr):
 | 
			
		|||
        trio.run(main)
 | 
			
		||||
 | 
			
		||||
    # ensure boxed error type
 | 
			
		||||
    excinfo.value.boxed_type == Exception
 | 
			
		||||
    excinfo.value.boxed_type is Exception
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_trio_closes_early_and_channel_exits(reg_addr):
 | 
			
		||||
| 
						 | 
				
			
			@ -533,7 +533,7 @@ def test_aio_errors_and_channel_propagates_and_closes(reg_addr):
 | 
			
		|||
    ) as excinfo:
 | 
			
		||||
        trio.run(main)
 | 
			
		||||
 | 
			
		||||
    excinfo.value.boxed_type == Exception
 | 
			
		||||
    excinfo.value.boxed_type is Exception
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@tractor.context
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue