Show More
@@ -1484,13 +1484,42 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||||
1484 | print 'Exception value:',value |
|
1484 | print 'Exception value:',value | |
1485 | print 'Traceback :',tb |
|
1485 | print 'Traceback :',tb | |
1486 | #print 'Source code :','\n'.join(self.buffer) |
|
1486 | #print 'Source code :','\n'.join(self.buffer) | |
|
1487 | ||||
|
1488 | def validate_stb(stb): | |||
|
1489 | """validate structured traceback return type | |||
|
1490 | ||||
|
1491 | return type of CustomTB *should* be a list of strings, but allow | |||
|
1492 | single strings or None, which are harmless. | |||
|
1493 | ||||
|
1494 | This function will *always* return a list of strings, | |||
|
1495 | and will raise a TypeError if stb is inappropriate. | |||
|
1496 | """ | |||
|
1497 | msg = "CustomTB must return list of strings, not %r" % stb | |||
|
1498 | if stb is None: | |||
|
1499 | return [] | |||
|
1500 | elif isinstance(stb, basestring): | |||
|
1501 | return [stb] | |||
|
1502 | elif not isinstance(stb, list): | |||
|
1503 | raise TypeError(msg) | |||
|
1504 | # it's a list | |||
|
1505 | for line in stb: | |||
|
1506 | # check every element | |||
|
1507 | if not isinstance(line, basestring): | |||
|
1508 | raise TypeError(msg) | |||
|
1509 | return stb | |||
1487 |
|
1510 | |||
1488 | if handler is None: |
|
1511 | if handler is None: | |
1489 | wrapped = dummy_handler |
|
1512 | wrapped = dummy_handler | |
1490 | else: |
|
1513 | else: | |
1491 | def wrapped(self,etype,value,tb,tb_offset=None): |
|
1514 | def wrapped(self,etype,value,tb,tb_offset=None): | |
|
1515 | """wrap CustomTB handler, to protect IPython from user code | |||
|
1516 | ||||
|
1517 | This makes it harder (but not impossible) for custom exception | |||
|
1518 | handlers to crash IPython. | |||
|
1519 | """ | |||
1492 | try: |
|
1520 | try: | |
1493 |
|
|
1521 | stb = handler(self,etype,value,tb,tb_offset=tb_offset) | |
|
1522 | return validate_stb(stb) | |||
1494 | except: |
|
1523 | except: | |
1495 | # clear custom handler immediately |
|
1524 | # clear custom handler immediately | |
1496 | self.set_custom_exc((), None) |
|
1525 | self.set_custom_exc((), None) | |
@@ -1499,7 +1528,10 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||||
1499 | stb = self.InteractiveTB.structured_traceback(*sys.exc_info()) |
|
1528 | stb = self.InteractiveTB.structured_traceback(*sys.exc_info()) | |
1500 | print >> io.stdout, self.InteractiveTB.stb2text(stb) |
|
1529 | print >> io.stdout, self.InteractiveTB.stb2text(stb) | |
1501 | print >> io.stdout, "The original exception:" |
|
1530 | print >> io.stdout, "The original exception:" | |
1502 | self.showtraceback((etype,value,tb), tb_offset=tb_offset) |
|
1531 | stb = self.InteractiveTB.structured_traceback( | |
|
1532 | (etype,value,tb), tb_offset=tb_offset | |||
|
1533 | ) | |||
|
1534 | return stb | |||
1503 |
|
1535 | |||
1504 | self.CustomTB = types.MethodType(wrapped,self) |
|
1536 | self.CustomTB = types.MethodType(wrapped,self) | |
1505 | self.custom_exceptions = exc_tuple |
|
1537 | self.custom_exceptions = exc_tuple | |
@@ -1570,11 +1602,7 b' class InteractiveShell(SingletonConfigurable, Magic):' | |||||
1570 | sys.last_value = value |
|
1602 | sys.last_value = value | |
1571 | sys.last_traceback = tb |
|
1603 | sys.last_traceback = tb | |
1572 | if etype in self.custom_exceptions: |
|
1604 | if etype in self.custom_exceptions: | |
1573 | # FIXME: Old custom traceback objects may just return a |
|
|||
1574 | # string, in that case we just put it into a list |
|
|||
1575 | stb = self.CustomTB(etype, value, tb, tb_offset) |
|
1605 | stb = self.CustomTB(etype, value, tb, tb_offset) | |
1576 | if isinstance(ctb, basestring): |
|
|||
1577 | stb = [stb] |
|
|||
1578 | else: |
|
1606 | else: | |
1579 | if exception_only: |
|
1607 | if exception_only: | |
1580 | stb = ['An exception has occurred, use %tb to see ' |
|
1608 | stb = ['An exception has occurred, use %tb to see ' |
@@ -155,7 +155,7 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
155 | try: |
|
155 | try: | |
156 | # capture stderr |
|
156 | # capture stderr | |
157 | io.stderr = StringIO() |
|
157 | io.stderr = StringIO() | |
158 |
ip.set_custom_exc((IOError,),lambda etype,value,tb: |
|
158 | ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0) | |
159 | self.assertEquals(ip.custom_exceptions, (IOError,)) |
|
159 | self.assertEquals(ip.custom_exceptions, (IOError,)) | |
160 | ip.run_cell(u'raise IOError("foo")') |
|
160 | ip.run_cell(u'raise IOError("foo")') | |
161 | self.assertEquals(ip.custom_exceptions, ()) |
|
161 | self.assertEquals(ip.custom_exceptions, ()) | |
@@ -163,4 +163,20 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
163 | finally: |
|
163 | finally: | |
164 | io.stderr = save_stderr |
|
164 | io.stderr = save_stderr | |
165 |
|
165 | |||
|
166 | def test_bad_custom_tb_return(self): | |||
|
167 | """Check that InteractiveShell is protected from bad return types in custom exception handlers""" | |||
|
168 | ip = get_ipython() | |||
|
169 | from IPython.utils import io | |||
|
170 | save_stderr = io.stderr | |||
|
171 | try: | |||
|
172 | # capture stderr | |||
|
173 | io.stderr = StringIO() | |||
|
174 | ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1) | |||
|
175 | self.assertEquals(ip.custom_exceptions, (NameError,)) | |||
|
176 | ip.run_cell(u'a=abracadabra') | |||
|
177 | self.assertEquals(ip.custom_exceptions, ()) | |||
|
178 | self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue()) | |||
|
179 | finally: | |||
|
180 | io.stderr = save_stderr | |||
|
181 | ||||
166 |
|
182 |
General Comments 0
You need to be logged in to leave comments.
Login now