X Tutup
using System; using System.Diagnostics; using System.Reflection; using System.Runtime.ExceptionServices; namespace Python.Runtime { /// /// Encapsulates the Python exception APIs. /// /// /// Readability of the Exceptions class improvements as we look toward version 2.7 ... /// internal static class Exceptions { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. // set in Initialize internal static PyObject warnings_module; internal static PyObject exceptions_module; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. /// /// Initialization performed on startup of the Python runtime. /// internal static void Initialize() { string exceptionsModuleName = "builtins"; exceptions_module = PyModule.Import(exceptionsModuleName); warnings_module = PyModule.Import("warnings"); Type type = typeof(Exceptions); foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { using var op = Runtime.PyObject_GetAttrString(exceptions_module.obj, fi.Name); if (!@op.IsNull()) { fi.SetValue(type, op.MoveToPyObject()); } else { fi.SetValue(type, null); DebugUtil.Print($"Unknown exception: {fi.Name}"); } } Runtime.PyErr_Clear(); } /// /// Cleanup resources upon shutdown of the Python runtime. /// internal static void Shutdown() { if (Runtime.Py_IsInitialized() == 0) { return; } Type type = typeof(Exceptions); foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static)) { var op = (PyObject?)fi.GetValue(type); if (op is null) { continue; } op.Dispose(); fi.SetValue(null, null); } exceptions_module.Dispose(); warnings_module.Dispose(); } /// /// Set the 'args' slot on a python exception object that wraps /// a CLR exception. This is needed for pickling CLR exceptions as /// BaseException_reduce will only check the slots, bypassing the /// __getattr__ implementation, and thus dereferencing a NULL /// pointer. /// internal static bool SetArgsAndCause(BorrowedReference ob, Exception e) { NewReference args; if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); using var msg = Runtime.PyString_FromString(e.Message); Runtime.PyTuple_SetItem(args.Borrow(), 0, msg.StealOrThrow()); } else { args = Runtime.PyTuple_New(0); } using (args) { if (Runtime.PyObject_SetAttrString(ob, "args", args.Borrow()) != 0) { return false; } } if (e.InnerException != null) { // Note: For an AggregateException, InnerException is only the first of the InnerExceptions. using var cause = CLRObject.GetReference(e.InnerException); Runtime.PyException_SetCause(ob, cause.Steal()); } return true; } /// /// Shortcut for (pointer == NULL) -> throw PythonException /// /// Pointer to a Python object internal static BorrowedReference ErrorCheck(BorrowedReference pointer) { if (pointer.IsNull) { throw PythonException.ThrowLastAsClrException(); } return pointer; } internal static void ErrorCheck(IntPtr pointer) => ErrorCheck(new BorrowedReference(pointer)); /// /// Shortcut for (pointer == NULL or ErrorOccurred()) -> throw PythonException /// internal static void ErrorOccurredCheck(IntPtr pointer) { if (pointer == IntPtr.Zero || ErrorOccurred()) { throw PythonException.ThrowLastAsClrException(); } } internal static IntPtr ErrorCheckIfNull(IntPtr pointer) { if (pointer == IntPtr.Zero && ErrorOccurred()) { throw PythonException.ThrowLastAsClrException(); } return pointer; } /// /// ExceptionMatches Method /// /// /// Returns true if the current Python exception matches the given /// Python object. This is a wrapper for PyErr_ExceptionMatches. /// public static bool ExceptionMatches(BorrowedReference ob) { return Runtime.PyErr_ExceptionMatches(ob) != 0; } /// /// Sets the current Python exception given a native string. /// This is a wrapper for the Python PyErr_SetString call. /// public static void SetError(BorrowedReference type, string message) { Runtime.PyErr_SetString(type, message); } /// /// SetError Method /// /// /// Sets the current Python exception given a Python object. /// This is a wrapper for the Python PyErr_SetObject call. /// public static void SetError(BorrowedReference type, BorrowedReference exceptionObject) { Runtime.PyErr_SetObject(type, exceptionObject); } internal const string DispatchInfoAttribute = "__dispatch_info__"; /// /// SetError Method /// /// /// Sets the current Python exception given a CLR exception /// object. The CLR exception instance is wrapped as a Python /// object, allowing it to be handled naturally from Python. /// public static bool SetError(Exception e) { Debug.Assert(e is not null); // Because delegates allow arbitrary nesting of Python calling // managed calling Python calling... etc. it is possible that we // might get a managed exception raised that is a wrapper for a // Python exception. In that case we'd rather have the real thing. if (e is PythonException pe) { pe.Restore(); return true; } using var instance = Converter.ToPython(e); if (instance.IsNull()) return false; var exceptionInfo = ExceptionDispatchInfo.Capture(e); using var pyInfo = Converter.ToPython(exceptionInfo); if (Runtime.PyObject_SetAttrString(instance.Borrow(), DispatchInfoAttribute, pyInfo.Borrow()) != 0) return false; Debug.Assert(Runtime.PyObject_TypeCheck(instance.Borrow(), BaseException)); var type = Runtime.PyObject_TYPE(instance.Borrow()); Runtime.PyErr_SetObject(type, instance.Borrow()); return true; } /// /// When called after SetError, sets the cause of the error. /// /// The cause of the current error public static void SetCause(Exception cause) { var currentException = PythonException.FetchCurrentRaw(); currentException.Normalize(); using var causeInstance = Converter.ToPython(cause); Runtime.PyException_SetCause(currentException.Value!.Reference, causeInstance.Steal()); currentException.Restore(); } /// /// ErrorOccurred Method /// /// /// Returns true if an exception occurred in the Python runtime. /// This is a wrapper for the Python PyErr_Occurred call. /// public static bool ErrorOccurred() { return Runtime.PyErr_Occurred() != null; } /// /// Clear Method /// /// /// Clear any exception that has been set in the Python runtime. /// public static void Clear() { Runtime.PyErr_Clear(); } //==================================================================== // helper methods for raising warnings //==================================================================== /// /// Alias for Python's warnings.warn() function. /// public static void warn(string message, BorrowedReference exception, int stacklevel) { if (exception == null || (Runtime.PyObject_IsSubclass(exception, Exceptions.Warning) != 1)) { Exceptions.RaiseTypeError("Invalid exception"); } using var warn = Runtime.PyObject_GetAttrString(warnings_module.obj, "warn"); warn.BorrowOrThrow(); using var argsTemp = Runtime.PyTuple_New(3); BorrowedReference args = argsTemp.BorrowOrThrow(); using var msg = Runtime.PyString_FromString(message); Runtime.PyTuple_SetItem(args, 0, msg.StealOrThrow()); Runtime.PyTuple_SetItem(args, 1, exception); using var level = Runtime.PyInt_FromInt32(stacklevel); Runtime.PyTuple_SetItem(args, 2, level.StealOrThrow()); using var result = Runtime.PyObject_CallObject(warn.Borrow(), args); result.BorrowOrThrow(); } public static void warn(string message, BorrowedReference exception) { warn(message, exception, 1); } public static void deprecation(string message, int stacklevel) { warn(message, Exceptions.DeprecationWarning, stacklevel); } public static void deprecation(string message) { deprecation(message, 1); } //==================================================================== // Internal helper methods for common error handling scenarios. //==================================================================== /// /// Raises a and attaches any existing exception as its cause. /// /// The exception message /// null internal static NewReference RaiseTypeError(string message) { var cause = PythonException.FetchCurrentOrNullRaw(); cause?.Normalize(); Exceptions.SetError(Exceptions.TypeError, message); if (cause is null) return default; var typeError = PythonException.FetchCurrentRaw(); typeError.Normalize(); Runtime.PyException_SetCause( typeError.Value!, new NewReference(cause.Value!).Steal()); typeError.Restore(); return default; } // 2010-11-16: Arranged in python (2.6 & 2.7) source header file order /* Predefined exceptions are public static variables on the Exceptions class filled in from the python class using reflection in Initialize() looked up by name, not position. */ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. // set in Initialize public static PyObject BaseException; public static PyObject Exception; public static PyObject StopIteration; public static PyObject GeneratorExit; public static PyObject ArithmeticError; public static PyObject LookupError; public static PyObject AssertionError; public static PyObject AttributeError; public static PyObject BufferError; public static PyObject EOFError; public static PyObject FloatingPointError; public static PyObject EnvironmentError; public static PyObject IOError; public static PyObject OSError; public static PyObject ImportError; public static PyObject ModuleNotFoundError; public static PyObject IndexError; public static PyObject KeyError; public static PyObject KeyboardInterrupt; public static PyObject MemoryError; public static PyObject NameError; public static PyObject OverflowError; public static PyObject RuntimeError; public static PyObject NotImplementedError; public static PyObject SyntaxError; public static PyObject IndentationError; public static PyObject TabError; public static PyObject ReferenceError; public static PyObject SystemError; public static PyObject SystemExit; public static PyObject TypeError; public static PyObject UnboundLocalError; public static PyObject UnicodeError; public static PyObject UnicodeEncodeError; public static PyObject UnicodeDecodeError; public static PyObject UnicodeTranslateError; public static PyObject ValueError; public static PyObject ZeroDivisionError; //#ifdef MS_WINDOWS //public static IntPtr WindowsError; //#endif //#ifdef __VMS //public static IntPtr VMSError; //#endif //PyAPI_DATA(PyObject *) PyExc_BufferError; //PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst; //PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst; /* Predefined warning categories */ public static PyObject Warning; public static PyObject UserWarning; public static PyObject DeprecationWarning; public static PyObject PendingDeprecationWarning; public static PyObject SyntaxWarning; public static PyObject RuntimeWarning; public static PyObject FutureWarning; public static PyObject ImportWarning; public static PyObject UnicodeWarning; //PyAPI_DATA(PyObject *) PyExc_BytesWarning; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. } }
X Tutup