From 31a4482debc5b7000de35ef9efaaace9caa06dae Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 17:45:07 +0200 Subject: [PATCH 1/7] crashing exception test --- src/testing/exceptiontest.cs | 9 +++++++++ src/tests/test_exceptions.py | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/testing/exceptiontest.cs b/src/testing/exceptiontest.cs index e4f683721..6ae41c4e1 100644 --- a/src/testing/exceptiontest.cs +++ b/src/testing/exceptiontest.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; namespace Python.Test { @@ -54,6 +56,13 @@ public static bool ThrowException() throw new OverflowException("error"); } + public static IEnumerable ThrowExceptionInIterator() + { + yield return 1; + yield return 2; + throw new OverflowException("error"); + } + public static void ThrowChainedExceptions() { try diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index f47209f6d..c816701c4 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -345,3 +345,17 @@ def test_chained_exceptions(): 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 + + val = ExceptionTest.ThrowExceptionInIterator().__iter__() + assert next(val) == 1 + assert next(val) == 2 + with pytest.raises(OverflowException) as cm: + next(val) + + exc = cm.value + + assert isinstance(exc, OverflowException) From 8531755b006c65d2b02ea64fe9be07bf1016ebc6 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 22:03:32 +0200 Subject: [PATCH 2/7] Catch exceptions from iterator.MoveNext() --- src/runtime/iterator.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/runtime/iterator.cs b/src/runtime/iterator.cs index c7c60ab19..f9cf10178 100644 --- a/src/runtime/iterator.cs +++ b/src/runtime/iterator.cs @@ -23,9 +23,21 @@ public Iterator(IEnumerator e) public static IntPtr tp_iternext(IntPtr ob) { var self = GetManagedObject(ob) as Iterator; - if (!self.iter.MoveNext()) + try { - Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); + if (!self.iter.MoveNext()) + { + Exceptions.SetError(Exceptions.StopIteration, Runtime.PyNone); + return IntPtr.Zero; + } + } + catch (Exception e) + { + if (e.InnerException != null) + { + e = e.InnerException; + } + Exceptions.SetError(e); return IntPtr.Zero; } object item = self.iter.Current; From 9e08150e6df5aab61f1b83b81206dab207c15b2a Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 22:57:50 +0200 Subject: [PATCH 3/7] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae62d692..1fa16cab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Added `clr.GetClrType` (#432, #433) - Allowed passing `None` for nullable args (#460) - Added keyword arguments based on C# syntax for calling CPython methods (#461) +- Catches exceptions thrown in C# iterators (yield returns) and rethrows them in python (#475) ### Changed From 4956b3e1b5ddc47e95c7e889f781f24e537523c9 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 23:41:28 +0200 Subject: [PATCH 4/7] Add test throwing exception with inner exception in iterator --- src/tests/test_exceptions.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index c816701c4..74ed96f0d 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -350,12 +350,30 @@ def test_iteration_exception(): from Python.Test import ExceptionTest from System import OverflowException - val = ExceptionTest.ThrowExceptionInIterator().__iter__() + 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 + +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 + exc = cm.value - assert isinstance(exc, OverflowException) + assert exc == exception.InnerException From 0717b9e9ee0e10bfb8893d6609ca90e617c85f70 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 23:44:45 +0200 Subject: [PATCH 5/7] Use PyPI version of codecov instead of prerelease github version --- requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index bcceedf25..4ca3c5dc6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,10 @@ # Requirements for both Travis and AppVeyor pytest coverage +codecov # Platform specific requirements pip; sys_platform == 'win32' wheel; sys_platform == 'win32' pycparser; sys_platform != 'win32' -# Coverage upload -# codecov v2.0.6 isn't on PyPi -https://github.com/codecov/codecov-python/tarball/v2.0.6 From 470773fd47015b92cabc4c9c5e43aacd06f52266 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Wed, 9 Aug 2017 23:45:35 +0200 Subject: [PATCH 6/7] Add exception argument to test method --- src/testing/exceptiontest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testing/exceptiontest.cs b/src/testing/exceptiontest.cs index 6ae41c4e1..45acaa2cc 100644 --- a/src/testing/exceptiontest.cs +++ b/src/testing/exceptiontest.cs @@ -56,11 +56,11 @@ public static bool ThrowException() throw new OverflowException("error"); } - public static IEnumerable ThrowExceptionInIterator() + public static IEnumerable ThrowExceptionInIterator(Exception e) { yield return 1; yield return 2; - throw new OverflowException("error"); + throw e; } public static void ThrowChainedExceptions() From 73872bbddcb7313b296e7aa06e22eccac7b53e90 Mon Sep 17 00:00:00 2001 From: Rickard Holmberg Date: Thu, 10 Aug 2017 08:42:33 +0200 Subject: [PATCH 7/7] Revert "Use PyPI version of codecov instead of prerelease github version" This reverts commit 0717b9e9ee0e10bfb8893d6609ca90e617c85f70. --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4ca3c5dc6..bcceedf25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,12 @@ # Requirements for both Travis and AppVeyor pytest coverage -codecov # Platform specific requirements pip; sys_platform == 'win32' wheel; sys_platform == 'win32' pycparser; sys_platform != 'win32' +# Coverage upload +# codecov v2.0.6 isn't on PyPi +https://github.com/codecov/codecov-python/tarball/v2.0.6