Compare commits

..

No commits in common. "e4ec6b7b0c3401b336e03a4ea7b42015480b7677" and "3538ccd7992282a1b81e3560282a4e03b9e667d2" have entirely different histories.

9 changed files with 136 additions and 288 deletions

View File

@ -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():
...

View File

@ -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'

View File

@ -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

View File

@ -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!"

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: