Add `ActorCancelled` as an runtime-wide-signal
As in a layer "above" a KBI/SIGINT but "below" a `ContextCancelled` and generally signalling an interrupt which requests cancellation of the actor's `trio.run()`. Impl deats, - mk the new exc type inherit from our ctxc (for now) but overriding the `.canceller` impl to, * pull from the `RemoteActorError._extra_msgdata: dict` when no `._ipc_msg` is set (which is always to start, until we incorporate a new `CancelActor` msg type). * not allow a `None` value since we should key-error if not set per prev bullet. - Mk adjustments (related) to parent `RemoteActorError.pformat()` to accommodate showing the `.canceller` field in repr output, * change `.relay_uid` to not crash when `._ipc_msg` is unset. * support `.msg.types.Aid` and use its `.reprol()` from `._mk_fields_str()`. * always call `._mk_fields_str()`, not just when `tb_str` is provided, and for now use any `._message` in-place of a `tb_str` when undefined.actor_cancelled_exc_type
parent
5ab642bdf0
commit
dc806b8aba
|
@ -46,6 +46,7 @@ from msgspec import (
|
|||
from tractor._state import current_actor
|
||||
from tractor.log import get_logger
|
||||
from tractor.msg import (
|
||||
Aid,
|
||||
Error,
|
||||
PayloadMsg,
|
||||
MsgType,
|
||||
|
@ -479,8 +480,9 @@ class RemoteActorError(Exception):
|
|||
|
||||
@property
|
||||
def relay_uid(self) -> tuple[str, str]|None:
|
||||
if msg := self._ipc_msg:
|
||||
return tuple(
|
||||
self._ipc_msg.relay_path[-1]
|
||||
msg.relay_path[-1]
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -521,7 +523,8 @@ class RemoteActorError(Exception):
|
|||
for key in fields:
|
||||
if (
|
||||
key == 'relay_uid'
|
||||
and not self.is_inception()
|
||||
and
|
||||
not self.is_inception()
|
||||
):
|
||||
continue
|
||||
|
||||
|
@ -534,6 +537,13 @@ class RemoteActorError(Exception):
|
|||
None,
|
||||
)
|
||||
)
|
||||
if (
|
||||
key == 'canceller'
|
||||
and
|
||||
isinstance(val, Aid)
|
||||
):
|
||||
val: str = val.reprol(sin_uuid=False)
|
||||
|
||||
# TODO: for `.relay_path` on multiline?
|
||||
# if not isinstance(val, str):
|
||||
# val_str = pformat(val)
|
||||
|
@ -623,12 +633,19 @@ class RemoteActorError(Exception):
|
|||
# IFF there is an embedded traceback-str we always
|
||||
# draw the ascii-box around it.
|
||||
body: str = ''
|
||||
if tb_str := self.tb_str:
|
||||
fields: str = self._mk_fields_str(
|
||||
_body_fields
|
||||
+
|
||||
self.extra_body_fields,
|
||||
)
|
||||
|
||||
# ?TODO, ensure the `.message` doesn't show up 2x in
|
||||
# output ya?
|
||||
tb_str: str = (
|
||||
self.tb_str
|
||||
or
|
||||
self._message
|
||||
)
|
||||
from tractor.devx import (
|
||||
pformat_boxed_tb,
|
||||
)
|
||||
|
@ -640,7 +657,7 @@ class RemoteActorError(Exception):
|
|||
# just after <Type(
|
||||
# |___ ..
|
||||
tb_body_indent=1,
|
||||
boxer_header=self.relay_uid,
|
||||
boxer_header=self.relay_uid or '-',
|
||||
)
|
||||
|
||||
# !TODO, it'd be nice to import these top level without
|
||||
|
@ -713,6 +730,10 @@ class RemoteActorError(Exception):
|
|||
|
||||
class ContextCancelled(RemoteActorError):
|
||||
'''
|
||||
IPC context cancellation signal/msg.
|
||||
|
||||
Often reffed with the short-hand: "ctxc".
|
||||
|
||||
Inter-actor task context was cancelled by either a call to
|
||||
``Portal.cancel_actor()`` or ``Context.cancel()``.
|
||||
|
||||
|
@ -737,8 +758,8 @@ class ContextCancelled(RemoteActorError):
|
|||
|
||||
- (simulating) an IPC transport network outage
|
||||
- a (malicious) pkt sent specifically to cancel an actor's
|
||||
runtime non-gracefully without ensuring ongoing RPC tasks are
|
||||
incrementally cancelled as is done with:
|
||||
runtime non-gracefully without ensuring ongoing RPC tasks
|
||||
are incrementally cancelled as is done with:
|
||||
`Actor`
|
||||
|_`.cancel()`
|
||||
|_`.cancel_soon()`
|
||||
|
@ -759,6 +780,59 @@ class ContextCancelled(RemoteActorError):
|
|||
# src_actor_uid = canceller
|
||||
|
||||
|
||||
class ActorCancelled(ContextCancelled):
|
||||
'''
|
||||
Runtime-layer cancellation signal/msg.
|
||||
|
||||
Indicates a "graceful interrupt" of the machinery scheduled by
|
||||
the py-proc's `trio.run()`.
|
||||
|
||||
Often reffed with the short-hand: "actorc".
|
||||
|
||||
Raised from within `an: ActorNursery` (via an `ExceptionGroup`)
|
||||
when an actor has been "process wide" cancel-called using any of,
|
||||
|
||||
- `ActorNursery.cancel()`
|
||||
- `Portal.cancel_actor()`
|
||||
|
||||
**and** that cancel request was part of a "non graceful" cancel
|
||||
condition.
|
||||
|
||||
That is, whenever an exception is to be raised outside an `an`
|
||||
scope-block due to some error raised-in/relayed-to that scope. In
|
||||
such cases for every subactor which was cancelledand subsequently
|
||||
( and according to the `an`'s supervision strat ) this is
|
||||
normally raised per subactor portal.
|
||||
|
||||
'''
|
||||
@property
|
||||
def canceller(self) -> Aid:
|
||||
'''
|
||||
Return the (maybe) `Actor.aid: Aid` for the requesting-author
|
||||
of this actorc.
|
||||
|
||||
Emit a warning msg when `.canceller` has not been set.
|
||||
|
||||
See additional relevant notes in
|
||||
`ContextCancelled.canceller`.
|
||||
|
||||
'''
|
||||
value: tuple[str, str]|None
|
||||
if msg := self._ipc_msg:
|
||||
value = msg.canceller
|
||||
else:
|
||||
value = self._extra_msgdata['canceller']
|
||||
|
||||
if value:
|
||||
return value
|
||||
|
||||
log.warning(
|
||||
'IPC Context cancelled without a requesting actor?\n'
|
||||
'Maybe the IPC transport ended abruptly?\n\n'
|
||||
f'{self}'
|
||||
)
|
||||
|
||||
|
||||
class MsgTypeError(
|
||||
RemoteActorError,
|
||||
):
|
||||
|
|
Loading…
Reference in New Issue