# -*- coding: utf-8 -*- import sys import unittest import System from _compat import PY2, PY3, pickle, text_type class ExceptionTests(unittest.TestCase): """Test exception support.""" def test_unified_exception_semantics(self): """Test unified exception semantics.""" e = System.Exception('Something bad happened') self.assertTrue(isinstance(e, Exception)) self.assertTrue(isinstance(e, System.Exception)) def test_standard_exception_attributes(self): """Test accessing standard exception attributes.""" from System import OverflowException from Python.Test import ExceptionTest e = ExceptionTest.GetExplicitException() self.assertTrue(isinstance(e, OverflowException)) self.assertTrue(e.Message == 'error') e.Source = 'Test Suite' self.assertTrue(e.Source == 'Test Suite') v = e.ToString() self.assertTrue(len(v) > 0) def test_extended_exception_attributes(self): """Test accessing extended exception attributes.""" from Python.Test import ExceptionTest, ExtendedException from System import OverflowException e = ExceptionTest.GetExtendedException() self.assertTrue(isinstance(e, ExtendedException)) self.assertTrue(isinstance(e, OverflowException)) self.assertTrue(isinstance(e, System.Exception)) self.assertTrue(e.Message == 'error') e.Source = 'Test Suite' self.assertTrue(e.Source == 'Test Suite') v = e.ToString() self.assertTrue(len(v) > 0) self.assertTrue(e.ExtraProperty == 'extra') e.ExtraProperty = 'changed' self.assertTrue(e.ExtraProperty == 'changed') self.assertTrue(e.GetExtraInfo() == 'changed') def test_raise_class_exception(self): """Test class exception propagation.""" from System import NullReferenceException with self.assertRaises(NullReferenceException) as cm: raise NullReferenceException exc = cm.exception self.assertTrue(isinstance(exc, NullReferenceException)) def test_exc_info(self): """Test class exception propagation. Behavior of exc_info changed in Py3. Refactoring its test""" from System import NullReferenceException try: raise NullReferenceException("message") except Exception as exc: type_, value, tb = sys.exc_info() self.assertTrue(type_ is NullReferenceException) self.assertTrue(value.Message == "message") self.assertTrue(exc.Message == "message") # FIXME: Lower-case message isn't implemented # self.assertTrue(exc.message == "message") self.assertTrue(value is exc) def test_raise_class_exception_with_value(self): """Test class exception propagation with associated value.""" from System import NullReferenceException with self.assertRaises(NullReferenceException) as cm: raise NullReferenceException('Aiiieee!') exc = cm.exception self.assertTrue(isinstance(exc, NullReferenceException)) self.assertTrue(exc.Message == 'Aiiieee!') def test_raise_instance_exception(self): """Test instance exception propagation.""" from System import NullReferenceException with self.assertRaises(NullReferenceException) as cm: raise NullReferenceException() exc = cm.exception self.assertTrue(isinstance(exc, NullReferenceException)) self.assertTrue(len(exc.Message) > 0) def test_raise_instance_exception_with_args(self): """Test instance exception propagation with args.""" from System import NullReferenceException with self.assertRaises(NullReferenceException) as cm: raise NullReferenceException("Aiiieee!") exc = cm.exception self.assertTrue(isinstance(exc, NullReferenceException)) self.assertTrue(exc.Message == 'Aiiieee!') def test_managed_exception_propagation(self): """Test propagation of exceptions raised in managed code.""" from System import Decimal, OverflowException with self.assertRaises(OverflowException): Decimal.ToInt64(Decimal.MaxValue) def test_managed_exception_conversion(self): """Test conversion of managed exceptions.""" from System import OverflowException from Python.Test import ExceptionTest e = ExceptionTest.GetBaseException() self.assertTrue(isinstance(e, System.Exception)) e = ExceptionTest.GetExplicitException() self.assertTrue(isinstance(e, OverflowException)) self.assertTrue(isinstance(e, System.Exception)) e = ExceptionTest.GetWidenedException() self.assertTrue(isinstance(e, OverflowException)) self.assertTrue(isinstance(e, System.Exception)) v = ExceptionTest.SetBaseException(System.Exception('error')) self.assertTrue(v) v = ExceptionTest.SetExplicitException(OverflowException('error')) self.assertTrue(v) v = ExceptionTest.SetWidenedException(OverflowException('error')) self.assertTrue(v) def test_catch_exception_from_managed_method(self): """Test catching an exception from a managed method.""" from Python.Test import ExceptionTest from System import OverflowException with self.assertRaises(OverflowException) as cm: ExceptionTest().ThrowException() e = cm.exception self.assertTrue(isinstance(e, OverflowException)) def test_catch_exception_from_managed_property(self): """Test catching an exception from a managed property.""" from Python.Test import ExceptionTest from System import OverflowException with self.assertRaises(OverflowException) as cm: _ = ExceptionTest().ThrowProperty e = cm.exception self.assertTrue(isinstance(e, OverflowException)) with self.assertRaises(OverflowException) as cm: ExceptionTest().ThrowProperty = 1 e = cm.exception self.assertTrue(isinstance(e, OverflowException)) def test_catch_exception_managed_class(self): """Test catching the managed class of an exception.""" from System import OverflowException with self.assertRaises(OverflowException): raise OverflowException('overflow') def test_catch_exception_python_class(self): """Test catching the python class of an exception.""" from System import OverflowException with self.assertRaises(Exception): raise OverflowException('overflow') def test_catch_exception_base_class(self): """Test catching the base of an exception.""" from System import OverflowException, ArithmeticException with self.assertRaises(ArithmeticException): raise OverflowException('overflow') def test_catch_exception_nested_base_class(self): """Test catching the nested base of an exception.""" from System import OverflowException, SystemException with self.assertRaises(SystemException): raise OverflowException('overflow') def test_catch_exception_with_assignment(self): """Test catching an exception with assignment.""" from System import OverflowException with self.assertRaises(OverflowException) as cm: raise OverflowException('overflow') e = cm.exception self.assertTrue(isinstance(e, OverflowException)) def test_catch_exception_unqualified(self): """Test catching an unqualified exception.""" from System import OverflowException try: raise OverflowException('overflow') except: pass else: self.fail("failed to catch unqualified exception") def test_catch_baseexception(self): """Test catching an unqualified exception with BaseException.""" from System import OverflowException with self.assertRaises(BaseException): raise OverflowException('overflow') def test_apparent_module_of_exception(self): """Test the apparent module of an exception.""" from System import OverflowException self.assertTrue(System.Exception.__module__ == 'System') self.assertTrue(OverflowException.__module__ == 'System') def test_str_of_exception(self): """Test the str() representation of an exception.""" from System import NullReferenceException, Convert, FormatException e = NullReferenceException('') self.assertEqual(str(e), '') e = NullReferenceException('Something bad happened') self.assertTrue(str(e).startswith('Something bad happened')) with self.assertRaises(FormatException) as cm: Convert.ToDateTime('this will fail') e = cm.exception # fix for international installation msg = text_type(e).encode("utf8") fnd = text_type('System.Convert.ToDateTime').encode("utf8") self.assertTrue(msg.find(fnd) > -1, msg) def test_python_compat_of_managed_exceptions(self): """Test managed exceptions compatible with Python's implementation""" from System import OverflowException msg = "Simple message" e = OverflowException(msg) self.assertEqual(str(e), msg) self.assertEqual(text_type(e), msg) self.assertEqual(e.args, (msg,)) self.assertTrue(isinstance(e.args, tuple)) if PY3: self.assertEqual(repr(e), "OverflowException('Simple message',)") elif PY2: self.assertEqual(repr(e), "OverflowException(u'Simple message',)") def test_exception_is_instance_of_system_object(self): """Test behavior of isinstance(, System.Object).""" # This is an anti-test, in that this is a caveat of the current # implementation. Because exceptions are not allowed to be new-style # classes, we wrap managed exceptions in a general-purpose old-style # class that delegates to the wrapped object. This makes _almost_ # everything work as expected, except that an isinstance check against # CLR.System.Object will fail for a managed exception (because a new # style class cannot appear in the __bases__ of an old-style class # without causing a crash in the CPython interpreter). This test is # here mainly to remind me to update the caveat in the documentation # one day when when exceptions can be new-style classes. # This behavior is now over-shadowed by the implementation of # __instancecheck__ (i.e., overloading isinstance), so for all Python # version >= 2.6 we expect isinstance(, Object) to # be true, even though it does not really subclass Object. from System import OverflowException, Object o = OverflowException('error') if sys.version_info >= (2, 6): self.assertTrue(isinstance(o, Object)) else: self.assertFalse(isinstance(o, Object)) def test_pickling_exceptions(self): exc = System.Exception("test") dumped = pickle.dumps(exc) loaded = pickle.loads(dumped) self.assertEqual(exc.args, loaded.args) @unittest.skipIf(PY2, "__cause__ isn't implemented in PY2") def test_chained_exceptions(self): from Python.Test import ExceptionTest with self.assertRaises(Exception) as cm: ExceptionTest.ThrowChainedExceptions() exc = cm.exception msgs = ("Outer exception", "Inner exception", "Innermost exception",) for msg in msgs: self.assertEqual(exc.Message, msg) self.assertEqual(exc.__cause__, exc.InnerException) exc = exc.__cause__ def test_suite(): return unittest.makeSuite(ExceptionTests)