Always restore at least `trio`'s sigint handler

We can get it during runtime startup and stash on a new
`Lock._trio_handler`. Always at least revert to this handler to
guarantee graceful kbi handling despite mucking about with our own
handler in debug mode.
egs_with_ctx_res_consumption
Tyler Goodlet 2022-10-13 13:12:17 -04:00
parent 8727c1e4c2
commit f6ac0c2eb7
2 changed files with 37 additions and 15 deletions

View File

@ -75,8 +75,10 @@ class Lock:
# placeholder for function to set a ``trio.Event`` on debugger exit # placeholder for function to set a ``trio.Event`` on debugger exit
# pdb_release_hook: Optional[Callable] = None # pdb_release_hook: Optional[Callable] = None
_trio_handler: Callable | None = None
# actor-wide variable pointing to current task name using debugger # actor-wide variable pointing to current task name using debugger
local_task_in_debug: Optional[str] = None local_task_in_debug: str | None = None
# NOTE: set by the current task waiting on the root tty lock from # NOTE: set by the current task waiting on the root tty lock from
# the CALLER side of the `lock_tty_for_child()` context entry-call # the CALLER side of the `lock_tty_for_child()` context entry-call
@ -111,13 +113,10 @@ class Lock:
@classmethod @classmethod
def unshield_sigint(cls): def unshield_sigint(cls):
if cls._orig_sigint_handler is not None: # always restore (some) sigint handler, either
# restore original sigint handler # the prior or at least ``trio``'s.
signal.signal( orig = cls._orig_sigint_handler or cls._trio_handler
signal.SIGINT, signal.signal(signal.SIGINT, orig)
cls._orig_sigint_handler
)
cls._orig_sigint_handler = None cls._orig_sigint_handler = None
@classmethod @classmethod
@ -544,7 +543,7 @@ def shield_sigint(
) -> None: ) -> None:
''' '''
Specialized debugger compatible SIGINT handler. Specialized, debugger-aware SIGINT handler.
In childred we always ignore to avoid deadlocks since cancellation In childred we always ignore to avoid deadlocks since cancellation
should always be managed by the parent supervising actor. The root should always be managed by the parent supervising actor. The root
@ -601,6 +600,8 @@ def shield_sigint(
# which has already terminated to unlock. # which has already terminated to unlock.
and any_connected and any_connected
): ):
# we are root and some actor is in debug mode
# if uid_in_debug is not None:
name = uid_in_debug[0] name = uid_in_debug[0]
if name != 'root': if name != 'root':
log.pdb( log.pdb(
@ -611,6 +612,22 @@ def shield_sigint(
log.pdb( log.pdb(
"Ignoring SIGINT while in debug mode" "Ignoring SIGINT while in debug mode"
) )
elif (
is_root_process()
):
log.pdb(
"Ignoring SIGINT since debug mode is enabled"
)
# revert back to ``trio`` handler asap!
Lock.unshield_sigint()
if (
Lock._root_local_task_cs_in_debug
and not Lock._root_local_task_cs_in_debug.cancel_called
):
Lock._root_local_task_cs_in_debug.cancel()
# raise KeyboardInterrupt
# child actor that has locked the debugger # child actor that has locked the debugger
elif not is_root_process(): elif not is_root_process():
@ -636,10 +653,9 @@ def shield_sigint(
# https://github.com/goodboy/tractor/issues/320 # https://github.com/goodboy/tractor/issues/320
# elif debug_mode(): # elif debug_mode():
else: else: # XXX: shouldn't ever get here?
log.pdb( print("WTFWTFWTF")
"Ignoring SIGINT since debug mode is enabled" raise KeyboardInterrupt
)
# NOTE: currently (at least on ``fancycompleter`` 0.9.2) # NOTE: currently (at least on ``fancycompleter`` 0.9.2)
# it lookks to be that the last command that was run (eg. ll) # it lookks to be that the last command that was run (eg. ll)

View File

@ -23,6 +23,7 @@ from functools import partial
import importlib import importlib
import logging import logging
import os import os
import signal
from typing import ( from typing import (
Optional, Optional,
) )
@ -76,14 +77,19 @@ async def open_root_actor(
rpc_module_paths: Optional[list] = None, rpc_module_paths: Optional[list] = None,
) -> typing.Any: ) -> typing.Any:
"""Async entry point for ``tractor``. '''
Runtime init entry point for ``tractor``.
""" '''
# Override the global debugger hook to make it play nice with # Override the global debugger hook to make it play nice with
# ``trio``, see: # ``trio``, see:
# https://github.com/python-trio/trio/issues/1155#issuecomment-742964018 # https://github.com/python-trio/trio/issues/1155#issuecomment-742964018
os.environ['PYTHONBREAKPOINT'] = 'tractor._debug._set_trace' os.environ['PYTHONBREAKPOINT'] = 'tractor._debug._set_trace'
# attempt to retreive ``trio``'s sigint handler and stash it
# on our debugger lock state.
_debug.Lock._trio_handler = signal.getsignal(signal.SIGINT)
# mark top most level process as root actor # mark top most level process as root actor
_state._runtime_vars['_is_root'] = True _state._runtime_vars['_is_root'] = True