Compare commits
No commits in common. "e4ec6b7b0c3401b336e03a4ea7b42015480b7677" and "3538ccd7992282a1b81e3560282a4e03b9e667d2" have entirely different histories.
e4ec6b7b0c
...
3538ccd799
|
@ -146,10 +146,9 @@ def in_prompt_msg(
|
||||||
log/REPL output for a given `pdb` interact point.
|
log/REPL output for a given `pdb` interact point.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = False
|
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if part not in prompt:
|
if part not in prompt:
|
||||||
|
|
||||||
if pause_on_false:
|
if pause_on_false:
|
||||||
import pdbp
|
import pdbp
|
||||||
pdbp.set_trace()
|
pdbp.set_trace()
|
||||||
|
@ -168,7 +167,6 @@ def assert_before(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|
||||||
) -> None:
|
) -> None:
|
||||||
__tracebackhide__: bool = False
|
|
||||||
|
|
||||||
# as in before the prompt end
|
# as in before the prompt end
|
||||||
before: str = str(child.before.decode())
|
before: str = str(child.before.decode())
|
||||||
|
@ -221,10 +219,7 @@ def ctlc(
|
||||||
],
|
],
|
||||||
ids=lambda item: f'{item[0]} -> {item[1]}',
|
ids=lambda item: f'{item[0]} -> {item[1]}',
|
||||||
)
|
)
|
||||||
def test_root_actor_error(
|
def test_root_actor_error(spawn, user_in_out):
|
||||||
spawn,
|
|
||||||
user_in_out,
|
|
||||||
):
|
|
||||||
'''
|
'''
|
||||||
Demonstrate crash handler entering pdb from basic error in root actor.
|
Demonstrate crash handler entering pdb from basic error in root actor.
|
||||||
|
|
||||||
|
@ -470,12 +465,8 @@ def test_subactor_breakpoint(
|
||||||
child.expect(PROMPT)
|
child.expect(PROMPT)
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert in_prompt_msg(
|
assert "RemoteActorError: ('breakpoint_forever'" in before
|
||||||
before,
|
assert 'bdb.BdbQuit' in before
|
||||||
['RemoteActorError:',
|
|
||||||
"('breakpoint_forever'",
|
|
||||||
'bdb.BdbQuit',]
|
|
||||||
)
|
|
||||||
|
|
||||||
if ctlc:
|
if ctlc:
|
||||||
do_ctlc(child)
|
do_ctlc(child)
|
||||||
|
@ -487,12 +478,8 @@ def test_subactor_breakpoint(
|
||||||
child.expect(pexpect.EOF)
|
child.expect(pexpect.EOF)
|
||||||
|
|
||||||
before = str(child.before.decode())
|
before = str(child.before.decode())
|
||||||
assert in_prompt_msg(
|
assert "RemoteActorError: ('breakpoint_forever'" in before
|
||||||
before,
|
assert 'bdb.BdbQuit' in before
|
||||||
['RemoteActorError:',
|
|
||||||
"('breakpoint_forever'",
|
|
||||||
'bdb.BdbQuit',]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@has_nested_actors
|
@has_nested_actors
|
||||||
|
@ -760,9 +747,8 @@ def test_multi_daemon_subactors(
|
||||||
# boxed error raised in root task
|
# boxed error raised in root task
|
||||||
# "Attaching to pdb in crashed actor: ('root'",
|
# "Attaching to pdb in crashed actor: ('root'",
|
||||||
_crash_msg,
|
_crash_msg,
|
||||||
"('root'", # should attach in root
|
"('root'",
|
||||||
"_exceptions.RemoteActorError:", # with an embedded RAE for..
|
"_exceptions.RemoteActorError: ('name_error'",
|
||||||
"('name_error'", # the src subactor which raised
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -863,11 +849,10 @@ def test_multi_nested_subactors_error_through_nurseries(
|
||||||
# https://github.com/goodboy/tractor/issues/320
|
# https://github.com/goodboy/tractor/issues/320
|
||||||
# ctlc: bool,
|
# ctlc: bool,
|
||||||
):
|
):
|
||||||
'''
|
"""Verify deeply nested actors that error trigger debugger entries
|
||||||
Verify deeply nested actors that error trigger debugger entries
|
|
||||||
at each actor nurserly (level) all the way up the tree.
|
at each actor nurserly (level) all the way up the tree.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
# NOTE: previously, inside this script was a bug where if the
|
# NOTE: previously, inside this script was a bug where if the
|
||||||
# parent errors before a 2-levels-lower actor has released the lock,
|
# parent errors before a 2-levels-lower actor has released the lock,
|
||||||
# the parent tries to cancel it but it's stuck in the debugger?
|
# the parent tries to cancel it but it's stuck in the debugger?
|
||||||
|
@ -887,31 +872,22 @@ def test_multi_nested_subactors_error_through_nurseries(
|
||||||
except EOF:
|
except EOF:
|
||||||
break
|
break
|
||||||
|
|
||||||
assert_before(
|
assert_before(child, [
|
||||||
child,
|
|
||||||
[ # boxed source errors
|
|
||||||
"NameError: name 'doggypants' is not defined",
|
|
||||||
"tractor._exceptions.RemoteActorError:",
|
|
||||||
"('name_error'",
|
|
||||||
"bdb.BdbQuit",
|
|
||||||
|
|
||||||
# first level subtrees
|
# boxed source errors
|
||||||
# "tractor._exceptions.RemoteActorError: ('spawner0'",
|
"NameError: name 'doggypants' is not defined",
|
||||||
"src_uid=('spawner0'",
|
"tractor._exceptions.RemoteActorError: ('name_error'",
|
||||||
|
"bdb.BdbQuit",
|
||||||
|
|
||||||
# "tractor._exceptions.RemoteActorError: ('spawner1'",
|
# first level subtrees
|
||||||
|
"tractor._exceptions.RemoteActorError: ('spawner0'",
|
||||||
|
# "tractor._exceptions.RemoteActorError: ('spawner1'",
|
||||||
|
|
||||||
# propagation of errors up through nested subtrees
|
# propagation of errors up through nested subtrees
|
||||||
# "tractor._exceptions.RemoteActorError: ('spawn_until_0'",
|
"tractor._exceptions.RemoteActorError: ('spawn_until_0'",
|
||||||
# "tractor._exceptions.RemoteActorError: ('spawn_until_1'",
|
"tractor._exceptions.RemoteActorError: ('spawn_until_1'",
|
||||||
# "tractor._exceptions.RemoteActorError: ('spawn_until_2'",
|
"tractor._exceptions.RemoteActorError: ('spawn_until_2'",
|
||||||
# ^-NOTE-^ old RAE repr, new one is below with a field
|
])
|
||||||
# showing the src actor's uid.
|
|
||||||
"src_uid=('spawn_until_0'",
|
|
||||||
"relay_uid=('spawn_until_1'",
|
|
||||||
"src_uid=('spawn_until_2'",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(15)
|
@pytest.mark.timeout(15)
|
||||||
|
@ -1045,16 +1021,13 @@ def test_different_debug_mode_per_actor(
|
||||||
# msg reported back from the debug mode actor is processed.
|
# msg reported back from the debug mode actor is processed.
|
||||||
# assert "tractor._exceptions.RemoteActorError: ('debugged_boi'" in before
|
# assert "tractor._exceptions.RemoteActorError: ('debugged_boi'" in before
|
||||||
|
|
||||||
|
assert "tractor._exceptions.RemoteActorError: ('crash_boi'" in before
|
||||||
|
|
||||||
# the crash boi should not have made a debugger request but
|
# the crash boi should not have made a debugger request but
|
||||||
# instead crashed completely
|
# instead crashed completely
|
||||||
assert_before(
|
assert "tractor._exceptions.RemoteActorError: ('crash_boi'" in before
|
||||||
child,
|
assert "RuntimeError" in before
|
||||||
[
|
|
||||||
"tractor._exceptions.RemoteActorError:",
|
|
||||||
"src_uid=('crash_boi'",
|
|
||||||
"RuntimeError",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_pause_from_sync(
|
def test_pause_from_sync(
|
||||||
|
@ -1073,15 +1046,13 @@ def test_pause_from_sync(
|
||||||
assert_before(
|
assert_before(
|
||||||
child,
|
child,
|
||||||
[
|
[
|
||||||
|
'`greenback` portal opened!',
|
||||||
# pre-prompt line
|
# pre-prompt line
|
||||||
_pause_msg,
|
_pause_msg, "('root'",
|
||||||
"<Task '__main__.main'",
|
|
||||||
"('root'",
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if ctlc:
|
if ctlc:
|
||||||
do_ctlc(child)
|
do_ctlc(child)
|
||||||
|
|
||||||
child.sendline('c')
|
child.sendline('c')
|
||||||
child.expect(PROMPT)
|
child.expect(PROMPT)
|
||||||
|
|
||||||
|
@ -1098,7 +1069,6 @@ def test_pause_from_sync(
|
||||||
|
|
||||||
if ctlc:
|
if ctlc:
|
||||||
do_ctlc(child)
|
do_ctlc(child)
|
||||||
|
|
||||||
child.sendline('c')
|
child.sendline('c')
|
||||||
child.expect(PROMPT)
|
child.expect(PROMPT)
|
||||||
assert_before(
|
assert_before(
|
||||||
|
@ -1108,7 +1078,6 @@ def test_pause_from_sync(
|
||||||
|
|
||||||
if ctlc:
|
if ctlc:
|
||||||
do_ctlc(child)
|
do_ctlc(child)
|
||||||
|
|
||||||
child.sendline('c')
|
child.sendline('c')
|
||||||
child.expect(PROMPT)
|
child.expect(PROMPT)
|
||||||
# non-main thread case
|
# non-main thread case
|
||||||
|
@ -1120,22 +1089,5 @@ def test_pause_from_sync(
|
||||||
|
|
||||||
if ctlc:
|
if ctlc:
|
||||||
do_ctlc(child)
|
do_ctlc(child)
|
||||||
|
|
||||||
child.sendline('c')
|
child.sendline('c')
|
||||||
child.expect(pexpect.EOF)
|
child.expect(pexpect.EOF)
|
||||||
|
|
||||||
|
|
||||||
# TODO!
|
|
||||||
def test_correct_frames_below_hidden():
|
|
||||||
'''
|
|
||||||
Ensure that once a `tractor.pause()` enages, when the user
|
|
||||||
inputs a "next"/"n" command the actual next line steps
|
|
||||||
and that using a "step"/"s" into the next LOC, particuarly
|
|
||||||
`tractor` APIs, you can step down into that code.
|
|
||||||
|
|
||||||
'''
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
def test_cant_pause_from_paused_task():
|
|
||||||
...
|
|
||||||
|
|
|
@ -33,7 +33,6 @@ from .log import (
|
||||||
get_logger,
|
get_logger,
|
||||||
)
|
)
|
||||||
from . import _state
|
from . import _state
|
||||||
from .devx import _debug
|
|
||||||
from .to_asyncio import run_as_asyncio_guest
|
from .to_asyncio import run_as_asyncio_guest
|
||||||
from ._runtime import (
|
from ._runtime import (
|
||||||
async_main,
|
async_main,
|
||||||
|
@ -97,6 +96,7 @@ def _mp_main(
|
||||||
|
|
||||||
|
|
||||||
def _trio_main(
|
def _trio_main(
|
||||||
|
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
*,
|
*,
|
||||||
parent_addr: tuple[str, int] | None = None,
|
parent_addr: tuple[str, int] | None = None,
|
||||||
|
@ -107,9 +107,7 @@ def _trio_main(
|
||||||
Entry point for a `trio_run_in_process` subactor.
|
Entry point for a `trio_run_in_process` subactor.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# __tracebackhide__: bool = True
|
__tracebackhide__: bool = True
|
||||||
_debug.hide_runtime_frames()
|
|
||||||
|
|
||||||
_state._current_actor = actor
|
_state._current_actor = actor
|
||||||
trio_main = partial(
|
trio_main = partial(
|
||||||
async_main,
|
async_main,
|
||||||
|
@ -148,6 +146,7 @@ def _trio_main(
|
||||||
+
|
+
|
||||||
actor_info
|
actor_info
|
||||||
)
|
)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
log.info(
|
log.info(
|
||||||
'Subactor terminated\n'
|
'Subactor terminated\n'
|
||||||
|
|
|
@ -35,6 +35,7 @@ import trio
|
||||||
from msgspec import (
|
from msgspec import (
|
||||||
defstruct,
|
defstruct,
|
||||||
msgpack,
|
msgpack,
|
||||||
|
Raw,
|
||||||
structs,
|
structs,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
|
@ -43,12 +44,11 @@ from tractor._state import current_actor
|
||||||
from tractor.log import get_logger
|
from tractor.log import get_logger
|
||||||
from tractor.msg import (
|
from tractor.msg import (
|
||||||
Error,
|
Error,
|
||||||
PayloadMsg,
|
|
||||||
MsgType,
|
MsgType,
|
||||||
MsgCodec,
|
|
||||||
MsgDec,
|
|
||||||
Stop,
|
Stop,
|
||||||
types as msgtypes,
|
types as msgtypes,
|
||||||
|
MsgCodec,
|
||||||
|
MsgDec,
|
||||||
)
|
)
|
||||||
from tractor.msg.pretty_struct import (
|
from tractor.msg.pretty_struct import (
|
||||||
iter_fields,
|
iter_fields,
|
||||||
|
@ -156,7 +156,6 @@ def pack_from_raise(
|
||||||
`Error`-msg using `pack_error()` to extract the tb info.
|
`Error`-msg using `pack_error()` to extract the tb info.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = True
|
|
||||||
try:
|
try:
|
||||||
raise local_err
|
raise local_err
|
||||||
except type(local_err) as local_err:
|
except type(local_err) as local_err:
|
||||||
|
@ -526,26 +525,10 @@ class RemoteActorError(Exception):
|
||||||
if not with_type_header:
|
if not with_type_header:
|
||||||
body = '\n' + body
|
body = '\n' + body
|
||||||
else:
|
else:
|
||||||
first: str = ''
|
body: str = textwrap.indent(
|
||||||
message: str = self._message
|
self._message,
|
||||||
|
prefix=' ',
|
||||||
# split off the first line so it isn't indented
|
) + '\n'
|
||||||
# the same like the "boxed content".
|
|
||||||
if not with_type_header:
|
|
||||||
lines: list[str] = message.splitlines()
|
|
||||||
first = lines[0]
|
|
||||||
message = ''.join(lines[1:])
|
|
||||||
|
|
||||||
body: str = (
|
|
||||||
first
|
|
||||||
+
|
|
||||||
textwrap.indent(
|
|
||||||
message,
|
|
||||||
prefix=' ',
|
|
||||||
)
|
|
||||||
+
|
|
||||||
'\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
if with_type_header:
|
if with_type_header:
|
||||||
tail: str = ')>'
|
tail: str = ')>'
|
||||||
|
@ -751,38 +734,25 @@ class MsgTypeError(
|
||||||
def from_decode(
|
def from_decode(
|
||||||
cls,
|
cls,
|
||||||
message: str,
|
message: str,
|
||||||
|
msgdict: dict,
|
||||||
ipc_msg: PayloadMsg|None = None,
|
|
||||||
msgdict: dict|None = None,
|
|
||||||
|
|
||||||
) -> MsgTypeError:
|
) -> MsgTypeError:
|
||||||
'''
|
|
||||||
Constuctor for easy creation from (presumably) catching
|
|
||||||
the backend interchange lib's underlying validation error
|
|
||||||
and passing context-specific meta-data to `_mk_msg_type_err()`
|
|
||||||
(which is normally the caller of this).
|
|
||||||
|
|
||||||
'''
|
|
||||||
# if provided, expand and pack all RAE compat fields into the
|
|
||||||
# `._extra_msgdata` auxillary data `dict` internal to
|
|
||||||
# `RemoteActorError`.
|
|
||||||
extra_msgdata: dict = {}
|
|
||||||
if msgdict:
|
|
||||||
extra_msgdata: dict = {
|
|
||||||
k: v
|
|
||||||
for k, v in msgdict.items()
|
|
||||||
if k in _ipcmsg_keys
|
|
||||||
}
|
|
||||||
# NOTE: original "vanilla decode" of the msg-bytes
|
|
||||||
# is placed inside a value readable from
|
|
||||||
# `.msgdata['_msg_dict']`
|
|
||||||
extra_msgdata['_msg_dict'] = msgdict
|
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
message=message,
|
message=message,
|
||||||
boxed_type=cls,
|
boxed_type=cls,
|
||||||
ipc_msg=ipc_msg,
|
|
||||||
**extra_msgdata,
|
# NOTE: original "vanilla decode" of the msg-bytes
|
||||||
|
# is placed inside a value readable from
|
||||||
|
# `.msgdata['_msg_dict']`
|
||||||
|
_msg_dict=msgdict,
|
||||||
|
|
||||||
|
# expand and pack all RAE compat fields
|
||||||
|
# into the `._extra_msgdata` aux `dict`.
|
||||||
|
**{
|
||||||
|
k: v
|
||||||
|
for k, v in msgdict.items()
|
||||||
|
if k in _ipcmsg_keys
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1106,7 +1076,7 @@ _raise_from_no_key_in_msg = _raise_from_unexpected_msg
|
||||||
|
|
||||||
|
|
||||||
def _mk_msg_type_err(
|
def _mk_msg_type_err(
|
||||||
msg: Any|bytes|MsgType,
|
msg: Any|bytes|Raw,
|
||||||
codec: MsgCodec|MsgDec,
|
codec: MsgCodec|MsgDec,
|
||||||
|
|
||||||
message: str|None = None,
|
message: str|None = None,
|
||||||
|
@ -1115,7 +1085,6 @@ def _mk_msg_type_err(
|
||||||
src_validation_error: ValidationError|None = None,
|
src_validation_error: ValidationError|None = None,
|
||||||
src_type_error: TypeError|None = None,
|
src_type_error: TypeError|None = None,
|
||||||
is_invalid_payload: bool = False,
|
is_invalid_payload: bool = False,
|
||||||
src_err_msg: Error|None = None,
|
|
||||||
|
|
||||||
**mte_kwargs,
|
**mte_kwargs,
|
||||||
|
|
||||||
|
@ -1190,10 +1159,9 @@ def _mk_msg_type_err(
|
||||||
# only the payload being wrong?
|
# only the payload being wrong?
|
||||||
# -[ ] maybe the better design is to break this construct
|
# -[ ] maybe the better design is to break this construct
|
||||||
# logic into a separate explicit helper raiser-func?
|
# logic into a separate explicit helper raiser-func?
|
||||||
msg_dict = None
|
msg_dict: dict = {}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg: bytes
|
|
||||||
# decode the msg-bytes using the std msgpack
|
# decode the msg-bytes using the std msgpack
|
||||||
# interchange-prot (i.e. without any
|
# interchange-prot (i.e. without any
|
||||||
# `msgspec.Struct` handling) so that we can
|
# `msgspec.Struct` handling) so that we can
|
||||||
|
@ -1238,14 +1206,6 @@ def _mk_msg_type_err(
|
||||||
msgtyperr = MsgTypeError.from_decode(
|
msgtyperr = MsgTypeError.from_decode(
|
||||||
message=message,
|
message=message,
|
||||||
msgdict=msg_dict,
|
msgdict=msg_dict,
|
||||||
|
|
||||||
# NOTE: for the send-side `.started()` pld-validate
|
|
||||||
# case we actually set the `._ipc_msg` AFTER we return
|
|
||||||
# from here inside `Context.started()` since we actually
|
|
||||||
# want to emulate the `Error` from the mte we build here
|
|
||||||
# Bo
|
|
||||||
# so by default in that case this is set to `None`
|
|
||||||
ipc_msg=src_err_msg,
|
|
||||||
)
|
)
|
||||||
msgtyperr.__cause__ = src_validation_error
|
msgtyperr.__cause__ = src_validation_error
|
||||||
return msgtyperr
|
return msgtyperr
|
||||||
|
|
|
@ -91,16 +91,12 @@ async def open_root_actor(
|
||||||
# and that this call creates it.
|
# and that this call creates it.
|
||||||
ensure_registry: bool = False,
|
ensure_registry: bool = False,
|
||||||
|
|
||||||
hide_tb: bool = True,
|
|
||||||
|
|
||||||
) -> Actor:
|
) -> Actor:
|
||||||
'''
|
'''
|
||||||
Runtime init entry point for ``tractor``.
|
Runtime init entry point for ``tractor``.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = hide_tb
|
__tracebackhide__ = True
|
||||||
_debug.hide_runtime_frames()
|
|
||||||
|
|
||||||
# TODO: stick this in a `@cm` defined in `devx._debug`?
|
# TODO: stick this in a `@cm` defined in `devx._debug`?
|
||||||
#
|
#
|
||||||
# Override the global debugger hook to make it play nice with
|
# Override the global debugger hook to make it play nice with
|
||||||
|
@ -129,7 +125,7 @@ async def open_root_actor(
|
||||||
# usage by a clobbered TTY's stdstreams!
|
# usage by a clobbered TTY's stdstreams!
|
||||||
def block_bps(*args, **kwargs):
|
def block_bps(*args, **kwargs):
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
'Trying to use `breakpoint()` eh?\n\n'
|
'Trying to use `breakpoint()` eh?\n'
|
||||||
'Welp, `tractor` blocks `breakpoint()` built-in calls by default!\n'
|
'Welp, `tractor` blocks `breakpoint()` built-in calls by default!\n'
|
||||||
'If you need to use it please install `greenback` and set '
|
'If you need to use it please install `greenback` and set '
|
||||||
'`debug_mode=True` when opening the runtime '
|
'`debug_mode=True` when opening the runtime '
|
||||||
|
@ -137,9 +133,7 @@ async def open_root_actor(
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.breakpointhook = block_bps
|
sys.breakpointhook = block_bps
|
||||||
# lol ok,
|
# os.environ['PYTHONBREAKPOINT'] = None
|
||||||
# https://docs.python.org/3/library/sys.html#sys.breakpointhook
|
|
||||||
os.environ['PYTHONBREAKPOINT'] = "0"
|
|
||||||
|
|
||||||
# attempt to retreive ``trio``'s sigint handler and stash it
|
# attempt to retreive ``trio``'s sigint handler and stash it
|
||||||
# on our debugger lock state.
|
# on our debugger lock state.
|
||||||
|
@ -209,7 +203,6 @@ async def open_root_actor(
|
||||||
):
|
):
|
||||||
loglevel = 'PDB'
|
loglevel = 'PDB'
|
||||||
|
|
||||||
|
|
||||||
elif debug_mode:
|
elif debug_mode:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Debug mode is only supported for the `trio` backend!"
|
"Debug mode is only supported for the `trio` backend!"
|
||||||
|
|
|
@ -692,21 +692,21 @@ class Actor:
|
||||||
proc: trio.Process
|
proc: trio.Process
|
||||||
_, proc, _ = entry
|
_, proc, _ = entry
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(poll := getattr(proc, 'poll', None))
|
(poll := getattr(proc, 'poll', None))
|
||||||
and poll() is None
|
and poll() is None
|
||||||
):
|
):
|
||||||
log.cancel(
|
log.cancel(
|
||||||
'Root actor reports no-more-peers, BUT\n'
|
'Root actor reports no-more-peers, BUT\n'
|
||||||
'a DISCONNECTED child still has the debug '
|
'a DISCONNECTED child still has the debug '
|
||||||
'lock!\n\n'
|
'lock!\n\n'
|
||||||
# f'root uid: {self.uid}\n'
|
# f'root uid: {self.uid}\n'
|
||||||
f'last disconnected child uid: {uid}\n'
|
f'last disconnected child uid: {uid}\n'
|
||||||
f'locking child uid: {pdb_user_uid}\n'
|
f'locking child uid: {pdb_user_uid}\n'
|
||||||
)
|
)
|
||||||
await maybe_wait_for_debugger(
|
await maybe_wait_for_debugger(
|
||||||
child_in_debug=True
|
child_in_debug=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: just bc a child's transport dropped
|
# TODO: just bc a child's transport dropped
|
||||||
# doesn't mean it's not still using the pdb
|
# doesn't mean it's not still using the pdb
|
||||||
|
@ -1140,6 +1140,7 @@ class Actor:
|
||||||
requester_type,
|
requester_type,
|
||||||
req_chan,
|
req_chan,
|
||||||
log_meth,
|
log_meth,
|
||||||
|
|
||||||
) = (
|
) = (
|
||||||
req_chan.uid,
|
req_chan.uid,
|
||||||
'peer',
|
'peer',
|
||||||
|
@ -1172,11 +1173,7 @@ class Actor:
|
||||||
# with the root actor in this tree
|
# with the root actor in this tree
|
||||||
debug_req = _debug.DebugStatus
|
debug_req = _debug.DebugStatus
|
||||||
lock_req_ctx: Context = debug_req.req_ctx
|
lock_req_ctx: Context = debug_req.req_ctx
|
||||||
if (
|
if lock_req_ctx is not None:
|
||||||
lock_req_ctx
|
|
||||||
and
|
|
||||||
lock_req_ctx.has_outcome
|
|
||||||
):
|
|
||||||
msg += (
|
msg += (
|
||||||
'-> Cancelling active debugger request..\n'
|
'-> Cancelling active debugger request..\n'
|
||||||
f'|_{_debug.Lock.repr()}\n\n'
|
f'|_{_debug.Lock.repr()}\n\n'
|
||||||
|
|
|
@ -43,7 +43,6 @@ from tractor._state import (
|
||||||
is_main_process,
|
is_main_process,
|
||||||
is_root_process,
|
is_root_process,
|
||||||
debug_mode,
|
debug_mode,
|
||||||
_runtime_vars,
|
|
||||||
)
|
)
|
||||||
from tractor.log import get_logger
|
from tractor.log import get_logger
|
||||||
from tractor._portal import Portal
|
from tractor._portal import Portal
|
||||||
|
@ -300,6 +299,7 @@ async def hard_kill(
|
||||||
|
|
||||||
|
|
||||||
async def soft_kill(
|
async def soft_kill(
|
||||||
|
|
||||||
proc: ProcessType,
|
proc: ProcessType,
|
||||||
wait_func: Callable[
|
wait_func: Callable[
|
||||||
[ProcessType],
|
[ProcessType],
|
||||||
|
@ -329,18 +329,6 @@ async def soft_kill(
|
||||||
await wait_func(proc)
|
await wait_func(proc)
|
||||||
|
|
||||||
except trio.Cancelled:
|
except trio.Cancelled:
|
||||||
with trio.CancelScope(shield=True):
|
|
||||||
await maybe_wait_for_debugger(
|
|
||||||
child_in_debug=_runtime_vars.get(
|
|
||||||
'_debug_mode', False
|
|
||||||
),
|
|
||||||
header_msg=(
|
|
||||||
'Delaying `soft_kill()` subproc reaper while debugger locked..\n'
|
|
||||||
),
|
|
||||||
# TODO: need a diff value then default?
|
|
||||||
# poll_steps=9999999,
|
|
||||||
)
|
|
||||||
|
|
||||||
# if cancelled during a soft wait, cancel the child
|
# if cancelled during a soft wait, cancel the child
|
||||||
# actor before entering the hard reap sequence
|
# actor before entering the hard reap sequence
|
||||||
# below. This means we try to do a graceful teardown
|
# below. This means we try to do a graceful teardown
|
||||||
|
|
|
@ -48,11 +48,9 @@ from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
from types import (
|
from types import (
|
||||||
FunctionType,
|
|
||||||
FrameType,
|
FrameType,
|
||||||
ModuleType,
|
ModuleType,
|
||||||
TracebackType,
|
TracebackType,
|
||||||
CodeType,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from msgspec import Struct
|
from msgspec import Struct
|
||||||
|
@ -92,72 +90,43 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
# XXX HACKZONE XXX
|
||||||
|
# hide exit stack frames on nurseries and cancel-scopes!
|
||||||
|
# |_ so avoid seeing it when the `pdbp` REPL is first engaged from
|
||||||
|
# inside a `trio.open_nursery()` scope (with no line after it
|
||||||
|
# in before the block end??).
|
||||||
|
#
|
||||||
|
# TODO: FINALLY got this workin originally with
|
||||||
|
# `@pdbp.hideframe` around the `wrapper()` def embedded inside
|
||||||
|
# `_ki_protection_decoratior()`.. which is in the module:
|
||||||
|
# /home/goodboy/.virtualenvs/tractor311/lib/python3.11/site-packages/trio/_core/_ki.py
|
||||||
|
#
|
||||||
|
# -[ ] make an issue and patch for `trio` core? maybe linked
|
||||||
|
# to the long outstanding `pdb` one below?
|
||||||
|
# |_ it's funny that there's frame hiding throughout `._run.py`
|
||||||
|
# but not where it matters on the below exit funcs..
|
||||||
|
#
|
||||||
|
# -[ ] provide a patchset for the lonstanding
|
||||||
|
# |_ https://github.com/python-trio/trio/issues/1155
|
||||||
|
#
|
||||||
|
# -[ ] make a linked issue to ^ and propose allowing all the
|
||||||
|
# `._core._run` code to have their `__tracebackhide__` value
|
||||||
|
# configurable by a `RunVar` to allow getting scheduler frames
|
||||||
|
# if desired through configuration?
|
||||||
|
#
|
||||||
|
# -[ ] maybe dig into the core `pdb` issue why the extra frame is shown
|
||||||
|
# at all?
|
||||||
|
#
|
||||||
|
pdbp.hideframe(trio._core._run.NurseryManager.__aexit__)
|
||||||
|
pdbp.hideframe(trio._core._run.CancelScope.__exit__)
|
||||||
|
pdbp.hideframe(_GeneratorContextManager.__exit__)
|
||||||
|
pdbp.hideframe(_AsyncGeneratorContextManager.__aexit__)
|
||||||
|
pdbp.hideframe(trio.Event.wait)
|
||||||
|
|
||||||
def hide_runtime_frames() -> dict[FunctionType, CodeType]:
|
__all__ = [
|
||||||
'''
|
'breakpoint',
|
||||||
Hide call-stack frames for various std-lib and `trio`-API primitives
|
'post_mortem',
|
||||||
such that the tracebacks presented from our runtime are as minimized
|
]
|
||||||
as possible, particularly from inside a `PdbREPL`.
|
|
||||||
|
|
||||||
'''
|
|
||||||
# XXX HACKZONE XXX
|
|
||||||
# hide exit stack frames on nurseries and cancel-scopes!
|
|
||||||
# |_ so avoid seeing it when the `pdbp` REPL is first engaged from
|
|
||||||
# inside a `trio.open_nursery()` scope (with no line after it
|
|
||||||
# in before the block end??).
|
|
||||||
#
|
|
||||||
# TODO: FINALLY got this workin originally with
|
|
||||||
# `@pdbp.hideframe` around the `wrapper()` def embedded inside
|
|
||||||
# `_ki_protection_decoratior()`.. which is in the module:
|
|
||||||
# /home/goodboy/.virtualenvs/tractor311/lib/python3.11/site-packages/trio/_core/_ki.py
|
|
||||||
#
|
|
||||||
# -[ ] make an issue and patch for `trio` core? maybe linked
|
|
||||||
# to the long outstanding `pdb` one below?
|
|
||||||
# |_ it's funny that there's frame hiding throughout `._run.py`
|
|
||||||
# but not where it matters on the below exit funcs..
|
|
||||||
#
|
|
||||||
# -[ ] provide a patchset for the lonstanding
|
|
||||||
# |_ https://github.com/python-trio/trio/issues/1155
|
|
||||||
#
|
|
||||||
# -[ ] make a linked issue to ^ and propose allowing all the
|
|
||||||
# `._core._run` code to have their `__tracebackhide__` value
|
|
||||||
# configurable by a `RunVar` to allow getting scheduler frames
|
|
||||||
# if desired through configuration?
|
|
||||||
#
|
|
||||||
# -[ ] maybe dig into the core `pdb` issue why the extra frame is shown
|
|
||||||
# at all?
|
|
||||||
#
|
|
||||||
funcs: list[FunctionType] = [
|
|
||||||
trio._core._run.NurseryManager.__aexit__,
|
|
||||||
trio._core._run.CancelScope.__exit__,
|
|
||||||
_GeneratorContextManager.__exit__,
|
|
||||||
_AsyncGeneratorContextManager.__aexit__,
|
|
||||||
_AsyncGeneratorContextManager.__aenter__,
|
|
||||||
trio.Event.wait,
|
|
||||||
]
|
|
||||||
func_list_str: str = textwrap.indent(
|
|
||||||
"\n".join(f.__qualname__ for f in funcs),
|
|
||||||
prefix=' |_ ',
|
|
||||||
)
|
|
||||||
log.devx(
|
|
||||||
'Hiding the following runtime frames by default:\n'
|
|
||||||
f'{func_list_str}\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
codes: dict[FunctionType, CodeType] = {}
|
|
||||||
for ref in funcs:
|
|
||||||
# stash a pre-modified version of each ref's code-obj
|
|
||||||
# so it can be reverted later if needed.
|
|
||||||
codes[ref] = ref.__code__
|
|
||||||
pdbp.hideframe(ref)
|
|
||||||
#
|
|
||||||
# pdbp.hideframe(trio._core._run.NurseryManager.__aexit__)
|
|
||||||
# pdbp.hideframe(trio._core._run.CancelScope.__exit__)
|
|
||||||
# pdbp.hideframe(_GeneratorContextManager.__exit__)
|
|
||||||
# pdbp.hideframe(_AsyncGeneratorContextManager.__aexit__)
|
|
||||||
# pdbp.hideframe(_AsyncGeneratorContextManager.__aenter__)
|
|
||||||
# pdbp.hideframe(trio.Event.wait)
|
|
||||||
return codes
|
|
||||||
|
|
||||||
|
|
||||||
class LockStatus(
|
class LockStatus(
|
||||||
|
@ -1063,24 +1032,15 @@ async def request_root_stdio_lock(
|
||||||
|
|
||||||
except (
|
except (
|
||||||
BaseException,
|
BaseException,
|
||||||
) as ctx_err:
|
):
|
||||||
message: str = (
|
log.exception(
|
||||||
'Failed during debug request dialog with root actor?\n\n'
|
'Failed during root TTY-lock dialog?\n'
|
||||||
|
f'{req_ctx}\n'
|
||||||
|
|
||||||
|
f'Cancelling IPC ctx!\n'
|
||||||
)
|
)
|
||||||
|
await req_ctx.cancel()
|
||||||
if req_ctx:
|
raise
|
||||||
message += (
|
|
||||||
f'{req_ctx}\n'
|
|
||||||
f'Cancelling IPC ctx!\n'
|
|
||||||
)
|
|
||||||
await req_ctx.cancel()
|
|
||||||
|
|
||||||
else:
|
|
||||||
message += 'Failed during `Portal.open_context()` ?\n'
|
|
||||||
|
|
||||||
log.exception(message)
|
|
||||||
ctx_err.add_note(message)
|
|
||||||
raise ctx_err
|
|
||||||
|
|
||||||
|
|
||||||
except (
|
except (
|
||||||
|
@ -1107,7 +1067,6 @@ async def request_root_stdio_lock(
|
||||||
# ctl-c out of the currently hanging task!
|
# ctl-c out of the currently hanging task!
|
||||||
raise DebugRequestError(
|
raise DebugRequestError(
|
||||||
'Failed to lock stdio from subactor IPC ctx!\n\n'
|
'Failed to lock stdio from subactor IPC ctx!\n\n'
|
||||||
|
|
||||||
f'req_ctx: {DebugStatus.req_ctx}\n'
|
f'req_ctx: {DebugStatus.req_ctx}\n'
|
||||||
) from req_err
|
) from req_err
|
||||||
|
|
||||||
|
@ -1818,7 +1777,7 @@ def _set_trace(
|
||||||
):
|
):
|
||||||
__tracebackhide__: bool = hide_tb
|
__tracebackhide__: bool = hide_tb
|
||||||
actor: tractor.Actor = actor or current_actor()
|
actor: tractor.Actor = actor or current_actor()
|
||||||
task: trio.Task = task or current_task()
|
task: task or current_task()
|
||||||
|
|
||||||
# else:
|
# else:
|
||||||
# TODO: maybe print the actor supervion tree up to the
|
# TODO: maybe print the actor supervion tree up to the
|
||||||
|
|
|
@ -53,14 +53,17 @@ LOG_FORMAT = (
|
||||||
|
|
||||||
DATE_FORMAT = '%b %d %H:%M:%S'
|
DATE_FORMAT = '%b %d %H:%M:%S'
|
||||||
|
|
||||||
# FYI, ERROR is 40
|
LEVELS: dict[str, int] = {
|
||||||
CUSTOM_LEVELS: dict[str, int] = {
|
|
||||||
'TRANSPORT': 5,
|
'TRANSPORT': 5,
|
||||||
'RUNTIME': 15,
|
'RUNTIME': 15,
|
||||||
'CANCEL': 16,
|
'CANCEL': 16,
|
||||||
'DEVX': 17,
|
'DEVX': 400,
|
||||||
'PDB': 500,
|
'PDB': 500,
|
||||||
}
|
}
|
||||||
|
# _custom_levels: set[str] = {
|
||||||
|
# lvlname.lower for lvlname in LEVELS.keys()
|
||||||
|
# }
|
||||||
|
|
||||||
STD_PALETTE = {
|
STD_PALETTE = {
|
||||||
'CRITICAL': 'red',
|
'CRITICAL': 'red',
|
||||||
'ERROR': 'red',
|
'ERROR': 'red',
|
||||||
|
@ -134,7 +137,7 @@ class StackLevelAdapter(LoggerAdapter):
|
||||||
"Developer experience" sub-sys statuses.
|
"Developer experience" sub-sys statuses.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
return self.log(17, msg)
|
return self.log(400, msg)
|
||||||
|
|
||||||
def log(
|
def log(
|
||||||
self,
|
self,
|
||||||
|
@ -151,7 +154,8 @@ class StackLevelAdapter(LoggerAdapter):
|
||||||
if self.isEnabledFor(level):
|
if self.isEnabledFor(level):
|
||||||
stacklevel: int = 3
|
stacklevel: int = 3
|
||||||
if (
|
if (
|
||||||
level in CUSTOM_LEVELS.values()
|
level in LEVELS.values()
|
||||||
|
# or level in _custom_levels
|
||||||
):
|
):
|
||||||
stacklevel: int = 4
|
stacklevel: int = 4
|
||||||
|
|
||||||
|
@ -198,8 +202,7 @@ class StackLevelAdapter(LoggerAdapter):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO IDEAs:
|
# TODO IDEA:
|
||||||
# -[ ] move to `.devx.pformat`?
|
|
||||||
# -[ ] do per task-name and actor-name color coding
|
# -[ ] do per task-name and actor-name color coding
|
||||||
# -[ ] unique color per task-id and actor-uuid
|
# -[ ] unique color per task-id and actor-uuid
|
||||||
def pformat_task_uid(
|
def pformat_task_uid(
|
||||||
|
@ -306,7 +309,7 @@ def get_logger(
|
||||||
logger = StackLevelAdapter(log, ActorContextInfo())
|
logger = StackLevelAdapter(log, ActorContextInfo())
|
||||||
|
|
||||||
# additional levels
|
# additional levels
|
||||||
for name, val in CUSTOM_LEVELS.items():
|
for name, val in LEVELS.items():
|
||||||
logging.addLevelName(val, name)
|
logging.addLevelName(val, name)
|
||||||
|
|
||||||
# ensure customs levels exist as methods
|
# ensure customs levels exist as methods
|
||||||
|
@ -374,7 +377,7 @@ def at_least_level(
|
||||||
|
|
||||||
'''
|
'''
|
||||||
if isinstance(level, str):
|
if isinstance(level, str):
|
||||||
level: int = CUSTOM_LEVELS[level.upper()]
|
level: int = LEVELS[level.upper()]
|
||||||
|
|
||||||
if log.getEffectiveLevel() <= level:
|
if log.getEffectiveLevel() <= level:
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -162,10 +162,7 @@ class MsgDec(Struct):
|
||||||
# TODO: would get moved into `FieldSpec.__str__()` right?
|
# TODO: would get moved into `FieldSpec.__str__()` right?
|
||||||
@property
|
@property
|
||||||
def spec_str(self) -> str:
|
def spec_str(self) -> str:
|
||||||
return pformat_msgspec(
|
return pformat_msgspec(codec=self)
|
||||||
codec=self,
|
|
||||||
join_char='|',
|
|
||||||
)
|
|
||||||
|
|
||||||
pld_spec_str = spec_str
|
pld_spec_str = spec_str
|
||||||
|
|
||||||
|
@ -214,7 +211,7 @@ def mk_msgspec_table(
|
||||||
msgtypes = [msgspec]
|
msgtypes = [msgspec]
|
||||||
|
|
||||||
msgt_table: dict[str, MsgType] = {
|
msgt_table: dict[str, MsgType] = {
|
||||||
msgt: str(msgt.__name__)
|
msgt: str(msgt)
|
||||||
for msgt in msgtypes
|
for msgt in msgtypes
|
||||||
}
|
}
|
||||||
if msg:
|
if msg:
|
||||||
|
|
Loading…
Reference in New Issue