X Tutup
# -*- coding: utf-8 -*- """Test exception support.""" import sys import System import pytest import pickle # begin code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects import gc # Recursively expand slist's objects # into olist, using seen to track # already processed objects. def _getr(slist, olist, seen): for e in slist: if id(e) in seen: continue seen[id(e)] = None olist.append(e) tl = gc.get_referents(e) if tl: _getr(tl, olist, seen) # The public function. def get_all_objects(): gcl = gc.get_objects() olist = [] seen = {} # Just in case: seen[id(gcl)] = None seen[id(olist)] = None seen[id(seen)] = None # _getr does the real work. _getr(gcl, olist, seen) return olist # end code from https://utcc.utoronto.ca/~cks/space/blog/python/GetAllObjects def leak_check(func): def do_leak_check(): func() gc.collect() exc = {x for x in get_all_objects() if isinstance(x, Exception) and not isinstance(x, pytest.PytestDeprecationWarning)} print(len(exc)) if len(exc): for x in exc: print('-------') print(repr(x)) print(gc.get_referrers(x)) print(len(gc.get_referrers(x))) assert False gc.collect() return do_leak_check def test_unified_exception_semantics(): """Test unified exception semantics.""" e = System.Exception('Something bad happened') assert isinstance(e, Exception) assert isinstance(e, System.Exception) def test_standard_exception_attributes(): """Test accessing standard exception attributes.""" from System import OverflowException from Python.Test import ExceptionTest e = ExceptionTest.GetExplicitException() assert isinstance(e, OverflowException) assert e.Message == 'error' e.Source = 'Test Suite' assert e.Source == 'Test Suite' v = e.ToString() assert len(v) > 0 def test_extended_exception_attributes(): """Test accessing extended exception attributes.""" from Python.Test import ExceptionTest, ExtendedException from System import OverflowException e = ExceptionTest.GetExtendedException() assert isinstance(e, ExtendedException) assert isinstance(e, OverflowException) assert isinstance(e, System.Exception) assert e.Message == 'error' e.Source = 'Test Suite' assert e.Source == 'Test Suite' v = e.ToString() assert len(v) > 0 assert e.ExtraProperty == 'extra' e.ExtraProperty = 'changed' assert e.ExtraProperty == 'changed' assert e.GetExtraInfo() == 'changed' def test_raise_class_exception(): """Test class exception propagation.""" from System import NullReferenceException with pytest.raises(NullReferenceException) as cm: raise NullReferenceException exc = cm.value assert isinstance(exc, NullReferenceException) def test_exc_info(): """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() assert type_ is NullReferenceException assert value.Message == "message" assert exc.Message == "message" # FIXME: Lower-case message isn't implemented # self.assertTrue(exc.message == "message") assert value is exc def test_raise_class_exception_with_value(): """Test class exception propagation with associated value.""" from System import NullReferenceException with pytest.raises(NullReferenceException) as cm: raise NullReferenceException('Aiiieee!') exc = cm.value assert isinstance(exc, NullReferenceException) assert exc.Message == 'Aiiieee!' def test_raise_instance_exception(): """Test instance exception propagation.""" from System import NullReferenceException with pytest.raises(NullReferenceException) as cm: raise NullReferenceException() exc = cm.value assert isinstance(exc, NullReferenceException) assert len(exc.Message) > 0 def test_raise_instance_exception_with_args(): """Test instance exception propagation with args.""" from System import NullReferenceException with pytest.raises(NullReferenceException) as cm: raise NullReferenceException("Aiiieee!") exc = cm.value assert isinstance(exc, NullReferenceException) assert exc.Message == 'Aiiieee!' def test_managed_exception_propagation(): """Test propagation of exceptions raised in managed code.""" from System import Decimal, OverflowException with pytest.raises(OverflowException): Decimal.ToInt64(Decimal.MaxValue) def test_managed_exception_conversion(): """Test conversion of managed exceptions.""" from System import OverflowException from Python.Test import ExceptionTest e = ExceptionTest.GetBaseException() assert isinstance(e, System.Exception) e = ExceptionTest.GetExplicitException() assert isinstance(e, OverflowException) assert isinstance(e, System.Exception) e = ExceptionTest.GetWidenedException() assert isinstance(e, OverflowException) assert isinstance(e, System.Exception) v = ExceptionTest.SetBaseException(System.Exception('error')) assert v v = ExceptionTest.SetExplicitException(OverflowException('error')) assert v v = ExceptionTest.SetWidenedException(OverflowException('error')) assert v def test_catch_exception_from_managed_method(): """Test catching an exception from a managed method.""" from Python.Test import ExceptionTest from System import OverflowException with pytest.raises(OverflowException) as cm: ExceptionTest().ThrowException() e = cm.value assert isinstance(e, OverflowException) def test_catch_exception_from_managed_property(): """Test catching an exception from a managed property.""" from Python.Test import ExceptionTest from System import OverflowException with pytest.raises(OverflowException) as cm: _ = ExceptionTest().ThrowProperty e = cm.value assert isinstance(e, OverflowException) with pytest.raises(OverflowException) as cm: ExceptionTest().ThrowProperty = 1 e = cm.value assert isinstance(e, OverflowException) def test_catch_exception_managed_class(): """Test catching the managed class of an exception.""" from System import OverflowException with pytest.raises(OverflowException): raise OverflowException('overflow') def test_catch_exception_python_class(): """Test catching the python class of an exception.""" from System import OverflowException with pytest.raises(Exception): raise OverflowException('overflow') def test_catch_exception_base_class(): """Test catching the base of an exception.""" from System import OverflowException, ArithmeticException with pytest.raises(ArithmeticException): raise OverflowException('overflow') def test_catch_exception_nested_base_class(): """Test catching the nested base of an exception.""" from System import OverflowException, SystemException with pytest.raises(SystemException): raise OverflowException('overflow') def test_catch_exception_with_assignment(): """Test catching an exception with assignment.""" from System import OverflowException with pytest.raises(OverflowException) as cm: raise OverflowException('overflow') e = cm.value assert isinstance(e, OverflowException) def test_catch_exception_unqualified(): """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(): """Test catching an unqualified exception with BaseException.""" from System import OverflowException with pytest.raises(BaseException): raise OverflowException('overflow') def test_apparent_module_of_exception(): """Test the apparent module of an exception.""" from System import OverflowException assert System.Exception.__module__ == 'System' assert OverflowException.__module__ == 'System' def test_str_of_exception(): """Test the str() representation of an exception.""" from System import NullReferenceException, Convert, FormatException e = NullReferenceException('') assert str(e) == '' e = NullReferenceException('Something bad happened') assert str(e).startswith('Something bad happened') with pytest.raises(FormatException) as cm: Convert.ToDateTime('this will fail') def test_python_compat_of_managed_exceptions(): """Test managed exceptions compatible with Python's implementation""" from System import OverflowException msg = "Simple message" e = OverflowException(msg) assert str(e) == msg assert e.args == (msg,) assert isinstance(e.args, tuple) strexp = "OverflowException('Simple message" assert repr(e)[:len(strexp)] == strexp def test_exception_is_instance_of_system_object(): """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 # 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): assert isinstance(o, Object) else: assert not isinstance(o, Object) def test_pickling_exceptions(): exc = System.Exception("test") dumped = pickle.dumps(exc) loaded = pickle.loads(dumped) assert exc.args == loaded.args def test_chained_exceptions(): from Python.Test import ExceptionTest with pytest.raises(Exception) as cm: ExceptionTest.ThrowChainedExceptions() exc = cm.value msgs = ("Outer exception", "Inner exception", "Innermost exception",) for msg in msgs: assert exc.Message == msg assert exc.__cause__ == exc.InnerException exc = exc.__cause__ def test_iteration_exception(): from Python.Test import ExceptionTest from System import OverflowException exception = OverflowException("error") val = ExceptionTest.ThrowExceptionInIterator(exception).__iter__() assert next(val) == 1 assert next(val) == 2 with pytest.raises(OverflowException) as cm: next(val) exc = cm.value assert exc == exception # after exception is thrown iterator is no longer valid with pytest.raises(StopIteration): next(val) def test_iteration_innerexception(): from Python.Test import ExceptionTest from System import OverflowException exception = System.Exception("message", OverflowException("error")) val = ExceptionTest.ThrowExceptionInIterator(exception).__iter__() assert next(val) == 1 assert next(val) == 2 with pytest.raises(OverflowException) as cm: next(val) exc = cm.value assert exc == exception.InnerException # after exception is thrown iterator is no longer valid with pytest.raises(StopIteration): next(val) def leak_test(func): def do_test_leak(): # PyTest leaks things, gather the current state orig_exc = {x for x in get_all_objects() if isinstance(x, Exception)} func() exc = {x for x in get_all_objects() if isinstance(x, Exception)} possibly_leaked = exc - orig_exc assert not possibly_leaked return do_test_leak @leak_test def test_dont_leak_exceptions_simple(): from Python.Test import ExceptionTest try: ExceptionTest.DoThrowSimple() except System.ArgumentException: print('type error, as expected') @leak_test def test_dont_leak_exceptions_inner(): from Python.Test import ExceptionTest try: ExceptionTest.DoThrowWithInner() except TypeError: print('type error, as expected') except System.ArgumentException: print('type error, also expected')
X Tutup