Last active
June 18, 2021 17:39
-
-
Save djosix/545af4d7eb4ec0417c3a1dccecfc19b6 to your computer and use it in GitHub Desktop.
Pop IPython embed shell when any exception is raised (before ei)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import inspect | |
import traceback | |
import importlib | |
import readline | |
import contextlib | |
__all__ = [ | |
'probe', | |
'DebugHook', | |
'patch', | |
'unpatch', | |
'capture', | |
'embed' | |
] | |
assert importlib.util.find_spec('IPython') is not None, ( | |
'IPython is required for this module') | |
_original_excepthook = None | |
def probe(): | |
from IPython import embed | |
import IPython.core.ultratb | |
print('IPython debug hook is available!') | |
for name, value in IPython.core.ultratb.__dict__.items(): | |
if not isinstance(value, type): | |
continue | |
if value is IPython.core.ultratb.TBTools: | |
continue | |
if not issubclass(value, IPython.core.ultratb.TBTools): | |
continue | |
tb = value() | |
modes = getattr(tb, 'valid_modes', None) | |
colors = list(getattr(tb, 'color_scheme_table').keys()) \ | |
if hasattr(tb, 'color_scheme_table') else None | |
print(' class: {}'.format(name)) | |
print(' modes: {}'.format(modes)) | |
print(' colors: {}'.format(colors)) | |
class DebugHook: | |
def __init__(self, select=False, tb_class='AutoFormattedTB', tb_mode='Context', tb_color='Neutral'): | |
self.select = select | |
self.tb_class = tb_class | |
self.tb_mode = tb_mode | |
self.tb_color = tb_color | |
def __call__(self, exc_type, exc, tb): | |
from IPython import embed | |
import IPython.core.ultratb | |
TB = getattr(IPython.core.ultratb, self.tb_class) | |
tb_handler = TB(mode=self.tb_mode, color_scheme=self.tb_color) | |
tb_handler(exc_type, exc, tb) | |
frames = [t[0] for t in traceback.walk_tb(tb)][::-1] | |
while True: | |
print() | |
if self.select: | |
print('Select a stack frame to embed IPython shell (default: 0):') | |
for i, frame in enumerate(frames): | |
print('{}. {}'.format(i, frame)) | |
try: | |
s = input('> ').strip() | |
n = int(s) if s else 0 | |
frame = frames[n] | |
except (KeyboardInterrupt, EOFError): | |
break | |
except: | |
continue | |
else: | |
frame = frames[0] | |
print('Embedded into', frame) | |
user_module = inspect.getmodule(frame) | |
user_ns = frame.f_locals | |
user_ns.setdefault('etype', exc_type) | |
user_ns.setdefault('evalue', exc) | |
user_ns.setdefault('etb', tb) | |
embed(banner1='', user_module=user_module, user_ns=user_ns, colors='Neutral') | |
if not self.select: | |
break | |
def patch(**kwargs): | |
global _original_excepthook | |
_original_excepthook = sys.excepthook | |
sys.excepthook = DebugHook(**kwargs) | |
def unpatch(): | |
global _original_excepthook | |
assert _original_excepthook is not None, 'not patched' | |
sys.excepthook = _original_excepthook | |
_original_excepthook = None | |
@contextlib.contextmanager | |
def capture(**kwargs): | |
patch(**kwargs) | |
yield | |
unpatch() | |
def embed(message=None, exit=False): | |
from IPython import embed | |
e = sys.exc_info()[1] | |
if e is not None and message is None: | |
message = traceback.format_exc() | |
frame = inspect.stack()[1].frame | |
user_ns = frame.f_locals | |
user_module = inspect.getmodule(frame) | |
embed(header=message, user_ns=user_ns, user_module=user_module) | |
if exit: | |
sys.exit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Install
Example 1
Example 2
Example 3