forked from goodboy/tractor
				
			Fix frame-selection display on first REPL entry
For whatever reason pdb(p), and in general, will show the frame of the *next* python instruction/LOC on initial entry (at least using `.set_trace()`), as such remove the `try/finally` block in the sync code entrypoint `.pause_from_sync()`, and also since doesn't seem like we really need it anyway. Further, and to this end: - enable hidden frames support in our default config. - fix/drop/mask all the frame ref-ing/mangling we had prior since it's no longer needed as well as manual `Lock` releasing which seems to work already by having the `greenback` spawned task do it's normal thing? - move to no `Union` type annots. - hide all frames that can add "this is the runtime confusion" to traces.remotes/1757153874605917753/main
							parent
							
								
									1deed8dbee
								
							
						
					
					
						commit
						ab8bd9b787
					
				|  | @ -30,7 +30,6 @@ from functools import ( | |||
| from contextlib import asynccontextmanager as acm | ||||
| from typing import ( | ||||
|     Any, | ||||
|     Optional, | ||||
|     Callable, | ||||
|     AsyncIterator, | ||||
|     AsyncGenerator, | ||||
|  | @ -40,7 +39,10 @@ from types import FrameType | |||
| import pdbp | ||||
| import tractor | ||||
| import trio | ||||
| from trio_typing import TaskStatus | ||||
| from trio_typing import ( | ||||
|     TaskStatus, | ||||
|     # Task, | ||||
| ) | ||||
| 
 | ||||
| from .log import get_logger | ||||
| from ._discovery import get_root | ||||
|  | @ -69,10 +71,10 @@ class Lock: | |||
|     ''' | ||||
|     repl: MultiActorPdb | None = None | ||||
|     # placeholder for function to set a ``trio.Event`` on debugger exit | ||||
|     # pdb_release_hook: Optional[Callable] = None | ||||
|     # pdb_release_hook: Callable | None = None | ||||
| 
 | ||||
|     _trio_handler: Callable[ | ||||
|         [int, Optional[FrameType]], Any | ||||
|         [int, FrameType | None], Any | ||||
|     ] | int | None = None | ||||
| 
 | ||||
|     # actor-wide variable pointing to current task name using debugger | ||||
|  | @ -83,23 +85,23 @@ class Lock: | |||
|     # and must be cancelled if this actor is cancelled via IPC | ||||
|     # request-message otherwise deadlocks with the parent actor may | ||||
|     # ensure | ||||
|     _debugger_request_cs: Optional[trio.CancelScope] = None | ||||
|     _debugger_request_cs: trio.CancelScope | None = None | ||||
| 
 | ||||
|     # NOTE: set only in the root actor for the **local** root spawned task | ||||
|     # which has acquired the lock (i.e. this is on the callee side of | ||||
|     # the `lock_tty_for_child()` context entry). | ||||
|     _root_local_task_cs_in_debug: Optional[trio.CancelScope] = None | ||||
|     _root_local_task_cs_in_debug: trio.CancelScope | None = None | ||||
| 
 | ||||
|     # actor tree-wide actor uid that supposedly has the tty lock | ||||
|     global_actor_in_debug: Optional[tuple[str, str]] = None | ||||
|     global_actor_in_debug: tuple[str, str] = None | ||||
| 
 | ||||
|     local_pdb_complete: Optional[trio.Event] = None | ||||
|     no_remote_has_tty: Optional[trio.Event] = None | ||||
|     local_pdb_complete: trio.Event | None = None | ||||
|     no_remote_has_tty: trio.Event | None = None | ||||
| 
 | ||||
|     # lock in root actor preventing multi-access to local tty | ||||
|     _debug_lock: trio.StrictFIFOLock = trio.StrictFIFOLock() | ||||
| 
 | ||||
|     _orig_sigint_handler: Optional[Callable] = None | ||||
|     _orig_sigint_handler: Callable | None = None | ||||
|     _blocked: set[tuple[str, str]] = set() | ||||
| 
 | ||||
|     @classmethod | ||||
|  | @ -110,6 +112,7 @@ class Lock: | |||
|         ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     @pdbp.hideframe  # XXX NOTE XXX see below in `.pause_from_sync()` | ||||
|     def unshield_sigint(cls): | ||||
|         # always restore ``trio``'s sigint handler. see notes below in | ||||
|         # the pdb factory about the nightmare that is that code swapping | ||||
|  | @ -129,10 +132,6 @@ class Lock: | |||
|             if owner: | ||||
|                 raise | ||||
| 
 | ||||
|         # actor-local state, irrelevant for non-root. | ||||
|         cls.global_actor_in_debug = None | ||||
|         cls.local_task_in_debug = None | ||||
| 
 | ||||
|         try: | ||||
|             # sometimes the ``trio`` might already be terminated in | ||||
|             # which case this call will raise. | ||||
|  | @ -143,6 +142,11 @@ class Lock: | |||
|             cls.unshield_sigint() | ||||
|             cls.repl = None | ||||
| 
 | ||||
|             # actor-local state, irrelevant for non-root. | ||||
|             cls.global_actor_in_debug = None | ||||
|             cls.local_task_in_debug = None | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class TractorConfig(pdbp.DefaultConfig): | ||||
|     ''' | ||||
|  | @ -151,7 +155,7 @@ class TractorConfig(pdbp.DefaultConfig): | |||
|     ''' | ||||
|     use_pygments: bool = True | ||||
|     sticky_by_default: bool = False | ||||
|     enable_hidden_frames: bool = False | ||||
|     enable_hidden_frames: bool = True | ||||
| 
 | ||||
|     # much thanks @mdmintz for the hot tip! | ||||
|     # fixes line spacing issue when resizing terminal B) | ||||
|  | @ -228,26 +232,23 @@ async def _acquire_debug_lock_from_root_task( | |||
|     to the ``pdb`` repl. | ||||
| 
 | ||||
|     ''' | ||||
|     task_name = trio.lowlevel.current_task().name | ||||
|     task_name: str = trio.lowlevel.current_task().name | ||||
|     we_acquired: bool = False | ||||
| 
 | ||||
|     log.runtime( | ||||
|         f"Attempting to acquire TTY lock, remote task: {task_name}:{uid}" | ||||
|     ) | ||||
| 
 | ||||
|     we_acquired = False | ||||
| 
 | ||||
|     try: | ||||
|         log.runtime( | ||||
|             f"entering lock checkpoint, remote task: {task_name}:{uid}" | ||||
|         ) | ||||
|         we_acquired = True | ||||
| 
 | ||||
|         # NOTE: if the surrounding cancel scope from the | ||||
|         # `lock_tty_for_child()` caller is cancelled, this line should | ||||
|         # unblock and NOT leave us in some kind of | ||||
|         # a "child-locked-TTY-but-child-is-uncontactable-over-IPC" | ||||
|         # condition. | ||||
|         await Lock._debug_lock.acquire() | ||||
|         we_acquired = True | ||||
| 
 | ||||
|         if Lock.no_remote_has_tty is None: | ||||
|             # mark the tty lock as being in use so that the runtime | ||||
|  | @ -573,13 +574,15 @@ async def _pause( | |||
|     try: | ||||
|         # breakpoint() | ||||
|         if debug_func is None: | ||||
|             assert release_lock_signal, ( | ||||
|                 'Must pass `release_lock_signal: trio.Event` if no ' | ||||
|                 'trace func provided!' | ||||
|             ) | ||||
|             # assert release_lock_signal, ( | ||||
|             #     'Must pass `release_lock_signal: trio.Event` if no ' | ||||
|             #     'trace func provided!' | ||||
|             # ) | ||||
|             print(f"{actor.uid} ENTERING WAIT") | ||||
|             task_status.started() | ||||
|             await release_lock_signal.wait() | ||||
| 
 | ||||
|             # with trio.CancelScope(shield=True): | ||||
|             #     await release_lock_signal.wait() | ||||
| 
 | ||||
|         else: | ||||
|             # block here one (at the appropriate frame *up*) where | ||||
|  | @ -606,7 +609,7 @@ async def _pause( | |||
| def shield_sigint_handler( | ||||
|     signum: int, | ||||
|     frame: 'frame',  # type: ignore # noqa | ||||
|     # pdb_obj: Optional[MultiActorPdb] = None, | ||||
|     # pdb_obj: MultiActorPdb | None = None, | ||||
|     *args, | ||||
| 
 | ||||
| ) -> None: | ||||
|  | @ -620,7 +623,7 @@ def shield_sigint_handler( | |||
|     ''' | ||||
|     __tracebackhide__ = True | ||||
| 
 | ||||
|     uid_in_debug = Lock.global_actor_in_debug | ||||
|     uid_in_debug: tuple[str, str] | None = Lock.global_actor_in_debug | ||||
| 
 | ||||
|     actor = tractor.current_actor() | ||||
|     # print(f'{actor.uid} in HANDLER with ') | ||||
|  | @ -638,14 +641,14 @@ def shield_sigint_handler( | |||
|         else: | ||||
|             raise KeyboardInterrupt | ||||
| 
 | ||||
|     any_connected = False | ||||
|     any_connected: bool = False | ||||
| 
 | ||||
|     if uid_in_debug is not None: | ||||
|         # try to see if the supposed (sub)actor in debug still | ||||
|         # has an active connection to *this* actor, and if not | ||||
|         # it's likely they aren't using the TTY lock / debugger | ||||
|         # and we should propagate SIGINT normally. | ||||
|         chans = actor._peers.get(tuple(uid_in_debug)) | ||||
|         chans: list[tractor.Channel] = actor._peers.get(tuple(uid_in_debug)) | ||||
|         if chans: | ||||
|             any_connected = any(chan.connected() for chan in chans) | ||||
|             if not any_connected: | ||||
|  | @ -658,7 +661,7 @@ def shield_sigint_handler( | |||
|                 return do_cancel() | ||||
| 
 | ||||
|     # only set in the actor actually running the REPL | ||||
|     pdb_obj = Lock.repl | ||||
|     pdb_obj: MultiActorPdb | None = Lock.repl | ||||
| 
 | ||||
|     # root actor branch that reports whether or not a child | ||||
|     # has locked debugger. | ||||
|  | @ -716,7 +719,7 @@ def shield_sigint_handler( | |||
|             ) | ||||
|             return do_cancel() | ||||
| 
 | ||||
|         task = Lock.local_task_in_debug | ||||
|         task: str | None = Lock.local_task_in_debug | ||||
|         if ( | ||||
|             task | ||||
|             and pdb_obj | ||||
|  | @ -791,15 +794,18 @@ def _set_trace( | |||
|         Lock.local_task_in_debug = 'sync' | ||||
| 
 | ||||
|     pdb.set_trace(frame=frame) | ||||
|     # undo_ | ||||
| 
 | ||||
| 
 | ||||
| # TODO: allow pausing from sync code, normally by remapping | ||||
| # python's builtin breakpoint() hook to this runtime aware version. | ||||
| def pause_from_sync() -> None: | ||||
|     print("ENTER SYNC PAUSE") | ||||
|     import greenback | ||||
|     __tracebackhide__ = True | ||||
| 
 | ||||
|     actor: tractor.Actor = tractor.current_actor() | ||||
|     task_can_release_tty_lock = trio.Event() | ||||
|     # task_can_release_tty_lock = trio.Event() | ||||
| 
 | ||||
|     # spawn bg task which will lock out the TTY, we poll | ||||
|     # just below until the release event is reporting that task as | ||||
|  | @ -808,34 +814,39 @@ def pause_from_sync() -> None: | |||
|         actor._service_n.start(partial( | ||||
|             _pause, | ||||
|             debug_func=None, | ||||
|             release_lock_signal=task_can_release_tty_lock, | ||||
|             # release_lock_signal=task_can_release_tty_lock, | ||||
|         )) | ||||
|     ) | ||||
|     print("ENTER SYNC PAUSE") | ||||
|     pdb, undo_sigint = mk_mpdb() | ||||
|     try: | ||||
|         print("ENTER SYNC PAUSE") | ||||
|         # _set_trace(actor=actor) | ||||
| 
 | ||||
|         # we entered the global ``breakpoint()`` built-in from sync | ||||
|         # code? | ||||
|         Lock.local_task_in_debug = 'sync' | ||||
|         frame: FrameType | None = sys._getframe() | ||||
|         print(f'FRAME: {str(frame)}') | ||||
|     db, undo_sigint = mk_mpdb() | ||||
|     Lock.local_task_in_debug = 'sync' | ||||
|     # db.config.enable_hidden_frames = True | ||||
| 
 | ||||
|         frame: FrameType = frame.f_back  # type: ignore | ||||
|         print(f'FRAME: {str(frame)}') | ||||
|     # we entered the global ``breakpoint()`` built-in from sync | ||||
|     # code? | ||||
|     frame: FrameType | None = sys._getframe() | ||||
|     # print(f'FRAME: {str(frame)}') | ||||
|     # assert not db._is_hidden(frame) | ||||
| 
 | ||||
|         frame: FrameType = frame.f_back  # type: ignore | ||||
|         print(f'FRAME: {str(frame)}') | ||||
|     frame: FrameType = frame.f_back  # type: ignore | ||||
|     # print(f'FRAME: {str(frame)}') | ||||
|     # if not db._is_hidden(frame): | ||||
|     #     pdbp.set_trace() | ||||
|     # db._hidden_frames.append( | ||||
|     #     (frame, frame.f_lineno) | ||||
|     # ) | ||||
|     db.set_trace(frame=frame) | ||||
|     # NOTE XXX: see the `@pdbp.hideframe` decoration | ||||
|     # on `Lock.unshield_sigint()`.. I have NO CLUE why | ||||
|     # the next instruction's def frame is being shown | ||||
|     # in the tb but it seems to be something wonky with | ||||
|     # the way `pdb` core works? | ||||
|     # undo_sigint() | ||||
| 
 | ||||
|         pdb.set_trace(frame=frame) | ||||
|         # pdb.do_frame( | ||||
|         #     pdb.curindex | ||||
|     # Lock.global_actor_in_debug = actor.uid | ||||
|     # Lock.release() | ||||
|     # task_can_release_tty_lock.set() | ||||
| 
 | ||||
|     finally: | ||||
|         task_can_release_tty_lock.set() | ||||
|         undo_sigint() | ||||
| 
 | ||||
| # using the "pause" semantics instead since | ||||
| # that better covers actually somewhat "pausing the runtime" | ||||
|  | @ -959,8 +970,7 @@ async def maybe_wait_for_debugger( | |||
|         # will make the pdb repl unusable. | ||||
|         # Instead try to wait for pdb to be released before | ||||
|         # tearing down. | ||||
| 
 | ||||
|         sub_in_debug = None | ||||
|         sub_in_debug: tuple[str, str] | None = None | ||||
| 
 | ||||
|         for _ in range(poll_steps): | ||||
| 
 | ||||
|  | @ -980,13 +990,15 @@ async def maybe_wait_for_debugger( | |||
| 
 | ||||
|                 debug_complete = Lock.no_remote_has_tty | ||||
|                 if ( | ||||
|                     (debug_complete and | ||||
|                      not debug_complete.is_set()) | ||||
|                     debug_complete | ||||
|                     and sub_in_debug is not None | ||||
|                     and not debug_complete.is_set() | ||||
|                 ): | ||||
|                     log.debug( | ||||
|                     log.pdb( | ||||
|                         'Root has errored but pdb is in use by ' | ||||
|                         f'child {sub_in_debug}\n' | ||||
|                         'Waiting on tty lock to release..') | ||||
|                         'Waiting on tty lock to release..' | ||||
|                     ) | ||||
| 
 | ||||
|                     await debug_complete.wait() | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue