[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/kadeng/pythonnet/master/src/embed_tests/TestPythonException.cs [Back]  [Original]

using System;
using System.IO;
using System.Linq;

using NUnit.Framework;
using Python.Runtime;

namespace Python.EmbeddingTest
{
    public class TestPythonException
    {
        [OneTimeSetUp]
        public void SetUp()
        {
            PythonEngine.Initialize();

            // Add scripts folder to path in order to be able to import the test modules
            string testPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "fixtures");
            TestContext.Out.WriteLine(testPath);

            using var str = Runtime.Runtime.PyString_FromString(testPath);
            Assert.IsFalse(str.IsNull());
            BorrowedReference path = Runtime.Runtime.PySys_GetObject("path");
            Assert.IsFalse(path.IsNull);
            Runtime.Runtime.PyList_Append(path, str.Borrow());
        }

        [OneTimeTearDown]
        public void Dispose()
        {
            PythonEngine.Shutdown();
        }

        [Test]
        public void TestMessage()
        {
            var list = new PyList();
            PyObject foo = null;

            var ex = Assert.Throws(() => foo = list[0]);

            Assert.AreEqual("list index out of range", ex.Message);
            Assert.IsNull(foo);
        }

        [Test]
        public void TestType()
        {
            var list = new PyList();
            PyObject foo = null;

            var ex = Assert.Throws(() => foo = list[0]);

            Assert.AreEqual("IndexError", ex.Type.Name);
            Assert.IsNull(foo);
        }

        [Test]
        public void TestMessageComplete()
        {
            using (Py.GIL())
            {
                try
                {
                    // importing a module with syntax error 'x = 01' will throw
                    PyModule.FromString(Guid.NewGuid().ToString(), "x = 01");
                }
                catch (PythonException exception)
                {
                    Assert.True(exception.Message.Contains("x = 01"));
                    return;
                }
                Assert.Fail("No Exception was thrown!");
            }
        }

        [Test]
        public void TestNoError()
        {
            // There is no PyErr to fetch
            Assert.Throws(() => PythonException.FetchCurrentRaw());
            var currentError = PythonException.FetchCurrentOrNullRaw();
            Assert.IsNull(currentError);
        }

        [Test]
        public void TestNestedExceptions()
        {
            try
            {
                PythonEngine.Exec(@"
try:
  raise Exception('inner')
except Exception as ex:
  raise Exception('outer') from ex
");
            }
            catch (PythonException ex)
            {
                Assert.That(ex.InnerException, Is.InstanceOf());
                Assert.That(ex.InnerException.Message, Is.EqualTo("inner"));
            }
        }

        [Test]
        public void InnerIsEmptyWithNoCause()
        {
            var list = new PyList();
            PyObject foo = null;

            var ex = Assert.Throws(() => foo = list[0]);

            Assert.IsNull(ex.InnerException);
        }

        [Test]
        public void TestPythonExceptionFormat()
        {
            try
            {
                PythonEngine.Exec("raise ValueError('Error!')");
                Assert.Fail("Exception should have been raised");
            }
            catch (PythonException ex)
            {
                // Console.WriteLine($"Format: {ex.Format()}");
                // Console.WriteLine($"Stacktrace: {ex.StackTrace}");
                Assert.That(
                    ex.Format(),
                    Does.Contain("Traceback")
                    .And.Contains("(most recent call last):")
                    .And.Contains("ValueError: Error!")
                );

                // Check that the stacktrace is properly formatted
                Assert.That(
                    ex.StackTrace,
                    Does.Not.StartWith("[")
                    .And.Not.Contain("\\n")
                );
            }
        }

        [Test]
        public void TestPythonExceptionFormatNoTraceback()
        {
            try
            {
                var module = PyModule.Import("really____unknown___module");
                Assert.Fail("Unknown module should not be loaded");
            }
            catch (PythonException ex)
            {
                // ImportError/ModuleNotFoundError do not have a traceback when not running in a script
                Assert.AreEqual(ex.StackTrace, ex.Format());
            }
        }

        [Test]
        public void TestPythonExceptionFormatNormalized()
        {
            try
            {
                PythonEngine.Exec("a=b\n");
                Assert.Fail("Exception should have been raised");
            }
            catch (PythonException ex)
            {
                Assert.AreEqual("Traceback (most recent call last):\n  File \"\", line 1, in \nNameError: name 'b' is not defined\n", ex.Format());
            }
        }

        [Test]
        public void TestPythonException_PyErr_NormalizeException()
        {
            using (var scope = Py.CreateScope())
            {
                scope.Exec(@"
class TestException(NameError):
    def __init__(self, val):
        super().__init__(val)
        x = int(val)");
                Assert.IsTrue(scope.TryGet("TestException", out PyObject type));

                PyObject str = "dummy string".ToPython();
                var typePtr = new NewReference(type.Reference);
                var strPtr = new NewReference(str.Reference);
                var tbPtr = new NewReference(Runtime.Runtime.None.Reference);
                Runtime.Runtime.PyErr_NormalizeException(ref typePtr, ref strPtr, ref tbPtr);

                using var typeObj = typePtr.MoveToPyObject();
                using var strObj = strPtr.MoveToPyObject();
                using var tbObj = tbPtr.MoveToPyObject();
                // the type returned from PyErr_NormalizeException should not be the same type since a new
                // exception was raised by initializing the exception
                Assert.AreNotEqual(type.Handle, typeObj.Handle);
                // the message should now be the string from the throw exception during normalization
                Assert.AreEqual("invalid literal for int() with base 10: 'dummy string'", strObj.ToString());
            }
        }

        [Test]
        public void TestPythonException_Normalize_ThrowsWhenErrorSet()
        {
            Exceptions.SetError(Exceptions.TypeError, "Error!");
            var pythonException = PythonException.FetchCurrentRaw();
            Exceptions.SetError(Exceptions.TypeError, "Another error");
            Assert.Throws(() => pythonException.Normalize());
            Exceptions.Clear();
        }

        [Test]
        public void TestGetsPythonCodeInfoInStackTrace()
        {
            using (Py.GIL())
            {
                dynamic testClassModule = PyModule.FromString("TestGetsPythonCodeInfoInStackTrace_Module", @"
from clr import AddReference
AddReference(""Python.EmbeddingTest"")

from Python.EmbeddingTest import *

class TestPythonClass(TestPythonException.TestClass):
    def CallThrow(self):
        super().ThrowException()
");

                try
                {
                    var instance = testClassModule.TestPythonClass();
                    dynamic module = Py.Import("PyImportTest.SampleScript");
                    module.invokeMethod(instance, "CallThrow");
                }
                catch (ClrBubbledException ex)
                {
                    Assert.AreEqual("Test Exception Message", ex.InnerException.Message);

                    var pythonTracebackLines = ex.PythonTraceback.TrimEnd('\n').Split('\n').Select(x => x.Trim()).ToList();
                    Assert.AreEqual(5, pythonTracebackLines.Count);

                    Assert.AreEqual("File \"none\", line 9, in CallThrow", pythonTracebackLines[0]);

                    Assert.IsTrue(new[]
                    {
                        "File ",
                        "fixtures\\PyImportTest\\SampleScript.py",
                        "line 5",
                        "in invokeMethodImpl"
                    }.All(x => pythonTracebackLines[1].Contains(x)));
                    Assert.AreEqual("getattr(instance, method_name)()", pythonTracebackLines[2]);

                    Assert.IsTrue(new[]
                    {
                        "File ",
                        "fixtures\\PyImportTest\\SampleScript.py",
                        "line 2",
                        "in invokeMethod"
                    }.All(x => pythonTracebackLines[3].Contains(x)));
                    Assert.AreEqual("invokeMethodImpl(instance, method_name)", pythonTracebackLines[4]);
                }
                catch (Exception ex)
                {
                    Assert.Fail($"Unexpected exception: {ex}");
                }
            }
        }

        [Test]
        public void TestGetsPythonCodeInfoInStackTraceForNestedInterop()
        {
            using (Py.GIL())
            {
                dynamic testClassModule = PyModule.FromString("TestGetsPythonCodeInfoInStackTraceForNestedInterop_Module", @"
from clr import AddReference
AddReference(""Python.EmbeddingTest"")
AddReference(""System"")

from Python.EmbeddingTest import *
from System import Action

class TestPythonClass(TestPythonException.TestClass):
    def CallThrow(self):
        super().ThrowExceptionNested()

def GetThrowAction():
    return Action(CallThrow)

def CallThrow():
    TestPythonClass().CallThrow()
");

                try
                {
                    var action = testClassModule.GetThrowAction();
                    action();
                }
                catch (ClrBubbledException ex)
                {
                    Assert.AreEqual("Test Exception Message", ex.InnerException.Message);

                    var pythonTracebackLines = ex.PythonTraceback.TrimEnd('\n').Split('\n').Select(x => x.Trim()).ToList();
                    Assert.AreEqual(4, pythonTracebackLines.Count);

                    Assert.IsTrue(new[]
                    {
                        "File ",
                        "fixtures\\PyImportTest\\SampleScript.py",
                        "line 5",
                        "in invokeMethodImpl"
                    }.All(x => pythonTracebackLines[0].Contains(x)));
                    Assert.AreEqual("getattr(instance, method_name)()", pythonTracebackLines[1]);

                    Assert.IsTrue(new[]
                    {
                        "File ",
                        "fixtures\\PyImportTest\\SampleScript.py",
                        "line 2",
                        "in invokeMethod"
                    }.All(x => pythonTracebackLines[2].Contains(x)));
                    Assert.AreEqual("invokeMethodImpl(instance, method_name)", pythonTracebackLines[3]);
                }
                catch (Exception ex)
                {
                    Assert.Fail($"Unexpected exception: {ex}");
                }
            }
        }

        public class TestClass
        {
            public void ThrowException()
            {
                throw new ArgumentException("Test Exception Message");
            }

            public void ThrowExceptionNested()
            {
                using var _ = Py.GIL();

                dynamic module = Py.Import("PyImportTest.SampleScript");
                module.invokeMethod(this, "ThrowException");
            }
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page