Opening an IPython shell on any (uncaught) exception [duplicate]
Asked Answered
H

2

2

I have defined the following (embedded) shell in Python:

from IPython.config.loader import Config
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\\#>: '
prompt_config.in2_template = '   .\\D.: '
prompt_config.out_template = 'N.Out<\\#>: '
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")
exit_msg = '**Leaving Nested interpreter'

from IPython.frontend.terminal.embed import InteractiveShellEmbed
ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

I would like my program to automatically drop me into that shell whenever there is an exception, in the scope where the exception occurs.

I have tried with:

def excepthook(type, value, traceback):
    ipshell(msg)

sys.excepthook = excepthook

But this doesn't seem to work (I just get the standard traceback). Also, ideally it would be best if :

  1. IPython prints the regular traceback
  2. and then drops me into an IPython shell

If the code I wrote above worked, it would merely drop me into an IPython shell, and not print the traceback.

Update 1:

I have read here that when running code from IPython, IPython is in charge of catching all exceptions, and that it may no be possible to directly override sys.excepthook(). If this is the case, how can I have IPython execute other exception hooks (e.g. my ipshell()) in the scope where the exception occurs and after it prints the traceback?

Update 2:

There seems to be a thread in the dev list discussing this: excepthook-like behavior in ipython. There is apparently a function set_custom_exc that takes care of this, but I am not sure how I could plug that in to my ipshell().

Hannahannah answered 1/4, 2013 at 21:44 Comment(2)
Just to make this clear: you want the embedded shell in the scope where the exception was first raised (in contrast to where it was first catched)?Messmate
Here's an answer to another question that accomplishes what you want, including preserving most of the context in which the exception occurs. Much more useful than PDB. https://mcmap.net/q/329762/-launch-an-ipython-shell-on-exceptionHoral
M
1

I think it's hardly possible to let the exception pass all the way through the call stack to find out whether or not someone would catch and handle it in the end, and then return to the scope where it was first raised. That original scope is gone, when the exception popped out of it.

Your best bet may be to wrap all the parts where you want your shell to come up to handle an exception with a dedicated try ... except block, as in the following:

try: 
   pass # stuff that may raise an exception
except KeyboardInterrupt:
   # some exception you want to handle
   raise 
except SystemExit:
   # another exception you want to handle
   raise 
except: 
   # get a hand on any unhandled exception
   ipshell(traceback.format_exc())

(Not sure if this can be implemented more elegantly. I'd be very interested in a better solution as well!)

Messmate answered 2/4, 2013 at 11:17 Comment(2)
This is the best solution I've found: https://mcmap.net/q/329762/-launch-an-ipython-shell-on-exceptionHoral
Thanks for pointing this out. Although both are quite old, I'm voting to mark this as a duplicate to that thread, as it seems to provide more/better solutions...Messmate
H
1

Defining the following in my ipython shell seems to do what you want:

def custom_exc(shell, etype, evalue, tb, tb_offset=None):
    shell.showtraceback((etype, evalue, tb), tb_offset=tb_offset)
    ipshell()

get_ipython().set_custom_exc((Exception,), custom_exc)
Hardunn answered 2/4, 2013 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.