End the `pdb` SIGINT handling madness

Turns out this is a lower level issue in terms of the stdlib's default
`pdb.Pdb` settings and how they conflict with `trio`s cancellation and
KBI handling. The details are hashed out more thoroughly in
python-trio/trio#1155. Maybe we can get a fix in trio so things are
solved under our feet :)
pdb_madness
Tyler Goodlet 2020-12-11 00:15:09 -05:00
parent e27dc2e244
commit e51c2620e5
4 changed files with 36 additions and 28 deletions

View File

@ -117,9 +117,13 @@ async def _main(
# Note that if the current actor is the arbiter it is desirable # Note that if the current actor is the arbiter it is desirable
# for it to stay up indefinitely until a re-election process has # for it to stay up indefinitely until a re-election process has
# taken place - which is not implemented yet FYI). # taken place - which is not implemented yet FYI).
try:
return await _start_actor( return await _start_actor(
actor, main, host, port, arbiter_addr=arbiter_addr actor, main, host, port, arbiter_addr=arbiter_addr
) )
finally:
logger.info("Root actor terminated")
def run( def run(

View File

@ -767,8 +767,8 @@ class Actor:
finally: finally:
log.info("Root nursery complete") log.info("Root nursery complete")
# tear down all lifetime contexts # tear down all lifetime contexts if not in guest mode
# api idea: ``tractor.open_context()`` # XXX: should this just be in the entrypoint?
log.warning("Closing all actor lifetime contexts") log.warning("Closing all actor lifetime contexts")
self._lifetime_stack.close() self._lifetime_stack.close()

View File

@ -6,12 +6,10 @@ import sys
from functools import partial from functools import partial
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import Awaitable, Tuple, Optional, Callable, AsyncIterator from typing import Awaitable, Tuple, Optional, Callable, AsyncIterator
# import signal
from async_generator import aclosing from async_generator import aclosing
import tractor import tractor
import trio import trio
from trio.testing import wait_all_tasks_blocked
from .log import get_logger from .log import get_logger
from . import _state from . import _state
@ -132,19 +130,6 @@ async def _acquire_debug_lock(uid: Tuple[str, str]) -> AsyncIterator[None]:
log.error(f"TTY lock released by {task_name}:{uid}") log.error(f"TTY lock released by {task_name}:{uid}")
def handler(signum, frame):
"""Block SIGINT while in debug to avoid deadlocks with cancellation.
"""
print(
"tractor ignores SIGINT while in debug mode\n"
"If you have a special need for it please open an issue.\n"
)
# don't allow those stdlib mofos to mess with sigint handler
pdbpp.pdb.Pdb.sigint_handler = handler
# @contextmanager # @contextmanager
# def _disable_sigint(): # def _disable_sigint():
# try: # try:
@ -269,14 +254,29 @@ def _breakpoint(debug_func) -> Awaitable[None]:
log.debug("Entering the synchronous world of pdb") log.debug("Entering the synchronous world of pdb")
debug_func(actor) debug_func(actor)
# user code **must** await this! # user code **must** await this!
return _bp() return _bp()
def _mk_pdb():
# XXX: setting these flags on the pdb instance are absolutely
# critical to having ctrl-c work in the ``trio`` standard way!
# The stdlib's pdb supports entering the current sync frame
# on a SIGINT, with ``trio`` we pretty much never want this
# and we did we can handle it in the ``tractor`` task runtime.
pdb = PdbwTeardown()
pdb.allow_kbdint = True
pdb.nosigint = True
return pdb
def _set_trace(actor): def _set_trace(actor):
log.critical(f"\nAttaching pdb to actor: {actor.uid}\n") log.critical(f"\nAttaching pdb to actor: {actor.uid}\n")
PdbwTeardown().set_trace(
pdb = _mk_pdb()
pdb.set_trace(
# start 2 levels up in user code # start 2 levels up in user code
frame=sys._getframe().f_back.f_back, frame=sys._getframe().f_back.f_back,
) )
@ -290,8 +290,10 @@ breakpoint = partial(
def _post_mortem(actor): def _post_mortem(actor):
log.critical(f"\nAttaching to pdb in crashed actor: {actor.uid}\n") log.critical(f"\nAttaching to pdb in crashed actor: {actor.uid}\n")
pdb = _mk_pdb()
# custom Pdb post-mortem entry # custom Pdb post-mortem entry
pdbpp.xpm(Pdb=PdbwTeardown) pdbpp.xpm(Pdb=lambda: pdb)
post_mortem = partial( post_mortem = partial(

View File

@ -7,7 +7,6 @@ import signal
import trio # type: ignore import trio # type: ignore
from ._actor import Actor
from .log import get_console_log, get_logger from .log import get_console_log, get_logger
from . import _state from . import _state
@ -16,7 +15,7 @@ log = get_logger(__name__)
def _mp_main( def _mp_main(
actor: 'Actor', actor: 'Actor', # noqa
accept_addr: Tuple[str, int], accept_addr: Tuple[str, int],
forkserver_info: Tuple[Any, Any, Any, Any, Any], forkserver_info: Tuple[Any, Any, Any, Any, Any],
start_method: str, start_method: str,
@ -49,11 +48,13 @@ def _mp_main(
trio.run(trio_main) trio.run(trio_main)
except KeyboardInterrupt: except KeyboardInterrupt:
pass # handle it the same way trio does? pass # handle it the same way trio does?
finally:
log.info(f"Actor {actor.uid} terminated") log.info(f"Actor {actor.uid} terminated")
def _trio_main( def _trio_main(
actor: 'Actor', actor: 'Actor', # noqa
parent_addr: Tuple[str, int] = None parent_addr: Tuple[str, int] = None
) -> None: ) -> None:
"""Entry point for a `trio_run_in_process` subactor. """Entry point for a `trio_run_in_process` subactor.
@ -86,4 +87,5 @@ def _trio_main(
except KeyboardInterrupt: except KeyboardInterrupt:
log.warning(f"Actor {actor.uid} received KBI") log.warning(f"Actor {actor.uid} received KBI")
finally:
log.info(f"Actor {actor.uid} terminated") log.info(f"Actor {actor.uid} terminated")