using System;
using System.Runtime.CompilerServices;
using System.Text;
namespace Python.Runtime
{
///
/// Provides a managed interface to exceptions thrown by the Python
/// runtime.
///
public class PythonException : System.Exception, IDisposable
{
private IntPtr _pyType = IntPtr.Zero;
private IntPtr _pyValue = IntPtr.Zero;
private IntPtr _pyTB = IntPtr.Zero;
private string _tb = "";
private string _message = "";
private string _pythonTypeName = "";
private bool disposed = false;
private bool _finalized = false;
public PythonException()
{
IntPtr gs = PythonEngine.AcquireLock();
Runtime.PyErr_Fetch(out _pyType, out _pyValue, out _pyTB);
if (_pyType != IntPtr.Zero && _pyValue != IntPtr.Zero)
{
string type;
string message;
Runtime.XIncref(_pyType);
using (var pyType = new PyObject(_pyType))
using (PyObject pyTypeName = pyType.GetAttr("__name__"))
{
type = pyTypeName.ToString();
}
_pythonTypeName = type;
Runtime.XIncref(_pyValue);
using (var pyValue = new PyObject(_pyValue))
{
message = pyValue.ToString();
}
_message = type + " : " + message;
}
if (_pyTB != IntPtr.Zero)
{
using (PyObject tb_module = PythonEngine.ImportModule("traceback"))
{
Runtime.XIncref(_pyTB);
using (var pyTB = new PyObject(_pyTB))
{
_tb = tb_module.InvokeMethod("format_tb", pyTB).ToString();
}
}
}
PythonEngine.ReleaseLock(gs);
}
// Ensure that encapsulated Python objects are decref'ed appropriately
// when the managed exception wrapper is garbage-collected.
~PythonException()
{
if (_finalized || disposed)
{
return;
}
_finalized = true;
Finalizer.Instance.AddFinalizedObject(ref _pyType);
Finalizer.Instance.AddFinalizedObject(ref _pyValue);
Finalizer.Instance.AddFinalizedObject(ref _pyTB);
}
///
/// Restores python error.
///
public void Restore()
{
IntPtr gs = PythonEngine.AcquireLock();
Runtime.PyErr_Restore(_pyType, _pyValue, _pyTB);
_pyType = IntPtr.Zero;
_pyValue = IntPtr.Zero;
_pyTB = IntPtr.Zero;
PythonEngine.ReleaseLock(gs);
}
///
/// PyType Property
///
///
/// Returns the exception type as a Python object.
///
public IntPtr PyType
{
get { return _pyType; }
}
///
/// PyValue Property
///
///
/// Returns the exception value as a Python object.
///
public IntPtr PyValue
{
get { return _pyValue; }
}
///
/// PyTB Property
///
///
/// Returns the TraceBack as a Python object.
///
public IntPtr PyTB
{
get { return _pyTB; }
}
///
/// Message Property
///
///
/// A string representing the python exception message.
///
public override string Message
{
get { return _message; }
}
///
/// StackTrace Property
///
///
/// A string representing the python exception stack trace.
///
public override string StackTrace
{
get { return _tb + base.StackTrace; }
}
///
/// Python error type name.
///
public string PythonTypeName
{
get { return _pythonTypeName; }
}
///
/// Formats this PythonException object into a message as would be printed
/// out via the Python console. See traceback.format_exception
///
public string Format()
{
string res;
IntPtr gs = PythonEngine.AcquireLock();
try
{
if (_pyTB != IntPtr.Zero && _pyType != IntPtr.Zero && _pyValue != IntPtr.Zero)
{
IntPtr tb = _pyTB;
IntPtr type = _pyType;
IntPtr value = _pyValue;
Runtime.XIncref(type);
Runtime.XIncref(value);
Runtime.XIncref(tb);
Runtime.PyErr_NormalizeException(ref type, ref value, ref tb);
using (PyObject pyType = new PyObject(type))
using (PyObject pyValue = new PyObject(value))
using (PyObject pyTB = new PyObject(tb))
using (PyObject tb_mod = PythonEngine.ImportModule("traceback"))
{
var buffer = new StringBuilder();
var values = tb_mod.InvokeMethod("format_exception", pyType, pyValue, pyTB);
foreach (PyObject val in values)
{
buffer.Append(val.ToString());
}
res = buffer.ToString();
}
}
else
{
res = StackTrace;
}
}
finally
{
PythonEngine.ReleaseLock(gs);
}
return res;
}
public bool IsMatches(IntPtr exc)
{
return Runtime.PyErr_GivenExceptionMatches(PyType, exc) != 0;
}
///
/// Dispose Method
///
///
/// The Dispose method provides a way to explicitly release the
/// Python objects represented by a PythonException.
/// If object not properly disposed can cause AppDomain unload issue.
/// See GH#397 and GH#400.
///
public void Dispose()
{
if (!disposed)
{
if (Runtime.Py_IsInitialized() > 0 && !Runtime.IsFinalizing)
{
IntPtr gs = PythonEngine.AcquireLock();
if (_pyType != IntPtr.Zero)
{
Runtime.XDecref(_pyType);
_pyType= IntPtr.Zero;
}
if (_pyValue != IntPtr.Zero)
{
Runtime.XDecref(_pyValue);
_pyValue = IntPtr.Zero;
}
// XXX Do we ever get TraceBack? //
if (_pyTB != IntPtr.Zero)
{
Runtime.XDecref(_pyTB);
_pyTB = IntPtr.Zero;
}
PythonEngine.ReleaseLock(gs);
}
GC.SuppressFinalize(this);
disposed = true;
}
}
///
/// Matches Method
///
///
/// Returns true if the Python exception type represented by the
/// PythonException instance matches the given exception type.
///
public static bool Matches(IntPtr ob)
{
return Runtime.PyErr_ExceptionMatches(ob) != 0;
}
[System.Diagnostics.DebuggerHidden]
public static void ThrowIfIsNull(IntPtr ob)
{
if (ob == IntPtr.Zero)
{
throw new PythonException();
}
}
[System.Diagnostics.DebuggerHidden]
internal static void ThrowIfIsNull(BorrowedReference reference)
{
if (reference.IsNull)
{
throw new PythonException();
}
}
[System.Diagnostics.DebuggerHidden]
public static void ThrowIfIsNotZero(int value)
{
if (value != 0)
{
throw new PythonException();
}
}
}
}