[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/mysl/pythonnet/develop/src/runtime/exceptions.cs [Back]  [Original]

// ==========================================================================
// This software is subject to the provisions of the Zope Public License,
// Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE.
// ==========================================================================

using System;
using System.Reflection;
using System.Collections;
using System.Runtime.InteropServices;


namespace Python.Runtime {

    /// 
    /// Base class for Python types that reflect managed exceptions based on
    /// System.Exception
    /// 
    /// 
    /// The Python wrapper for managed exceptions LIES about its inheritance
    /// tree. Although the real System.Exception is a subclass of 
    /// System.Object the Python type for System.Exception does NOT claim that
    /// it subclasses System.Object. Instead TypeManager.CreateType() uses 
    /// Python's exception.Exception class as base class for System.Exception.
    /// 
    internal class ExceptionClassObject : ClassObject {

        internal ExceptionClassObject(Type tp) : base(tp) {
        }

#if (PYTHON25 || PYTHON26 || PYTHON27)
        internal static Exception ToException(IntPtr ob) {
            CLRObject co = GetManagedObject(ob) as CLRObject;
            if (co == null) {
                return null;
            }
            Exception e = co.inst as Exception;
            if (e == null) {
                return null;
            }
            return e;
        }

        //====================================================================
        // Exception __str__ implementation
        //====================================================================
        
        public new static IntPtr tp_str(IntPtr ob) {
            Exception e = ToException(ob);
            if (e == null) {
                return Exceptions.RaiseTypeError("invalid object");
            }

            string message = String.Empty;
            if (e.Message != String.Empty) {
                message = e.Message;
            }
            if ((e.StackTrace != null) && (e.StackTrace != String.Empty)) {
                message = message + "\n" + e.StackTrace;
            }
            return Runtime.PyUnicode_FromString(message);
        }

        //====================================================================
        // Exception __repr__ implementation.
        //====================================================================

        public static IntPtr tp_repr(IntPtr ob) {
            Exception e = ToException(ob);
            if (e == null) {
                return Exceptions.RaiseTypeError("invalid object");
            }
            string name = e.GetType().Name;
            string message;
            if (e.Message != String.Empty) {
                message = String.Format("{0}('{1}',)", name, e.Message);
            } else {
                message = String.Format("{0}()", name);
            }
            return Runtime.PyUnicode_FromString(message);
        }
        //====================================================================
        // Exceptions __getattribute__ implementation. 
        // handles Python's args and message attributes
        //====================================================================

        public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
        {
            if (!Runtime.PyString_Check(key)) {
                Exceptions.SetError(Exceptions.TypeError, "string expected");
                return IntPtr.Zero;
            }

            string name = Runtime.GetManagedString(key);
            if (name == "args") {
                Exception e = ToException(ob);
                IntPtr args;
                if (e.Message != String.Empty) {
                    args = Runtime.PyTuple_New(1);
                    IntPtr msg = Runtime.PyUnicode_FromString(e.Message);
                    Runtime.PyTuple_SetItem(args, 0, msg);
                } else {
                    args = Runtime.PyTuple_New(0);
                }
                return args;
            }

            if (name == "message") {
                return ExceptionClassObject.tp_str(ob);
            }

            return Runtime.PyObject_GenericGetAttr(ob, key);
        }
#endif      // (PYTHON25 || PYTHON26 || PYTHON27)
    }

    /// 
    /// Encapsulates the Python exception APIs.
    /// 
    /// 
    /// Readability of the Exceptions class improvements as we look toward version 2.7 ...
    /// 

    public class Exceptions {

        internal static IntPtr warnings_module;
        internal static IntPtr exceptions_module;

        private Exceptions() {}

        //===================================================================
        // Initialization performed on startup of the Python runtime.
        //===================================================================

        internal static void Initialize() {
            exceptions_module = Runtime.PyImport_ImportModule("exceptions");
            Exceptions.ErrorCheck(exceptions_module);
            warnings_module = Runtime.PyImport_ImportModule("warnings");
            Exceptions.ErrorCheck(warnings_module);
            Type type = typeof(Exceptions);
            foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | 
                                                    BindingFlags.Static)) {
                IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module, fi.Name);
                if (op != IntPtr.Zero) {
                    fi.SetValue(type, op);
                }
                else {
                    fi.SetValue(type, IntPtr.Zero);
                    DebugUtil.Print("Unknown exception: " + fi.Name);
                }
            }
            Runtime.PyErr_Clear();
            if (Runtime.wrap_exceptions) {
                SetupExceptionHack();
            }
        }


        //===================================================================
        // Cleanup resources upon shutdown of the Python runtime.
        //===================================================================

        internal static void Shutdown() {
            Type type = typeof(Exceptions);
            foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | 
                                                    BindingFlags.Static)) {
                IntPtr op = (IntPtr)fi.GetValue(type);
                if (op != IntPtr.Zero) {
                      Runtime.Decref(op);
                }
            }
            Runtime.Decref(exceptions_module);
            Runtime.Decref(warnings_module);
        }

        /// 
        ///  Shortcut for (pointer == NULL) -> throw PythonException
        /// 
        /// Pointer to a Python object
        internal unsafe static void ErrorCheck(IntPtr pointer) {
            if (pointer == IntPtr.Zero) {
                throw new PythonException();
            }
        }

        /// 
        ///  Shortcut for (pointer == NULL or ErrorOccurred()) -> throw PythonException
        /// 
        ///  Shortcut for (pointer == NULL) -> throw PythonException
        internal unsafe static void ErrorOccurredCheck(IntPtr pointer) {
            if ((pointer == IntPtr.Zero) || Exceptions.ErrorOccurred()) {
                throw new PythonException();
            }
        }

        // Versions of CPython up to 2.4 do not allow exceptions to be
        // new-style classes. To get around that restriction and provide
        // a consistent user experience for programmers, we wrap managed
        // exceptions in an old-style class that (through some dont-try-
        // this-at-home hackery) delegates to the managed exception and
        // obeys the conventions of both Python and managed exceptions.

        /// 
        /// Conditionally initialized variables!
        /// 
        static IntPtr ns_exc; // new-style class for System.Exception
        static IntPtr os_exc; // old-style class for System.Exception
        static Hashtable cache;

        /// 
        /// the lines
        /// // XXX - hack to raise a compatible old-style exception ;(
        /// if (Runtime.wrap_exceptions) {
        ///     CallOneOfTheseMethods();
        ///  
        /// 
        internal static void SetupExceptionHack() {
            ns_exc = ClassManager.GetClass(typeof(Exception)).pyHandle;
            cache = new Hashtable();

            string code = 
            "import exceptions\n" +
            "class Exception(exceptions.Exception):\n" +
            "    _class = None\n" +
            "    _inner = None\n" +
            "    \n" +
            "    #@property\n" +
            "    def message(self):\n" +
            "        return self.Message\n" +
            "    message = property(message)\n" +
            "    \n" +
            "    def __init__(self, *args, **kw):\n" +
            "        inst = self.__class__._class(*args, **kw)\n" +
            "        self.__dict__['_inner'] = inst\n" +
            "        exceptions.Exception.__init__(self, *args, **kw)\n" +
            "\n" +
            "    def __getattr__(self, name, _marker=[]):\n" +
            "        inner = self.__dict__['_inner']\n" +
            "        v = getattr(inner, name, _marker)\n" +
            "        if v is not _marker:\n" +
            "            return v\n" +
            "        v = self.__dict__.get(name, _marker)\n" +
            "        if v is not _marker:\n" +
            "            return v\n" +
            "        raise AttributeError(name)\n" +
            "\n" +
            "    def __setattr__(self, name, value):\n" +
            "        inner = self.__dict__['_inner']\n" +
            "        setattr(inner, name, value)\n" +
            "\n" +
            "    def __str__(self):\n" +
            "        inner = self.__dict__.get('_inner')\n" +
            "        msg = getattr(inner, 'Message', '')\n" +
            "        st = getattr(inner, 'StackTrace', '')\n" +
            "        st = st and '\\n' + st or ''\n" +
            "        return msg + st\n" +
            "    \n" + 
            "    def __repr__(self):\n" +
            "        inner = self.__dict__.get('_inner')\n" +
            "        msg = getattr(inner, 'Message', '')\n" +
            "        name = self.__class__.__name__\n" +
            "        return '%s(\\'%s\\',)' % (name, msg) \n" +
            "\n";

            IntPtr dict = Runtime.PyDict_New();

            IntPtr builtins = Runtime.PyEval_GetBuiltins();
            Runtime.PyDict_SetItemString(dict, "__builtins__", builtins);

            IntPtr namestr = Runtime.PyString_FromString("System");
            Runtime.PyDict_SetItemString(dict, "__name__", namestr);
            Runtime.Decref(namestr);

            Runtime.PyDict_SetItemString(dict, "__file__", Runtime.PyNone);
            Runtime.PyDict_SetItemString(dict, "__doc__", Runtime.PyNone);

            IntPtr flag = Runtime.Py_file_input;
            IntPtr result = Runtime.PyRun_String(code, flag, dict, dict);
            Exceptions.ErrorCheck(result);
            Runtime.Decref(result);

            os_exc = Runtime.PyDict_GetItemString(dict, "Exception");
            Runtime.PyObject_SetAttrString(os_exc, "_class", ns_exc);
            Runtime.PyErr_Clear();
        }


        internal static IntPtr GenerateExceptionClass(IntPtr real) {
            if (real == ns_exc) {
                return os_exc;
            }

            IntPtr nbases = Runtime.PyObject_GetAttrString(real, "__bases__");
            if (Runtime.PyTuple_Size(nbases) != 1) {
                throw new SystemException("Invalid __bases__");
            }
            IntPtr nsbase = Runtime.PyTuple_GetItem(nbases, 0);
            Runtime.Decref(nbases);

            IntPtr osbase = GetExceptionClassWrapper(nsbase);
            IntPtr baselist = Runtime.PyTuple_New(1);
            Runtime.Incref(osbase);
            Runtime.PyTuple_SetItem(baselist, 0, osbase);
            IntPtr name = Runtime.PyObject_GetAttrString(real, "__name__");

            IntPtr dict = Runtime.PyDict_New();
            IntPtr mod = Runtime.PyObject_GetAttrString(real, "__module__");
            Runtime.PyDict_SetItemString(dict, "__module__", mod);
            Runtime.Decref(mod);

            IntPtr subc = Runtime.PyClass_New(baselist, dict, name);
            Runtime.Decref(baselist);
            Runtime.Decref(dict);
            Runtime.Decref(name);

            Runtime.PyObject_SetAttrString(subc, "_class", real);
            return subc;
        }

        internal static IntPtr GetExceptionClassWrapper(IntPtr real) {
            // Given the pointer to a new-style class representing a managed
            // exception, return an appropriate old-style class wrapper that
            // maintains all of the expectations and delegates to the wrapped
            // class.
            object ob = cache[real];
            if (ob == null) {
                IntPtr op = GenerateExceptionClass(real);
                cache[real] = op;
                return op;
            }
            return (IntPtr)ob;
        }

        internal static IntPtr GetExceptionInstanceWrapper(IntPtr real) {
            // Given the pointer to a new-style class instance representing a 
            // managed exception, return an appropriate old-style class 
            // wrapper instance that delegates to the wrapped instance.
            IntPtr tp = Runtime.PyObject_TYPE(real);
            if (Runtime.PyObject_TYPE(tp) == Runtime.PyInstanceType) {
                return real;
            }
            // Get / generate a class wrapper, instantiate it and set its
            // _inner attribute to the real new-style exception instance.
            IntPtr ct = GetExceptionClassWrapper(tp);
            Exceptions.ErrorCheck(ct);
            IntPtr op = Runtime.PyInstance_NewRaw(ct, IntPtr.Zero);
            Exceptions.ErrorCheck(op);
            IntPtr d = Runtime.PyObject_GetAttrString(op, "__dict__");
            Exceptions.ErrorCheck(d);
            Runtime.PyDict_SetItemString(d, "_inner", real);
            Runtime.Decref(d);
            return op;
        }

        internal static IntPtr UnwrapExceptionClass(IntPtr op) {
            // In some cases its necessary to recognize an exception *class*,
            // and obtain the inner (wrapped) exception class. This method
            // returns the inner class if found, or a null pointer.

            IntPtr d = Runtime.PyObject_GetAttrString(op, "__dict__");
            if (d == IntPtr.Zero) {
                Exceptions.Clear();
                return IntPtr.Zero;
            }
            IntPtr c = Runtime.PyDict_GetItemString(d, "_class");
            Runtime.Decref(d);
            if (c == IntPtr.Zero) {
                Exceptions.Clear();
            }
            return c;
        }

        /// 
        /// GetException Method
        /// 
        ///
        /// 
        /// Retrieve Python exception information as a PythonException
        /// instance. The properties of the PythonException may be used
        /// to access the exception type, value and traceback info.
        /// 

        public static PythonException GetException() {
            // TODO: implement this.
            return null;
        }

        /// 
        /// 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(IntPtr ob) {
            return Runtime.PyErr_ExceptionMatches(ob) != 0;
        }

        /// 
        /// ExceptionMatches Method
        /// 
        ///
        /// 
        /// Returns true if the given Python exception matches the given
        /// Python object. This is a wrapper for PyErr_GivenExceptionMatches.
        /// 

        public static bool ExceptionMatches(IntPtr exc, IntPtr ob) {
            int i = Runtime.PyErr_GivenExceptionMatches(exc, ob);
            return (i != 0);
        }

        /// 
        /// SetError Method
        /// 
        ///
        /// 
        /// Sets the current Python exception given a native string.
        /// This is a wrapper for the Python PyErr_SetString call.
        /// 

        public static void SetError(IntPtr ob, string value) {
            Runtime.PyErr_SetString(ob, value);
        }

        /// 
        /// 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(IntPtr ob, IntPtr value) {
            Runtime.PyErr_SetObject(ob, value);
        }

        /// 
        /// 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 void SetError(Exception e) {

            // Because delegates allow arbitrary nestings 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.

            PythonException pe = e as PythonException;
            if (pe != null) {
                Runtime.PyErr_SetObject(pe.PyType, pe.PyValue);
                return;
            }

            IntPtr op = CLRObject.GetInstHandle(e);

            // XXX - hack to raise a compatible old-style exception ;(
            if (Runtime.wrap_exceptions) {
                op = GetExceptionInstanceWrapper(op);
            }
            IntPtr etype = Runtime.PyObject_GetAttrString(op, "__class__");
            Runtime.PyErr_SetObject(etype, op);
            Runtime.Decref(etype);
            Runtime.Decref(op);
        }

        /// 
        /// 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() != 0;
        }

        /// 
        /// 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, IntPtr exception, int stacklevel)
        {
            if ((exception == IntPtr.Zero) ||
                (Runtime.PyObject_IsSubclass(exception, Exceptions.Warning) != 1)) {
                    Exceptions.RaiseTypeError("Invalid exception");
            }

            Runtime.Incref(warnings_module);
            IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module, "warn");
            Runtime.Decref(warnings_module);
            Exceptions.ErrorCheck(warn);

            IntPtr args = Runtime.PyTuple_New(3);
            IntPtr msg = Runtime.PyString_FromString(message);
            Runtime.Incref(exception); // PyTuple_SetItem steals a reference
            IntPtr level = Runtime.PyInt_FromInt32(stacklevel);
            Runtime.PyTuple_SetItem(args, 0, msg);
            Runtime.PyTuple_SetItem(args, 1, exception);
            Runtime.PyTuple_SetItem(args, 2, level);

            IntPtr result = Runtime.PyObject_CallObject(warn, args);
            Exceptions.ErrorCheck(result);

            Runtime.Decref(warn);
            Runtime.Decref(result);
            Runtime.Decref(args);
        }

        public static void warn(string message, IntPtr 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.
        //====================================================================

        internal static IntPtr RaiseTypeError(string message) {
            Exceptions.SetError(Exceptions.TypeError, message);
            return IntPtr.Zero;
        }

        // 2010-11-16: Arranged in python (2.6 & 2.7) source header file order
        /* Predefined exceptions are
           puplic static variables on the Exceptions class filled in from
           the python class using reflection in Initialize() looked up by
		   name, not posistion. */
#if (PYTHON25 || PYTHON26 || PYTHON27)
        public static IntPtr BaseException;
#endif
        public static IntPtr Exception;
        public static IntPtr StopIteration;
#if (PYTHON25 || PYTHON26 || PYTHON27)
        public static IntPtr GeneratorExit;
#endif
        public static IntPtr StandardError;
        public static IntPtr ArithmeticError;
        public static IntPtr LookupError;
        
        public static IntPtr AssertionError;
        public static IntPtr AttributeError;
        public static IntPtr EOFError;
        public static IntPtr FloatingPointError;
        public static IntPtr EnvironmentError;
        public static IntPtr IOError;
        public static IntPtr OSError;
        public static IntPtr ImportError;
        public static IntPtr IndexError;
        public static IntPtr KeyError;
        public static IntPtr KeyboardInterrupt;
        public static IntPtr MemoryError;
        public static IntPtr NameError;
        public static IntPtr OverflowError;
        public static IntPtr RuntimeError;
        public static IntPtr NotImplementedError;
        public static IntPtr SyntaxError;
        public static IntPtr IndentationError;
        public static IntPtr TabError;
        public static IntPtr ReferenceError;
        public static IntPtr SystemError;
        public static IntPtr SystemExit;
        public static IntPtr TypeError;
        public static IntPtr UnboundLocalError;
        public static IntPtr UnicodeError;
        public static IntPtr UnicodeEncodeError;
        public static IntPtr UnicodeDecodeError;
        public static IntPtr UnicodeTranslateError;
        public static IntPtr ValueError;
        public static IntPtr 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 IntPtr Warning;
        public static IntPtr UserWarning;
        public static IntPtr DeprecationWarning;
        public static IntPtr PendingDeprecationWarning;
        public static IntPtr SyntaxWarning;
        public static IntPtr RuntimeWarning;
        public static IntPtr FutureWarning;
#if (PYTHON25 || PYTHON26 || PYTHON27)
        public static IntPtr ImportWarning;
        public static IntPtr UnicodeWarning;
        //PyAPI_DATA(PyObject *) PyExc_BytesWarning;
#endif
    }


}

Web Proxy Viewer  |  New URL  |  Original Page