Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

protect IPython from bad custom exception handlers

Previously, errors in custom handlers would result in the custom exception
handler's error being printed in lieu of the real exception, and certain cases could cause infinite loops.

Now, if CustomTB fails it is unregistered immediately, and the original TB is also displayed.

IPython's own BdbQuit_IPython_excepthook had an invalid signature, which revealed this issue, and has also been fixed.

test included.

closes #692
  • Loading branch information...
commit ed5078c9955c3b20c92751b2d2beb0ea8006a3c3 1 parent 64e90b1
@minrk authored
View
2  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.'
View
22 IPython/core/interactiveshell.py
@@ -1478,16 +1478,30 @@ def my_handler(self, etype, value, tb, tb_offset=None)
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):
View
18 IPython/core/tests/test_interactiveshell.py
@@ -146,3 +146,21 @@ def test_future_unicode(self):
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
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.