diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs
index 3fc29bde2..e7bb345f8 100644
--- a/src/runtime/clrobject.cs
+++ b/src/runtime/clrobject.cs
@@ -31,8 +31,9 @@ internal CLRObject(Object ob, IntPtr tp) : base()
this.gcHandle = gc;
inst = ob;
- // Fix the BaseException args slot if wrapping a CLR exception
- Exceptions.SetArgs(py);
+ // Fix the BaseException args (and __cause__ in case of Python 3)
+ // slot if wrapping a CLR exception
+ Exceptions.SetArgsAndCause(py);
}
@@ -70,4 +71,4 @@ internal static IntPtr GetInstHandle(Object ob)
return co.pyHandle;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs
index ec1b82175..48f40958d 100644
--- a/src/runtime/exceptions.cs
+++ b/src/runtime/exceptions.cs
@@ -144,7 +144,7 @@ internal static void Shutdown()
///
/// A CLR exception
/// The python object wrapping
- internal static void SetArgs(IntPtr ob)
+ internal static void SetArgsAndCause(IntPtr ob)
{
var e = ExceptionClassObject.ToException(ob);
if (e == null)
@@ -163,6 +163,14 @@ internal static void SetArgs(IntPtr ob)
}
Marshal.WriteIntPtr(ob, ExceptionOffset.args, args);
+
+#if !(PYTHON25 || PYTHON26 || PYTHON27)
+ if (e.InnerException != null)
+ {
+ IntPtr cause = CLRObject.GetInstHandle(e.InnerException);
+ Marshal.WriteIntPtr(ob, ExceptionOffset.cause, cause);
+ }
+#endif
}
///
@@ -434,4 +442,4 @@ puplic static variables on the Exceptions class filled in from
//PyAPI_DATA(PyObject *) PyExc_BytesWarning;
#endif
}
-}
\ No newline at end of file
+}
diff --git a/src/testing/exceptiontest.cs b/src/testing/exceptiontest.cs
index b38182857..414c25d82 100644
--- a/src/testing/exceptiontest.cs
+++ b/src/testing/exceptiontest.cs
@@ -1,6 +1,5 @@
using System;
-
namespace Python.Test
{
//========================================================================
@@ -55,6 +54,25 @@ public static bool ThrowException()
{
throw new OverflowException("error");
}
+
+ public static void ThrowChainedExceptions()
+ {
+ try
+ {
+ try
+ {
+ throw new Exception("Innermost exception");
+ }
+ catch (Exception exc)
+ {
+ throw new Exception("Inner exception", exc);
+ }
+ }
+ catch (Exception exc2)
+ {
+ throw new Exception("Outer exception", exc2);
+ }
+ }
}
diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py
index 892d6f062..eedf2aa61 100644
--- a/src/tests/test_exceptions.py
+++ b/src/tests/test_exceptions.py
@@ -345,6 +345,27 @@ def testPicklingExceptions(self):
self.assertEqual(exc.args, loaded.args)
+ def testChainedExceptions(self):
+ if six.PY3:
+ from Python.Test import ExceptionTest
+
+ try:
+ ExceptionTest.ThrowChainedExceptions()
+ except Exception as exc:
+ 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__
+
+ else:
+ self.fail("Test should raise an exception")
+
def test_suite():
return unittest.makeSuite(ExceptionTests)