mirror of
https://github.com/python/cpython.git
synced 2024-11-25 10:54:51 +08:00
db26f7c137
an 'except' clause), the exception __context__ would be reset to None. This crases the interpreter if this precisely happens inside PyErr_SetObject. - now the __context__ is properly preserved - in any case, PyErr_SetObject now saves the current exc_value in a local variable, to avoid such crashes in the future. Reviewer: Antoine Pitrou.
375 lines
9.8 KiB
Python
375 lines
9.8 KiB
Python
# Copyright 2007 Google, Inc. All Rights Reserved.
|
|
# Licensed to PSF under a Contributor Agreement.
|
|
|
|
"""Tests for the raise statement."""
|
|
|
|
from test import support
|
|
import sys
|
|
import types
|
|
import unittest
|
|
|
|
|
|
def get_tb():
|
|
try:
|
|
raise OSError()
|
|
except:
|
|
return sys.exc_info()[2]
|
|
|
|
|
|
class Context:
|
|
def __enter__(self):
|
|
return self
|
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
return True
|
|
|
|
|
|
class TestRaise(unittest.TestCase):
|
|
def test_invalid_reraise(self):
|
|
try:
|
|
raise
|
|
except RuntimeError as e:
|
|
self.failUnless("No active exception" in str(e))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_reraise(self):
|
|
try:
|
|
try:
|
|
raise IndexError()
|
|
except IndexError as e:
|
|
exc1 = e
|
|
raise
|
|
except IndexError as exc2:
|
|
self.failUnless(exc1 is exc2)
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_except_reraise(self):
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
try:
|
|
raise KeyError("caught")
|
|
except KeyError:
|
|
pass
|
|
raise
|
|
self.assertRaises(TypeError, reraise)
|
|
|
|
def test_finally_reraise(self):
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
try:
|
|
raise KeyError("caught")
|
|
finally:
|
|
raise
|
|
self.assertRaises(KeyError, reraise)
|
|
|
|
def test_nested_reraise(self):
|
|
def nested_reraise():
|
|
raise
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
nested_reraise()
|
|
self.assertRaises(TypeError, reraise)
|
|
|
|
def test_with_reraise1(self):
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
with Context():
|
|
pass
|
|
raise
|
|
self.assertRaises(TypeError, reraise)
|
|
|
|
def test_with_reraise2(self):
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
with Context():
|
|
raise KeyError("caught")
|
|
raise
|
|
self.assertRaises(TypeError, reraise)
|
|
|
|
def test_yield_reraise(self):
|
|
def reraise():
|
|
try:
|
|
raise TypeError("foo")
|
|
except:
|
|
yield 1
|
|
raise
|
|
g = reraise()
|
|
next(g)
|
|
self.assertRaises(TypeError, lambda: next(g))
|
|
self.assertRaises(StopIteration, lambda: next(g))
|
|
|
|
def test_erroneous_exception(self):
|
|
class MyException(Exception):
|
|
def __init__(self):
|
|
raise RuntimeError()
|
|
|
|
try:
|
|
raise MyException
|
|
except RuntimeError:
|
|
pass
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
|
|
class TestCause(unittest.TestCase):
|
|
def test_invalid_cause(self):
|
|
try:
|
|
raise IndexError from 5
|
|
except TypeError as e:
|
|
self.failUnless("exception cause" in str(e))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_class_cause(self):
|
|
try:
|
|
raise IndexError from KeyError
|
|
except IndexError as e:
|
|
self.failUnless(isinstance(e.__cause__, KeyError))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_instance_cause(self):
|
|
cause = KeyError()
|
|
try:
|
|
raise IndexError from cause
|
|
except IndexError as e:
|
|
self.failUnless(e.__cause__ is cause)
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_erroneous_cause(self):
|
|
class MyException(Exception):
|
|
def __init__(self):
|
|
raise RuntimeError()
|
|
|
|
try:
|
|
raise IndexError from MyException
|
|
except RuntimeError:
|
|
pass
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
|
|
class TestTraceback(unittest.TestCase):
|
|
def test_sets_traceback(self):
|
|
try:
|
|
raise IndexError()
|
|
except IndexError as e:
|
|
self.failUnless(isinstance(e.__traceback__, types.TracebackType))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_accepts_traceback(self):
|
|
tb = get_tb()
|
|
try:
|
|
raise IndexError().with_traceback(tb)
|
|
except IndexError as e:
|
|
self.assertNotEqual(e.__traceback__, tb)
|
|
self.assertEqual(e.__traceback__.tb_next, tb)
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
|
|
class TestContext(unittest.TestCase):
|
|
def test_instance_context_instance_raise(self):
|
|
context = IndexError()
|
|
try:
|
|
try:
|
|
raise context
|
|
except:
|
|
raise OSError()
|
|
except OSError as e:
|
|
self.assertEqual(e.__context__, context)
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_class_context_instance_raise(self):
|
|
context = IndexError
|
|
try:
|
|
try:
|
|
raise context
|
|
except:
|
|
raise OSError()
|
|
except OSError as e:
|
|
self.assertNotEqual(e.__context__, context)
|
|
self.failUnless(isinstance(e.__context__, context))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_class_context_class_raise(self):
|
|
context = IndexError
|
|
try:
|
|
try:
|
|
raise context
|
|
except:
|
|
raise OSError
|
|
except OSError as e:
|
|
self.assertNotEqual(e.__context__, context)
|
|
self.failUnless(isinstance(e.__context__, context))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_c_exception_context(self):
|
|
try:
|
|
try:
|
|
1/0
|
|
except:
|
|
raise OSError
|
|
except OSError as e:
|
|
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_c_exception_raise(self):
|
|
try:
|
|
try:
|
|
1/0
|
|
except:
|
|
xyzzy
|
|
except NameError as e:
|
|
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_noraise_finally(self):
|
|
try:
|
|
try:
|
|
pass
|
|
finally:
|
|
raise OSError
|
|
except OSError as e:
|
|
self.failUnless(e.__context__ is None)
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_raise_finally(self):
|
|
try:
|
|
try:
|
|
1/0
|
|
finally:
|
|
raise OSError
|
|
except OSError as e:
|
|
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_context_manager(self):
|
|
class ContextManager:
|
|
def __enter__(self):
|
|
pass
|
|
def __exit__(self, t, v, tb):
|
|
xyzzy
|
|
try:
|
|
with ContextManager():
|
|
1/0
|
|
except NameError as e:
|
|
self.failUnless(isinstance(e.__context__, ZeroDivisionError))
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_cycle_broken(self):
|
|
# Self-cycles (when re-raising a caught exception) are broken
|
|
try:
|
|
try:
|
|
1/0
|
|
except ZeroDivisionError as e:
|
|
raise e
|
|
except ZeroDivisionError as e:
|
|
self.failUnless(e.__context__ is None, e.__context__)
|
|
|
|
def test_reraise_cycle_broken(self):
|
|
# Non-trivial context cycles (through re-raising a previous exception)
|
|
# are broken too.
|
|
try:
|
|
try:
|
|
xyzzy
|
|
except NameError as a:
|
|
try:
|
|
1/0
|
|
except ZeroDivisionError:
|
|
raise a
|
|
except NameError as e:
|
|
self.failUnless(e.__context__.__context__ is None)
|
|
|
|
def test_3118(self):
|
|
# deleting the generator caused the __context__ to be cleared
|
|
def gen():
|
|
try:
|
|
yield 1
|
|
finally:
|
|
pass
|
|
|
|
def f():
|
|
g = gen()
|
|
next(g)
|
|
try:
|
|
try:
|
|
raise ValueError
|
|
except:
|
|
del g
|
|
raise KeyError
|
|
except Exception as e:
|
|
self.assert_(isinstance(e.__context__, ValueError))
|
|
|
|
f()
|
|
|
|
def test_3611(self):
|
|
# A re-raised exception in a __del__ caused the __context__
|
|
# to be cleared
|
|
class C:
|
|
def __del__(self):
|
|
try:
|
|
1/0
|
|
except:
|
|
raise
|
|
|
|
def f():
|
|
x = C()
|
|
try:
|
|
try:
|
|
x.x
|
|
except AttributeError:
|
|
del x
|
|
raise TypeError
|
|
except Exception as e:
|
|
self.assertNotEqual(e.__context__, None)
|
|
self.assert_(isinstance(e.__context__, AttributeError))
|
|
|
|
with support.captured_output("stderr"):
|
|
f()
|
|
|
|
class TestRemovedFunctionality(unittest.TestCase):
|
|
def test_tuples(self):
|
|
try:
|
|
raise (IndexError, KeyError) # This should be a tuple!
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
def test_strings(self):
|
|
try:
|
|
raise "foo"
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
self.fail("No exception raised")
|
|
|
|
|
|
def test_main():
|
|
support.run_unittest(__name__)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|