8000 Add Format method to pythonexception (#1031) · pythonnet/pythonnet@5d28a8d · GitHub
[go: up one dir, main page]

Skip to content

Commit 5d28a8d

Browse files
authored
Add Format method to pythonexception (#1031)
Allows formatting a PythonException using traceback.format_exception
1 parent 638dc1c commit 5d28a8d

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1414
- Added support for Jetson Nano.
1515
- Added support for __len__ for .NET classes that implement ICollection
1616
- Added `object.GetRawPythonProxy() -> PyObject` extension method, that bypasses any conversions
17+
- Added PythonException.Format method to format exceptions the same as traceback.format_exception
1718

1819
### Changed
1920

src/embed_tests/TestPythonException.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,41 @@ public void TestPythonErrorTypeName()
5454
Assert.That(ex.PythonTypeName, Is.EqualTo("ModuleNotFoundError").Or.EqualTo("ImportError"));
5555
}
5656
}
57+
58+
[Test]
59+
public void TestPythonExceptionFormat()
60+
{
61+
try
62+
{
63+
PythonEngine.Exec("raise ValueError('Error!')");
64+
Assert.Fail("Exception should have been raised");
65+
}
66+
catch (PythonException ex)
67+
{
68+
Assert.That(ex.Format(), Does.Contain("Traceback").And.Contains("(most recent call last):").And.Contains("ValueError: Error!"));
69+
}
70+
}
71+
72+
[Test]
73+
public void TestPythonExceptionFormatNoError()
74+
{
75+
var ex = new PythonException();
76+
Assert.AreEqual(ex.StackTrace, ex.Format());
77+
}
78+
79+
[Test]
80+
public void TestPythonExceptionFormatNoTraceback()
81+
{
82+
try
83+
{
84+
var module = PythonEngine.ImportModule("really____unknown___module");
85+
Assert.Fail("Unknown module should not be loaded");
86+
}
87+
catch (PythonException ex)
88+
{
89+
// ImportError/ModuleNotFoundError do not have a traceback when not running in a script
90+
Assert.AreEqual(ex.StackTrace, ex.Format());
91+
}
92+
}
5793
}
5894
}

src/runtime/pythonexception.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Runtime.CompilerServices;
3+
using System.Text;
34

45
namespace Python.Runtime
56
{
@@ -145,6 +146,47 @@ public string PythonTypeName
145146
get { return _pythonTypeName; }
146147
}
147148

149+
/// <summary>
150+
/// Formats this PythonException object into a message as would be printed
151+
/// out via the Python console. See traceback.format_exception
152+
/// </summary>
153+
public string Format()
154+
{
155+
string res;
156+
IntPtr gs = PythonEngine.AcquireLock();
157+
try
158+
{
159+
if (_pyTB != IntPtr.Zero && _pyType != IntPtr.Zero && _pyValue != IntPtr.Zero)
160+
{
161+
Runtime.XIncref(_pyType);
162+
Runtime.XIncref(_pyValue);
163+
Runtime.XIncref(_pyTB);
164+
using (PyObject pyType = new PyObject(_pyType))
165+
using (PyObject pyValue = new PyObject(_pyValue))
166+
using (PyObject pyTB = new PyObject(_pyTB))
167+
using (PyObject tb_mod = PythonEngine.ImportModule("traceback"))
168+
{
169+
var buffer = new StringBuilder();
170+
var values = tb_mod.InvokeMethod("format_exception", pyType, pyValue, pyTB);
171+
foreach (PyObject val in values)
172+
{
173+
buffer.Append(val.ToString());
174+
}
175+
res = buffer.ToString();
176+
}
177+
}
178+
else
179+
{
180+
res = StackTrace;
181+
}
182+
}
183+
finally
184+
{
185+
PythonEngine.ReleaseLock(gs);
186+
}
187+
return res;
188+
}
189+
148190
/// <summary>
149191
/// Dispose Method
150192
/// </summary>

0 commit comments

Comments
 (0)
0