forked from goodboy/tractor
Kick off `.devx` subpkg for our dev tools B)
Where `.devx` is "developer experience", a hopefully broad enough subpkg
name for all the slick stuff planned to augment working on the actor
runtime 💥
Move the `._debug` module into the new subpkg and adjust rest of core
code base to reflect import path change. Also add a new
`.devx._debug.open_crash_handler()` manager for wrapping any sync code
outside a `trio.run()` which is handy for eventual CLI addons for
popular frameworks like `click`/`typer`.
multihomed
parent
3d0e95513c
commit
fa9a9cfb1d
|
@ -45,7 +45,7 @@ from ._exceptions import (
|
||||||
ModuleNotExposed,
|
ModuleNotExposed,
|
||||||
ContextCancelled,
|
ContextCancelled,
|
||||||
)
|
)
|
||||||
from ._debug import (
|
from .devx import (
|
||||||
breakpoint,
|
breakpoint,
|
||||||
pause,
|
pause,
|
||||||
pause_from_sync,
|
pause_from_sync,
|
||||||
|
|
|
@ -222,7 +222,7 @@ class Context:
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._cancel_called:
|
if self._cancel_called:
|
||||||
# from ._debug import breakpoint
|
# from .devx._debug import breakpoint
|
||||||
# await breakpoint()
|
# await breakpoint()
|
||||||
|
|
||||||
# this is an expected cancel request response message
|
# this is an expected cancel request response message
|
||||||
|
@ -247,7 +247,7 @@ class Context:
|
||||||
self._scope.cancel()
|
self._scope.cancel()
|
||||||
|
|
||||||
# NOTE: this usage actually works here B)
|
# NOTE: this usage actually works here B)
|
||||||
# from ._debug import breakpoint
|
# from .devx._debug import breakpoint
|
||||||
# await breakpoint()
|
# await breakpoint()
|
||||||
|
|
||||||
# XXX: this will break early callee results sending
|
# XXX: this will break early callee results sending
|
||||||
|
@ -277,7 +277,7 @@ class Context:
|
||||||
log.cancel(f'Cancelling {side} side of context to {self.chan.uid}')
|
log.cancel(f'Cancelling {side} side of context to {self.chan.uid}')
|
||||||
|
|
||||||
self._cancel_called = True
|
self._cancel_called = True
|
||||||
# await _debug.breakpoint()
|
# await devx._debug.breakpoint()
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
if side == 'caller':
|
if side == 'caller':
|
||||||
|
|
|
@ -482,7 +482,7 @@ class Portal:
|
||||||
# were initiated by *this* side's task.
|
# were initiated by *this* side's task.
|
||||||
if not ctx._cancel_called:
|
if not ctx._cancel_called:
|
||||||
# XXX: this should NEVER happen!
|
# XXX: this should NEVER happen!
|
||||||
# from ._debug import breakpoint
|
# from .devx._debug import breakpoint
|
||||||
# await breakpoint()
|
# await breakpoint()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -564,7 +564,7 @@ class Portal:
|
||||||
# a "stop" msg for a stream), this can result in a deadlock
|
# a "stop" msg for a stream), this can result in a deadlock
|
||||||
# where the root is waiting on the lock to clear but the
|
# where the root is waiting on the lock to clear but the
|
||||||
# child has already cleared it and clobbered IPC.
|
# child has already cleared it and clobbered IPC.
|
||||||
from ._debug import maybe_wait_for_debugger
|
from .devx._debug import maybe_wait_for_debugger
|
||||||
await maybe_wait_for_debugger()
|
await maybe_wait_for_debugger()
|
||||||
|
|
||||||
# remove the context from runtime tracking
|
# remove the context from runtime tracking
|
||||||
|
|
|
@ -37,7 +37,7 @@ from ._runtime import (
|
||||||
Arbiter,
|
Arbiter,
|
||||||
async_main,
|
async_main,
|
||||||
)
|
)
|
||||||
from . import _debug
|
from .devx import _debug
|
||||||
from . import _spawn
|
from . import _spawn
|
||||||
from . import _state
|
from . import _state
|
||||||
from . import log
|
from . import log
|
||||||
|
@ -89,7 +89,7 @@ async def open_root_actor(
|
||||||
# https://github.com/python-trio/trio/issues/1155#issuecomment-742964018
|
# https://github.com/python-trio/trio/issues/1155#issuecomment-742964018
|
||||||
builtin_bp_handler = sys.breakpointhook
|
builtin_bp_handler = sys.breakpointhook
|
||||||
orig_bp_path: str | None = os.environ.get('PYTHONBREAKPOINT', None)
|
orig_bp_path: str | None = os.environ.get('PYTHONBREAKPOINT', None)
|
||||||
os.environ['PYTHONBREAKPOINT'] = 'tractor._debug.pause_from_sync'
|
os.environ['PYTHONBREAKPOINT'] = 'tractor.devx._debug.pause_from_sync'
|
||||||
|
|
||||||
# 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.
|
||||||
|
@ -137,7 +137,7 @@ async def open_root_actor(
|
||||||
|
|
||||||
# expose internal debug module to every actor allowing
|
# expose internal debug module to every actor allowing
|
||||||
# for use of ``await tractor.breakpoint()``
|
# for use of ``await tractor.breakpoint()``
|
||||||
enable_modules.append('tractor._debug')
|
enable_modules.append('tractor.devx._debug')
|
||||||
|
|
||||||
# if debug mode get's enabled *at least* use that level of
|
# if debug mode get's enabled *at least* use that level of
|
||||||
# logging for some informative console prompts.
|
# logging for some informative console prompts.
|
||||||
|
|
|
@ -58,7 +58,7 @@ from ._exceptions import (
|
||||||
ContextCancelled,
|
ContextCancelled,
|
||||||
TransportClosed,
|
TransportClosed,
|
||||||
)
|
)
|
||||||
from . import _debug
|
from .devx import _debug
|
||||||
from ._discovery import get_registry
|
from ._discovery import get_registry
|
||||||
from ._portal import Portal
|
from ._portal import Portal
|
||||||
from . import _state
|
from . import _state
|
||||||
|
@ -264,7 +264,6 @@ async def _invoke(
|
||||||
cs: trio.CancelScope = ctx._scope
|
cs: trio.CancelScope = ctx._scope
|
||||||
if cs.cancel_called:
|
if cs.cancel_called:
|
||||||
canceller = ctx._cancelled_remote
|
canceller = ctx._cancelled_remote
|
||||||
# await _debug.breakpoint()
|
|
||||||
|
|
||||||
# NOTE / TODO: if we end up having
|
# NOTE / TODO: if we end up having
|
||||||
# ``Actor._cancel_task()`` call
|
# ``Actor._cancel_task()`` call
|
||||||
|
@ -536,7 +535,7 @@ class Actor:
|
||||||
self._parent_main_data = _mp_fixup_main._mp_figure_out_main()
|
self._parent_main_data = _mp_fixup_main._mp_figure_out_main()
|
||||||
|
|
||||||
# always include debugging tools module
|
# always include debugging tools module
|
||||||
enable_modules.append('tractor._debug')
|
enable_modules.append('tractor.devx._debug')
|
||||||
|
|
||||||
self.enable_modules: dict[str, str] = {}
|
self.enable_modules: dict[str, str] = {}
|
||||||
for name in enable_modules:
|
for name in enable_modules:
|
||||||
|
|
|
@ -35,7 +35,7 @@ from exceptiongroup import BaseExceptionGroup
|
||||||
import trio
|
import trio
|
||||||
from trio_typing import TaskStatus
|
from trio_typing import TaskStatus
|
||||||
|
|
||||||
from ._debug import (
|
from .devx._debug import (
|
||||||
maybe_wait_for_debugger,
|
maybe_wait_for_debugger,
|
||||||
acquire_debug_lock,
|
acquire_debug_lock,
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import warnings
|
||||||
from exceptiongroup import BaseExceptionGroup
|
from exceptiongroup import BaseExceptionGroup
|
||||||
import trio
|
import trio
|
||||||
|
|
||||||
from ._debug import maybe_wait_for_debugger
|
from .devx._debug import maybe_wait_for_debugger
|
||||||
from ._state import current_actor, is_main_process
|
from ._state import current_actor, is_main_process
|
||||||
from .log import get_logger, get_loglevel
|
from .log import get_logger, get_loglevel
|
||||||
from ._runtime import Actor
|
from ._runtime import Actor
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# tractor: structured concurrent "actors".
|
||||||
|
# Copyright 2018-eternity Tyler Goodlet.
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Runtime "developer experience" utils and addons to aid our
|
||||||
|
(advanced) users and core devs in building distributed applications
|
||||||
|
and working with/on the actor runtime.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from ._debug import (
|
||||||
|
maybe_wait_for_debugger,
|
||||||
|
acquire_debug_lock,
|
||||||
|
breakpoint,
|
||||||
|
pause,
|
||||||
|
pause_from_sync,
|
||||||
|
shield_sigint_handler,
|
||||||
|
MultiActorPdb,
|
||||||
|
open_crash_handler,
|
||||||
|
post_mortem,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'maybe_wait_for_debugger',
|
||||||
|
'acquire_debug_lock',
|
||||||
|
'breakpoint',
|
||||||
|
'pause',
|
||||||
|
'pause_from_sync',
|
||||||
|
'shield_sigint_handler',
|
||||||
|
'MultiActorPdb',
|
||||||
|
'open_crash_handler',
|
||||||
|
'post_mortem',
|
||||||
|
]
|
|
@ -28,6 +28,7 @@ from functools import (
|
||||||
cached_property,
|
cached_property,
|
||||||
)
|
)
|
||||||
from contextlib import asynccontextmanager as acm
|
from contextlib import asynccontextmanager as acm
|
||||||
|
from contextlib import contextmanager as cm
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
|
@ -44,22 +45,25 @@ from trio_typing import (
|
||||||
# Task,
|
# Task,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .log import get_logger
|
from ..log import get_logger
|
||||||
from ._discovery import get_root
|
from .._discovery import get_root
|
||||||
from ._state import (
|
from .._state import (
|
||||||
is_root_process,
|
is_root_process,
|
||||||
debug_mode,
|
debug_mode,
|
||||||
)
|
)
|
||||||
from ._exceptions import (
|
from .._exceptions import (
|
||||||
is_multi_cancelled,
|
is_multi_cancelled,
|
||||||
ContextCancelled,
|
ContextCancelled,
|
||||||
)
|
)
|
||||||
from ._ipc import Channel
|
from .._ipc import Channel
|
||||||
|
|
||||||
log = get_logger(__name__)
|
log = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['breakpoint', 'post_mortem']
|
__all__ = [
|
||||||
|
'breakpoint',
|
||||||
|
'post_mortem',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Lock:
|
class Lock:
|
||||||
|
@ -390,7 +394,7 @@ async def wait_for_parent_stdin_hijack(
|
||||||
# this syncs to child's ``Context.started()`` call.
|
# this syncs to child's ``Context.started()`` call.
|
||||||
async with portal.open_context(
|
async with portal.open_context(
|
||||||
|
|
||||||
tractor._debug.lock_tty_for_child,
|
lock_tty_for_child,
|
||||||
subactor_uid=actor_uid,
|
subactor_uid=actor_uid,
|
||||||
|
|
||||||
) as (ctx, val):
|
) as (ctx, val):
|
||||||
|
@ -855,7 +859,7 @@ pause = partial(
|
||||||
_pause,
|
_pause,
|
||||||
_set_trace,
|
_set_trace,
|
||||||
)
|
)
|
||||||
pp = pause # short-hand for "pause point"
|
# pp = pause # short-hand for "pause point"
|
||||||
|
|
||||||
|
|
||||||
async def breakpoint(**kwargs):
|
async def breakpoint(**kwargs):
|
||||||
|
@ -1008,3 +1012,32 @@ async def maybe_wait_for_debugger(
|
||||||
log.debug(
|
log.debug(
|
||||||
'Root acquired TTY LOCK'
|
'Root acquired TTY LOCK'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: better naming and what additionals?
|
||||||
|
# - optional runtime plugging?
|
||||||
|
# - detection for sync vs. async code?
|
||||||
|
# - specialized REPL entry when in distributed mode?
|
||||||
|
@cm
|
||||||
|
def open_crash_handler(
|
||||||
|
catch: set[BaseException] = {
|
||||||
|
Exception,
|
||||||
|
BaseException,
|
||||||
|
}
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
Generic "post mortem" crash handler using `pdbp` REPL debugger.
|
||||||
|
|
||||||
|
We expose this as a CLI framework addon to both `click` and
|
||||||
|
`typer` users so they can quickly wrap cmd endpoints which get
|
||||||
|
automatically wrapped to use the runtime's `debug_mode: bool`
|
||||||
|
AND `pdbp.pm()` around any code that is PRE-runtime entry
|
||||||
|
- any sync code which runs BEFORE the main call to
|
||||||
|
`trio.run()`.
|
||||||
|
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except tuple(catch):
|
||||||
|
pdbp.xpm()
|
||||||
|
raise
|
Loading…
Reference in New Issue