diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py index d7b22a3..5992b8e 100644 --- a/IPython/core/debugger.py +++ b/IPython/core/debugger.py @@ -64,7 +64,7 @@ def BdbQuit_excepthook(et,ev,tb): else: BdbQuit_excepthook.excepthook_ori(et,ev,tb) -def BdbQuit_IPython_excepthook(self,et,ev,tb): +def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None): print 'Exiting Debugger.' diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index ad8ebab..0a52cca 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -1478,16 +1478,30 @@ class InteractiveShell(SingletonConfigurable, Magic): assert type(exc_tuple)==type(()) , \ "The custom exceptions must be given AS A TUPLE." - def dummy_handler(self,etype,value,tb): + def dummy_handler(self,etype,value,tb,tb_offset=None): print '*** Simple custom exception handler ***' print 'Exception type :',etype print 'Exception value:',value print 'Traceback :',tb #print 'Source code :','\n'.join(self.buffer) - if handler is None: handler = dummy_handler - - self.CustomTB = types.MethodType(handler,self) + if handler is None: + wrapped = dummy_handler + else: + def wrapped(self,etype,value,tb,tb_offset=None): + try: + return handler(self,etype,value,tb,tb_offset=tb_offset) + except: + # clear custom handler immediately + self.set_custom_exc((), None) + print >> io.stderr, "Custom TB Handler failed, unregistering" + # show the exception in handler first + stb = self.InteractiveTB.structured_traceback(*sys.exc_info()) + print >> io.stdout, self.InteractiveTB.stb2text(stb) + print >> io.stdout, "The original exception:" + self.showtraceback((etype,value,tb), tb_offset=tb_offset) + + self.CustomTB = types.MethodType(wrapped,self) self.custom_exceptions = exc_tuple def excepthook(self, etype, value, tb): diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 1ed515a..9e7b848 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -146,3 +146,21 @@ class InteractiveShellTestCase(unittest.TestCase): finally: # Reset compiler flags so we don't mess up other tests. ip.compile.reset_compiler_flags() + + def test_bad_custom_tb(self): + """Check that InteractiveShell is protected from bad custom exception handlers""" + ip = get_ipython() + from IPython.utils import io + save_stderr = io.stderr + try: + # capture stderr + io.stderr = StringIO() + ip.set_custom_exc((IOError,),lambda etype,value,tb: None) + self.assertEquals(ip.custom_exceptions, (IOError,)) + ip.run_cell(u'raise IOError("foo")') + self.assertEquals(ip.custom_exceptions, ()) + self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue()) + finally: + io.stderr = save_stderr + +