forked from goodboy/tractor
1
0
Fork 0

Port to new debug api, set `_is_root` state flag on startup

debug_tests
Tyler Goodlet 2020-09-12 11:48:20 -04:00
parent 150179bfe4
commit 8b6e9f5530
1 changed files with 22 additions and 15 deletions

View File

@ -7,7 +7,6 @@ from itertools import chain
import importlib import importlib
import importlib.util import importlib.util
import inspect import inspect
import bdb
import uuid import uuid
import typing import typing
from typing import Dict, List, Tuple, Any, Optional from typing import Dict, List, Tuple, Any, Optional
@ -27,6 +26,7 @@ from ._exceptions import (
unpack_error, unpack_error,
ModuleNotExposed ModuleNotExposed
) )
from ._debug import _maybe_enter_pm
from ._discovery import get_arbiter from ._discovery import get_arbiter
from ._portal import Portal from ._portal import Portal
from . import _state from . import _state
@ -127,13 +127,8 @@ async def _invoke(
await chan.send({'return': await coro, 'cid': cid}) await chan.send({'return': await coro, 'cid': cid})
except (Exception, trio.MultiError) as err: except (Exception, trio.MultiError) as err:
# NOTE: don't enter debug mode recursively after quitting pdb # NOTE: don't enter debug mode recursively after quitting pdb
if _state.debug_mode() and not isinstance(err, bdb.BdbQuit):
# Allow for pdb control in parent
from ._debug import post_mortem
log.exception("Actor crashed, entering debug mode:")
await post_mortem()
else:
log.exception("Actor crashed:") log.exception("Actor crashed:")
await _maybe_enter_pm(err)
# always ship errors back to caller # always ship errors back to caller
err_msg = pack_error(err) err_msg = pack_error(err)
@ -201,6 +196,7 @@ class Actor:
""" """
self.name = name self.name = name
self.uid = (name, uid or str(uuid.uuid4())) self.uid = (name, uid or str(uuid.uuid4()))
self._is_cancelled: bool = False
# retreive and store parent `__main__` data which # retreive and store parent `__main__` data which
# will be passed to children # will be passed to children
@ -251,7 +247,7 @@ class Actor:
self._parent_chan: Optional[Channel] = None self._parent_chan: Optional[Channel] = None
self._forkserver_info: Optional[ self._forkserver_info: Optional[
Tuple[Any, Any, Any, Any, Any]] = None Tuple[Any, Any, Any, Any, Any]] = None
self._actoruid2nursery: Dict[str, 'ActorNursery'] = {} self._actoruid2nursery: Dict[str, 'ActorNursery'] = {} # noqa
async def wait_for_peer( async def wait_for_peer(
self, uid: Tuple[str, str] self, uid: Tuple[str, str]
@ -564,7 +560,7 @@ class Actor:
f"Exiting msg loop for {chan} from {chan.uid} " f"Exiting msg loop for {chan} from {chan.uid} "
f"with last msg:\n{msg}") f"with last msg:\n{msg}")
async def _chan_to_parent( async def _from_parent(
self, self,
parent_addr: Optional[Tuple[str, int]], parent_addr: Optional[Tuple[str, int]],
) -> Tuple[Channel, Optional[Tuple[str, int]]]: ) -> Tuple[Channel, Optional[Tuple[str, int]]]:
@ -593,6 +589,10 @@ class Actor:
parent_data.pop('bind_host'), parent_data.pop('bind_host'),
parent_data.pop('bind_port'), parent_data.pop('bind_port'),
) )
rvs = parent_data.pop('_runtime_vars')
rvs['_is_root'] = False
_state._runtime_vars.update(rvs)
for attr, value in parent_data.items(): for attr, value in parent_data.items():
setattr(self, attr, value) setattr(self, attr, value)
@ -630,13 +630,13 @@ class Actor:
# establish primary connection with immediate parent # establish primary connection with immediate parent
self._parent_chan = None self._parent_chan = None
if parent_addr is not None: if parent_addr is not None:
self._parent_chan, accept_addr_from_rent = await self._chan_to_parent( self._parent_chan, accept_addr_rent = await self._from_parent(
parent_addr) parent_addr)
# either it's passed in because we're not a child # either it's passed in because we're not a child
# or because we're running in mp mode # or because we're running in mp mode
if accept_addr_from_rent is not None: if accept_addr_rent is not None:
accept_addr = accept_addr_from_rent accept_addr = accept_addr_rent
# load exposed/allowed RPC modules # load exposed/allowed RPC modules
# XXX: do this **after** establishing a channel to the parent # XXX: do this **after** establishing a channel to the parent
@ -711,7 +711,7 @@ class Actor:
# Blocks here as expected until the root nursery is # Blocks here as expected until the root nursery is
# killed (i.e. this actor is cancelled or signalled by the parent) # killed (i.e. this actor is cancelled or signalled by the parent)
except (trio.MultiError, Exception) as err: except Exception as err:
if not registered_with_arbiter: if not registered_with_arbiter:
# TODO: I guess we could try to connect back # TODO: I guess we could try to connect back
# to the parent through a channel and engage a debugger # to the parent through a channel and engage a debugger
@ -822,6 +822,8 @@ class Actor:
spawning new rpc tasks spawning new rpc tasks
- return control the parent channel message loop - return control the parent channel message loop
""" """
self._is_cancelled = True
# cancel all ongoing rpc tasks # cancel all ongoing rpc tasks
with trio.CancelScope(shield=True): with trio.CancelScope(shield=True):
# kill all ongoing tasks # kill all ongoing tasks
@ -1039,7 +1041,12 @@ async def _start_actor(
parent_addr=None parent_addr=None
) )
) )
try:
result = await main() result = await main()
except (Exception, trio.MultiError) as err:
log.exception("Actor crashed:")
await _maybe_enter_pm(err)
raise
# XXX: the actor is cancelled when this context is complete # XXX: the actor is cancelled when this context is complete
# given that there are no more active peer channels connected # given that there are no more active peer channels connected