diff --git a/examples/debugging/fast_error_in_root_after_spawn.py b/examples/debugging/fast_error_in_root_after_spawn.py index a3953d36..fdafddef 100644 --- a/examples/debugging/fast_error_in_root_after_spawn.py +++ b/examples/debugging/fast_error_in_root_after_spawn.py @@ -13,7 +13,11 @@ import tractor @tractor.context async def sleep( ctx: tractor.Context, -): +) -> None: + ''' + Start a context after a brief initialization delay. + + ''' await trio.sleep(0.5) await ctx.started() await trio.sleep_forever() @@ -21,10 +25,13 @@ async def sleep( async def open_ctx( n: tractor.runtime._supervise.ActorNursery -): +) -> None: + ''' + Spawn a sleeper and open a context with it. + ''' # spawn both actors - portal = await n.start_actor( + portal: tractor.Portal = await n.start_actor( name='sleeper', enable_modules=[__name__], ) @@ -36,7 +43,10 @@ async def open_ctx( async def main() -> None: + ''' + Fail the root while a subactor context is still starting. + ''' async with tractor.open_nursery( debug_mode=True, loglevel='runtime', diff --git a/examples/debugging/multi_daemon_subactors.py b/examples/debugging/multi_daemon_subactors.py index 95822f93..0c77ffed 100644 --- a/examples/debugging/multi_daemon_subactors.py +++ b/examples/debugging/multi_daemon_subactors.py @@ -1,9 +1,14 @@ +from collections.abc import AsyncIterator + import tractor import trio -async def breakpoint_forever(): - "Indefinitely re-enter debugger in child actor." +async def breakpoint_forever() -> AsyncIterator[str]: + ''' + Indefinitely re-enter debugger in child actor. + + ''' try: while True: yield 'yo' @@ -15,8 +20,11 @@ async def breakpoint_forever(): raise -async def name_error(): - "Raise a ``NameError``" +async def name_error() -> None: + ''' + Raise a ``NameError``. + + ''' getattr(doggypants) # noqa @@ -28,8 +36,14 @@ async def main() -> None: async with tractor.open_nursery( debug_mode=True, ) as an: - p0 = await an.start_actor('bp_forever', enable_modules=[__name__]) - p1 = await an.start_actor('name_error', enable_modules=[__name__]) + p0: tractor.Portal = await an.start_actor( + 'bp_forever', + enable_modules=[__name__], + ) + p1: tractor.Portal = await an.start_actor( + 'name_error', + enable_modules=[__name__], + ) # retreive results async with p0.open_stream_from(breakpoint_forever) as stream: @@ -40,6 +54,7 @@ async def main() -> None: except tractor.RemoteActorError as rae: assert rae.boxed_type is NameError + i: str async for i in stream: # a second time try the failing subactor and this tie diff --git a/examples/debugging/multi_nested_subactors_error_up_through_nurseries.py b/examples/debugging/multi_nested_subactors_error_up_through_nurseries.py index 2895f0e6..6357b96f 100644 --- a/examples/debugging/multi_nested_subactors_error_up_through_nurseries.py +++ b/examples/debugging/multi_nested_subactors_error_up_through_nurseries.py @@ -4,13 +4,19 @@ import trio import tractor -async def name_error(): - "Raise a ``NameError``" +async def name_error() -> None: + ''' + Raise a ``NameError``. + + ''' getattr(doggypants) # noqa -async def breakpoint_forever(): - "Indefinitely re-enter debugger in child actor." +async def breakpoint_forever() -> None: + ''' + Indefinitely re-enter debugger in child actor. + + ''' while True: await tractor.pause() @@ -20,9 +26,13 @@ async def breakpoint_forever(): # await trio.sleep(0) -async def spawn_until(depth=0): - """"A nested nursery that triggers another ``NameError``. - """ +async def spawn_until( + depth: int = 0, +) -> None: + ''' + A nested nursery that triggers another ``NameError``. + + ''' async with ( tractor.open_nursery() as an, trio.open_nursery() as tn, @@ -37,7 +47,8 @@ async def spawn_until(depth=0): ) ) - # Let the background one-shot enter `breakpoint_forever()` + # Let the background one-shot enter + # `breakpoint_forever()` # before its sibling raises and cancellation propagates. await trio.sleep(0.5) # rx and propagate error from child @@ -48,9 +59,9 @@ async def spawn_until(depth=0): ) else: - # recusrive call to spawn another process branching layer of - # the tree; blocks (up) each level until the leaf's - # `name_error` relays through. + # recusrive call to spawn another process branching + # layer of the tree; blocks (up) each level until the + # leaf's `name_error` relays through. depth -= 1 await tractor.to_actor.run( partial( @@ -64,24 +75,34 @@ async def spawn_until(depth=0): # TODO: notes on the new boxed-relayed errors through proxy actors async def main() -> None: - """The main ``tractor`` routine. + ''' + The main ``tractor`` routine. - The process tree should look as approximately as follows when the debugger - first engages: + The process tree should look approximately as follows when the + debugger first engages: python examples/debugging/multi_nested_subactors_bp_forever.py - ├─ python -m tractor._child --uid ('spawner1', '7eab8462 ...) - │ └─ python -m tractor._child --uid ('spawn_until_3', 'afcba7a8 ...) - │ └─ python -m tractor._child --uid ('spawn_until_2', 'd2433d13 ...) - │ └─ python -m tractor._child --uid ('spawn_until_1', '1df589de ...) - │ └─ python -m tractor._child --uid ('spawn_until_0', '3720602b ...) + ├─ python -m tractor._child --uid + │ ('spawner1', '7eab8462 ...') + │ └─ python -m tractor._child --uid + │ ('spawn_until_3', 'afcba7a8 ...') + │ └─ python -m tractor._child --uid + │ ('spawn_until_2', 'd2433d13 ...') + │ └─ python -m tractor._child --uid + │ ('spawn_until_1', '1df589de ...') + │ └─ python -m tractor._child --uid + │ ('spawn_until_0', '3720602b ...') │ - └─ python -m tractor._child --uid ('spawner0', '1d42012b ...) - └─ python -m tractor._child --uid ('spawn_until_2', '2877e155 ...) - └─ python -m tractor._child --uid ('spawn_until_1', '0502d786 ...) - └─ python -m tractor._child --uid ('spawn_until_0', 'de918e6d ...) + └─ python -m tractor._child --uid + ('spawner0', '1d42012b ...') + └─ python -m tractor._child --uid + ('spawn_until_2', '2877e155 ...') + └─ python -m tractor._child --uid + ('spawn_until_1', '0502d786 ...') + └─ python -m tractor._child --uid + ('spawn_until_0', 'de918e6d ...') - """ + ''' async with ( tractor.open_nursery( debug_mode=True, diff --git a/examples/debugging/multi_subactor_root_errors.py b/examples/debugging/multi_subactor_root_errors.py index b934c515..a9d0debb 100644 --- a/examples/debugging/multi_subactor_root_errors.py +++ b/examples/debugging/multi_subactor_root_errors.py @@ -7,14 +7,19 @@ import trio import tractor -async def name_error(): - "Raise a ``NameError``" +async def name_error() -> None: + ''' + Raise a ``NameError``. + + ''' getattr(doggypants) # noqa -async def spawn_error(): - """"A nested nursery that triggers another ``NameError``. - """ +async def spawn_error() -> None: + ''' + A nested nursery that triggers another ``NameError``. + + ''' async with tractor.open_nursery() as an: return await tractor.to_actor.run( name_error, @@ -24,7 +29,8 @@ async def spawn_error(): async def main() -> None: - """The main ``tractor`` routine. + ''' + The main ``tractor`` routine. The process tree should look as approximately as follows: @@ -37,7 +43,8 @@ async def main() -> None: - nested name_error sub-sub-actor - root actor should then fail on assert - program termination - """ + + ''' async with ( tractor.open_nursery( debug_mode=True, @@ -46,11 +53,11 @@ async def main() -> None: trio.open_nursery() as tn, ): # spawn both actors.. - portal = await an.start_actor( + portal: tractor.Portal = await an.start_actor( 'name_error', enable_modules=[__name__], ) - portal1 = await an.start_actor( + portal1: tractor.Portal = await an.start_actor( 'spawn_error', enable_modules=[__name__], ) diff --git a/examples/debugging/multi_subactors.py b/examples/debugging/multi_subactors.py index f2ea18c9..efa8bfd1 100644 --- a/examples/debugging/multi_subactors.py +++ b/examples/debugging/multi_subactors.py @@ -1,22 +1,32 @@ +from collections.abc import Awaitable, Callable + import tractor import trio -async def breakpoint_forever(): - "Indefinitely re-enter debugger in child actor." +async def breakpoint_forever() -> None: + ''' + Indefinitely re-enter debugger in child actor. + + ''' while True: await trio.sleep(0.1) await tractor.pause() -async def name_error(): - "Raise a ``NameError``" +async def name_error() -> None: + ''' + Raise a ``NameError``. + + ''' getattr(doggypants) # noqa -async def spawn_error(): - """"A nested nursery that triggers another ``NameError``. - """ +async def spawn_error() -> None: + ''' + A nested nursery that triggers another ``NameError``. + + ''' async with tractor.open_nursery() as an: return await tractor.to_actor.run( name_error, @@ -26,7 +36,8 @@ async def spawn_error(): async def main() -> None: - """The main ``tractor`` routine. + ''' + The main ``tractor`` routine. The process tree should look as approximately as follows: @@ -35,7 +46,8 @@ async def main() -> None: |-python -m tractor._child --uid ('bp_forever', '1f787a7e ...) `-python -m tractor._child --uid ('spawn_error', '52ee14a5 ...) `-python -m tractor._child --uid ('name_error', '3391222c ...) - """ + + ''' errors: list[BaseException] = [] async with tractor.open_nursery( @@ -43,12 +55,14 @@ async def main() -> None: # loglevel='runtime', ) as an: - async def run_and_collect(fn): + async def run_and_collect( + fn: Callable[[], Awaitable[object]], + ) -> None: ''' One-shot whose (boxed) error is stashed instead of raised so a sibling's crash never cancels the others before they've had their own debugger sessions (the - "collect all errors" the legacy `run_in_actor()` API + 'collect all errors' the legacy `run_in_actor()` API did implicitly at nursery teardown). ''' diff --git a/examples/debugging/per_actor_debug.py b/examples/debugging/per_actor_debug.py index c5abe450..8345a35f 100644 --- a/examples/debugging/per_actor_debug.py +++ b/examples/debugging/per_actor_debug.py @@ -1,19 +1,28 @@ import trio import tractor -async def die(): + +async def die() -> None: + ''' + Deliberately crash the calling actor. + + ''' raise RuntimeError async def main() -> None: + ''' + Crash actors with different debugger settings concurrently. + + ''' async with tractor.open_nursery() as an: - debug_actor = await an.start_actor( + debug_actor: tractor.Portal = await an.start_actor( 'debugged_boi', enable_modules=[__name__], debug_mode=True, ) - crash_boi = await an.start_actor( + crash_boi: tractor.Portal = await an.start_actor( 'crash_boi', enable_modules=[__name__], # debug_mode=True, diff --git a/examples/debugging/pm_in_subactor.py b/examples/debugging/pm_in_subactor.py index a9728a6b..cc60347d 100644 --- a/examples/debugging/pm_in_subactor.py +++ b/examples/debugging/pm_in_subactor.py @@ -5,7 +5,7 @@ import tractor @tractor.context async def name_error( ctx: tractor.Context, -): +) -> None: ''' Raise a `NameError`, catch it and enter `.post_mortem()`, then expect the `._rpc._invoke()` crash handler to also engage. @@ -49,7 +49,9 @@ async def main() -> None: await tractor.post_mortem() raise else: - raise RuntimeError('IPC ctx should have remote errored!?') + raise RuntimeError( + 'IPC ctx should have remote errored!?' + ) if __name__ == '__main__': diff --git a/examples/debugging/root_actor_breakpoint.py b/examples/debugging/root_actor_breakpoint.py index 347123ef..35a029ec 100644 --- a/examples/debugging/root_actor_breakpoint.py +++ b/examples/debugging/root_actor_breakpoint.py @@ -3,7 +3,10 @@ import tractor async def main() -> None: + ''' + Pause in the root actor to exercise its debugger REPL. + ''' async with tractor.open_root_actor( debug_mode=True, ): diff --git a/examples/debugging/root_actor_error.py b/examples/debugging/root_actor_error.py index 49359f16..1fae47b3 100644 --- a/examples/debugging/root_actor_error.py +++ b/examples/debugging/root_actor_error.py @@ -3,6 +3,10 @@ import tractor async def main() -> None: + ''' + Raise an assertion error from the debug-enabled root actor. + + ''' async with tractor.open_root_actor( debug_mode=True, ): diff --git a/examples/debugging/root_cancelled_but_child_is_in_tty_lock.py b/examples/debugging/root_cancelled_but_child_is_in_tty_lock.py index ca15530b..7fafa1bb 100644 --- a/examples/debugging/root_cancelled_but_child_is_in_tty_lock.py +++ b/examples/debugging/root_cancelled_but_child_is_in_tty_lock.py @@ -4,14 +4,21 @@ import trio import tractor -async def name_error(): - "Raise a ``NameError``" +async def name_error() -> None: + ''' + Raise a ``NameError``. + + ''' getattr(doggypants) # noqa -async def spawn_until(depth=0): - """"A nested nursery that triggers another ``NameError``. - """ +async def spawn_until( + depth: int = 0, +) -> None: + ''' + A nested nursery that triggers another ``NameError``. + + ''' async with tractor.open_nursery() as an: if depth < 1: await tractor.to_actor.run(name_error, an=an) @@ -33,12 +40,17 @@ async def main() -> None: debugger first engages: python examples/debugging/multi_nested_subactors_bp_forever.py - ├─ python -m tractor._child --uid ('spawner1', '7eab8462 ...) - │ └─ python -m tractor._child --uid ('spawn_until_0', '3720602b ...) - │ └─ python -m tractor._child --uid ('name_error', '505bf71d ...) + ├─ python -m tractor._child --uid + │ ('spawner1', '7eab8462 ...') + │ └─ python -m tractor._child --uid + │ ('spawn_until_0', '3720602b ...') + │ └─ python -m tractor._child --uid + │ ('name_error', '505bf71d ...') │ - └─ python -m tractor._child --uid ('spawner0', '1d42012b ...) - └─ python -m tractor._child --uid ('name_error', '6c2733b8 ...) + └─ python -m tractor._child --uid + ('spawner0', '1d42012b ...') + └─ python -m tractor._child --uid + ('name_error', '6c2733b8 ...') ''' async with ( diff --git a/examples/debugging/root_self_cancelled_w_error.py b/examples/debugging/root_self_cancelled_w_error.py index e5ebbacc..fb4b2ecb 100644 --- a/examples/debugging/root_self_cancelled_w_error.py +++ b/examples/debugging/root_self_cancelled_w_error.py @@ -3,6 +3,10 @@ import tractor async def main() -> None: + ''' + Enter shielded debugging after root cancellation, then fail. + + ''' async with tractor.open_root_actor( debug_mode=True, loglevel='cancel', @@ -18,16 +22,19 @@ async def main() -> None: try: await tractor.pause() except trio.Cancelled as _taskc: - assert (root_cs := _root._root_tn.cancel_scope).cancel_called + root_cs: trio.CancelScope + assert ( + root_cs := _root._root_tn.cancel_scope + ).cancel_called # NOTE^^ above logic but inside `open_root_actor()` and # passed to the `shield=` expression is effectively what # we're testing here! await tractor.pause(shield=root_cs.cancel_called) - # XXX, if shield logic *is wrong* inside `open_root_actor()`'s - # crash-handler block this should never be interacted, - # instead `trio.Cancelled` would be bubbled up: the original - # BUG. + # XXX, if shield logic *is wrong* inside + # `open_root_actor()`'s crash-handler block this should never + # be interacted, instead `trio.Cancelled` would be bubbled + # up: the original BUG. assert 0 diff --git a/examples/debugging/root_timeout_while_child_crashed.py b/examples/debugging/root_timeout_while_child_crashed.py index 11533a8e..8ca1a813 100644 --- a/examples/debugging/root_timeout_while_child_crashed.py +++ b/examples/debugging/root_timeout_while_child_crashed.py @@ -2,8 +2,11 @@ import trio import tractor -async def key_error(): - "Raise a ``NameError``" +async def key_error() -> None: + ''' + Raise a ``KeyError``. + + ''' return {}['doggy'] @@ -21,7 +24,7 @@ async def main() -> None: trio.open_nursery() as tn, ): # spawn the actor.. - portal = await an.start_actor( + portal: tractor.Portal = await an.start_actor( 'key_error', enable_modules=[__name__], ) @@ -32,9 +35,9 @@ async def main() -> None: # root blocks below. tn.start_soon(portal.run, key_error) - # XXX: originally a bug caused by this is where root would enter - # the debugger and clobber the tty used by the repl even though - # child should have it locked. + # XXX: originally a bug caused by this is where root would + # enter the debugger and clobber the tty used by the repl + # even though child should have it locked. with trio.fail_after(1): await trio.Event().wait() diff --git a/examples/debugging/shielded_pause.py b/examples/debugging/shielded_pause.py index cfa8f4f8..a145a3bb 100644 --- a/examples/debugging/shielded_pause.py +++ b/examples/debugging/shielded_pause.py @@ -3,8 +3,15 @@ import tractor async def cancellable_pause_loop( - task_status: trio.TaskStatus[trio.CancelScope] = trio.TASK_STATUS_IGNORED -): + task_status: trio.TaskStatus[ + trio.CancelScope + ] = trio.TASK_STATUS_IGNORED, +) -> None: + ''' + Exercise shielded debugger pauses under cancellation. + + ''' + cs: trio.CancelScope with trio.CancelScope() as cs: task_status.started(cs) for _ in range(3): @@ -30,7 +37,11 @@ async def cancellable_pause_loop( await trio.lowlevel.checkpoint() -async def pm_on_cancelled(): +async def pm_on_cancelled() -> None: + ''' + Compare shielded and unshielded post-mortem entry. + + ''' async with trio.open_nursery() as tn: tn.cancel_scope.cancel() try: @@ -56,7 +67,7 @@ async def pm_on_cancelled(): async def cancelled_before_pause( -): +) -> None: ''' Verify that using a shielded pause works despite surrounding cancellation called state in the calling task. @@ -72,6 +83,10 @@ async def cancelled_before_pause( async def main() -> None: + ''' + Exercise shielded debugger entry in subactor and root tasks. + + ''' async with tractor.open_nursery( debug_mode=True, ) as an: diff --git a/examples/debugging/subactor_bp_in_ctx.py b/examples/debugging/subactor_bp_in_ctx.py index eafeb0b7..3b3074c2 100644 --- a/examples/debugging/subactor_bp_in_ctx.py +++ b/examples/debugging/subactor_bp_in_ctx.py @@ -1,10 +1,15 @@ import platform +from collections.abc import AsyncIterator import tractor import trio -async def gen(): +async def gen() -> AsyncIterator[str]: + ''' + Yield values around debugger pauses. + + ''' yield 'yo' await tractor.pause() yield 'yo' @@ -15,11 +20,15 @@ async def gen(): async def just_bp( ctx: tractor.Context, ) -> None: + ''' + Pause repeatedly before deliberately breaking the context. + ''' await ctx.started() await tractor.pause() # TODO: bps and errors in this call.. + val: str async for val in gen(): print(val) @@ -35,14 +44,17 @@ async def just_bp( async def main() -> None: + ''' + Run the breakpoint context over a supported transport. + ''' # !TODO, parametrize the --tpt-proto={key} with osenv vars just # like we do for loglevel/spawn-backend! # - [ ] run on both tpts for all such debugger tests? # - [ ] special skip for macos! # if platform.system() != 'Darwin': - tpt = 'uds' + tpt: str = 'uds' else: # XXX, precisely we can't use pytest's tmp-path generation # for tests.. apparently because: @@ -59,7 +71,7 @@ async def main() -> None: enable_transports=[tpt], loglevel='devx', ) as an: - p = await an.start_actor( + p: tractor.Portal = await an.start_actor( 'bp_boi', enable_modules=[__name__], ) diff --git a/examples/debugging/subactor_breakpoint.py b/examples/debugging/subactor_breakpoint.py index 0a047a21..7b844f10 100644 --- a/examples/debugging/subactor_breakpoint.py +++ b/examples/debugging/subactor_breakpoint.py @@ -2,7 +2,7 @@ import trio import tractor -async def breakpoint_forever(): +async def breakpoint_forever() -> None: ''' Indefinitely re-enter debugger in child actor. @@ -13,7 +13,10 @@ async def breakpoint_forever(): async def main() -> None: + ''' + Run a subactor that repeatedly pauses in the debugger. + ''' async with tractor.open_nursery( debug_mode=True, loglevel='cancel', diff --git a/examples/debugging/subactor_error.py b/examples/debugging/subactor_error.py index fd280cb9..8ec65781 100644 --- a/examples/debugging/subactor_error.py +++ b/examples/debugging/subactor_error.py @@ -2,11 +2,19 @@ import trio import tractor -async def name_error(): +async def name_error() -> None: + ''' + Deliberately raise a ``NameError`` in a subactor. + + ''' getattr(doggypants) # noqa (on purpose) async def main() -> None: + ''' + Surface a subactor `NameError` at the waiting root task. + + ''' async with tractor.open_nursery( debug_mode=True, ) as an: