cpython/Lib/test/test_raise.py

521 lines
14 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 OSError as e:
return e.__traceback__
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.assertIn("No active exception", 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.assertIs(exc1, exc2)
else:
self.fail("No exception raised")
def test_except_reraise(self):
def reraise():
try:
raise TypeError("foo")
except TypeError:
try:
raise KeyError("caught")
except KeyError:
pass
raise
self.assertRaises(TypeError, reraise)
def test_finally_reraise(self):
def reraise():
try:
raise TypeError("foo")
except TypeError:
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 TypeError:
nested_reraise()
self.assertRaises(TypeError, reraise)
def test_raise_from_None(self):
try:
try:
raise TypeError("foo")
except TypeError:
raise ValueError() from None
except ValueError as e:
self.assertIsInstance(e.__context__, TypeError)
self.assertIsNone(e.__cause__)
def test_with_reraise1(self):
def reraise():
try:
raise TypeError("foo")
except TypeError:
with Context():
pass
raise
self.assertRaises(TypeError, reraise)
def test_with_reraise2(self):
def reraise():
try:
raise TypeError("foo")
except TypeError:
with Context():
raise KeyError("caught")
raise
self.assertRaises(TypeError, reraise)
def test_yield_reraise(self):
def reraise():
try:
raise TypeError("foo")
except TypeError:
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")
def test_new_returns_invalid_instance(self):
# See issue #11627.
class MyException(Exception):
def __new__(cls, *args):
return object()
with self.assertRaises(TypeError):
raise MyException
def test_assert_with_tuple_arg(self):
try:
assert False, (3,)
except AssertionError as e:
self.assertEqual(str(e), "(3,)")
class TestCause(unittest.TestCase):
def testCauseSyntax(self):
try:
try:
try:
raise TypeError
except Exception:
raise ValueError from None
except ValueError as exc:
self.assertIsNone(exc.__cause__)
self.assertTrue(exc.__suppress_context__)
exc.__suppress_context__ = False
raise exc
except ValueError as exc:
e = exc
self.assertIsNone(e.__cause__)
self.assertFalse(e.__suppress_context__)
self.assertIsInstance(e.__context__, TypeError)
def test_invalid_cause(self):
try:
raise IndexError from 5
except TypeError as e:
self.assertIn("exception cause", str(e))
else:
self.fail("No exception raised")
def test_class_cause(self):
try:
raise IndexError from KeyError
except IndexError as e:
self.assertIsInstance(e.__cause__, KeyError)
else:
self.fail("No exception raised")
def test_class_cause_nonexception_result(self):
class ConstructsNone(BaseException):
@classmethod
def __new__(*args, **kwargs):
return None
try:
raise IndexError from ConstructsNone
except TypeError as e:
self.assertIn("should have returned an instance of BaseException", str(e))
except IndexError:
self.fail("Wrong kind of exception raised")
else:
self.fail("No exception raised")
def test_instance_cause(self):
cause = KeyError()
try:
raise IndexError from cause
except IndexError as e:
self.assertIs(e.__cause__, 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.assertIsInstance(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 TestTracebackType(unittest.TestCase):
def raiser(self):
raise ValueError
def test_attrs(self):
try:
self.raiser()
except Exception as exc:
tb = exc.__traceback__
self.assertIsInstance(tb.tb_next, types.TracebackType)
self.assertIs(tb.tb_frame, sys._getframe())
self.assertIsInstance(tb.tb_lasti, int)
self.assertIsInstance(tb.tb_lineno, int)
self.assertIs(tb.tb_next.tb_next, None)
# Invalid assignments
with self.assertRaises(TypeError):
del tb.tb_next
with self.assertRaises(TypeError):
tb.tb_next = "asdf"
# Loops
with self.assertRaises(ValueError):
tb.tb_next = tb
with self.assertRaises(ValueError):
tb.tb_next.tb_next = tb
# Valid assignments
tb.tb_next = None
self.assertIs(tb.tb_next, None)
new_tb = get_tb()
tb.tb_next = new_tb
self.assertIs(tb.tb_next, new_tb)
def test_constructor(self):
other_tb = get_tb()
frame = sys._getframe()
tb = types.TracebackType(other_tb, frame, 1, 2)
self.assertEqual(tb.tb_next, other_tb)
self.assertEqual(tb.tb_frame, frame)
self.assertEqual(tb.tb_lasti, 1)
self.assertEqual(tb.tb_lineno, 2)
tb = types.TracebackType(None, frame, 1, 2)
self.assertEqual(tb.tb_next, None)
with self.assertRaises(TypeError):
types.TracebackType("no", frame, 1, 2)
with self.assertRaises(TypeError):
types.TracebackType(other_tb, "no", 1, 2)
with self.assertRaises(TypeError):
types.TracebackType(other_tb, frame, "no", 2)
with self.assertRaises(TypeError):
types.TracebackType(other_tb, frame, 1, "nuh-uh")
class TestContext(unittest.TestCase):
def test_instance_context_instance_raise(self):
context = IndexError()
try:
try:
raise context
except IndexError:
raise OSError()
except OSError as e:
self.assertIs(e.__context__, context)
else:
self.fail("No exception raised")
def test_class_context_instance_raise(self):
context = IndexError
try:
try:
raise context
except IndexError:
raise OSError()
except OSError as e:
self.assertIsNot(e.__context__, context)
self.assertIsInstance(e.__context__, context)
else:
self.fail("No exception raised")
def test_class_context_class_raise(self):
context = IndexError
try:
try:
raise context
except IndexError:
raise OSError
except OSError as e:
self.assertIsNot(e.__context__, context)
self.assertIsInstance(e.__context__, context)
else:
self.fail("No exception raised")
def test_c_exception_context(self):
try:
try:
1/0
except ZeroDivisionError:
raise OSError
except OSError as e:
self.assertIsInstance(e.__context__, ZeroDivisionError)
else:
self.fail("No exception raised")
def test_c_exception_raise(self):
try:
try:
1/0
except ZeroDivisionError:
xyzzy
except NameError as e:
self.assertIsInstance(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.assertIsNone(e.__context__)
else:
self.fail("No exception raised")
def test_raise_finally(self):
try:
try:
1/0
finally:
raise OSError
except OSError as e:
self.assertIsInstance(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.assertIsInstance(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.assertIsNone(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.assertIsNone(e.__context__.__context__)
def test_not_last(self):
# Context is not necessarily the last exception
context = Exception("context")
try:
raise context
except Exception:
try:
raise Exception("caught")
except Exception:
pass
try:
raise Exception("new")
except Exception as exc:
raised = exc
self.assertIs(raised.__context__, context)
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 ValueError:
del g
raise KeyError
except Exception as e:
self.assertIsInstance(e.__context__, ValueError)
f()
def test_3611(self):
import gc
# A re-raised exception in a __del__ caused the __context__
# to be cleared
class C:
def __del__(self):
try:
1/0
except ZeroDivisionError:
raise
def f():
x = C()
try:
try:
f.x
except AttributeError:
# make x.__del__ trigger
del x
gc.collect() # For PyPy or other GCs.
raise TypeError
except Exception as e:
self.assertNotEqual(e.__context__, None)
self.assertIsInstance(e.__context__, AttributeError)
with support.catch_unraisable_exception() as cm:
f()
self.assertEqual(ZeroDivisionError, cm.unraisable.exc_type)
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")
if __name__ == "__main__":
unittest.main()