From 2b167afc38e8bcad1eed6c56d2651d1479a26b3b Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 09:40:39 +0100 Subject: [PATCH 1/7] Remove Python <2.5 exception wrapping. --- src/runtime/converter.cs | 45 +------- src/runtime/exceptions.cs | 216 ------------------------------------ src/runtime/managedtype.cs | 14 --- src/runtime/moduleobject.cs | 34 ------ src/runtime/runtime.cs | 20 ---- src/runtime/typemanager.cs | 18 ++- 6 files changed, 8 insertions(+), 339 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 2737046a0..ef3aa3e18 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -156,20 +156,7 @@ internal static IntPtr ToPython(Object value, Type type) switch (tc) { case TypeCode.Object: - result = CLRObject.GetInstHandle(value, type); - - // XXX - hack to make sure we convert new-style class based - // managed exception instances to wrappers ;( - if (Runtime.wrap_exceptions) - { - Exception e = value as Exception; - if (e != null) - { - return Exceptions.GetExceptionInstanceWrapper(result); - } - } - - return result; + return CLRObject.GetInstHandle(value, type); case TypeCode.String: return Runtime.PyUnicode_FromString((string)value); @@ -283,36 +270,6 @@ internal static bool ToManagedValue(IntPtr value, Type obType, ManagedType mt = ManagedType.GetManagedObject(value); result = null; - // XXX - hack to support objects wrapped in old-style classes - // (such as exception objects). - if (Runtime.wrap_exceptions) - { - if (mt == null) - { - if (Runtime.PyObject_IsInstance( - value, Exceptions.Exception - ) > 0) - { - IntPtr p = Runtime.PyObject_GetAttrString(value, "_inner"); - if (p != IntPtr.Zero) - { - // This is safe because we know that the __dict__ of - // value holds a reference to _inner. - value = p; - Runtime.XDecref(p); - mt = ManagedType.GetManagedObject(value); - } - } - IntPtr c = Exceptions.UnwrapExceptionClass(value); - if ((c != IntPtr.Zero) && (c != value)) - { - value = c; - Runtime.XDecref(c); - mt = ManagedType.GetManagedObject(value); - } - } - } - if (mt != null) { if (mt is CLRObject) diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index f95f177e2..1652ad794 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -23,7 +23,6 @@ internal ExceptionClassObject(Type tp) : base(tp) { } -#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) internal static Exception ToException(IntPtr ob) { CLRObject co = GetManagedObject(ob) as CLRObject; @@ -125,7 +124,6 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return Runtime.PyObject_GenericGetAttr(ob, key); } -#endif // (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) } /// @@ -173,10 +171,6 @@ internal static void Initialize() } } Runtime.PyErr_Clear(); - if (Runtime.wrap_exceptions) - { - SetupExceptionHack(); - } } @@ -228,210 +222,6 @@ internal unsafe static void ErrorOccurredCheck(IntPtr pointer) } } - // 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.XDecref(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.XDecref(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.XDecref(nbases); - - IntPtr osbase = GetExceptionClassWrapper(nsbase); - IntPtr baselist = Runtime.PyTuple_New(1); - Runtime.XIncref(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.XDecref(mod); - - IntPtr subc = Runtime.PyClass_New(baselist, dict, name); - Runtime.XDecref(baselist); - Runtime.XDecref(dict); - Runtime.XDecref(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.XDecref(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.XDecref(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 /// @@ -509,12 +299,6 @@ public static void SetError(Exception e) } 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.XDecref(etype); diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 98e9b1c2c..782865f93 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -41,20 +41,6 @@ internal static ManagedType GetManagedObject(IntPtr ob) GCHandle gc = (GCHandle)op; return (ManagedType)gc.Target; } - - // In certain situations, we need to recognize a wrapped - // exception class and be willing to unwrap the class :( - - if (Runtime.wrap_exceptions) - { - IntPtr e = Exceptions.UnwrapExceptionClass(ob); - if ((e != IntPtr.Zero) && (e != ob)) - { - ManagedType m = GetManagedObject(e); - Runtime.XDecref(e); - return m; - } - } } return null; } diff --git a/src/runtime/moduleobject.cs b/src/runtime/moduleobject.cs index 84690215f..87e3ed2f1 100644 --- a/src/runtime/moduleobject.cs +++ b/src/runtime/moduleobject.cs @@ -202,20 +202,6 @@ public void LoadNames() if (m == null) { ManagedType attr = this.GetAttribute(name, true); - if (Runtime.wrap_exceptions) - { - if (attr is ExceptionClassObject) - { - ExceptionClassObject c = attr as ExceptionClassObject; - if (c != null) - { - IntPtr p = attr.pyHandle; - IntPtr r = Exceptions.GetExceptionClassWrapper(p); - Runtime.PyDict_SetItemString(dict, name, r); - Runtime.XIncref(r); - } - } - } } } } @@ -307,26 +293,6 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) return IntPtr.Zero; } - // XXX - hack required to recognize exception types. These types - // may need to be wrapped in old-style class wrappers in versions - // of Python where new-style classes cannot be used as exceptions. - - if (Runtime.wrap_exceptions) - { - if (attr is ExceptionClassObject) - { - ExceptionClassObject c = attr as ExceptionClassObject; - if (c != null) - { - IntPtr p = attr.pyHandle; - IntPtr r = Exceptions.GetExceptionClassWrapper(p); - Runtime.PyDict_SetItemString(self.dict, name, r); - Runtime.XIncref(r); - return r; - } - } - } - Runtime.XIncref(attr.pyHandle); return attr.pyHandle; } diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 95ead8b59..6c9087169 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -212,7 +212,6 @@ public class Runtime internal static Object IsFinalizingLock = new Object(); internal static bool IsFinalizing = false; - internal static bool wrap_exceptions; internal static bool is32bit; /// @@ -332,25 +331,6 @@ internal static void Initialize() #endif #endif - - // Determine whether we need to wrap exceptions for versions of - // of the Python runtime that do not allow new-style classes to - // be used as exceptions (Python versions 2.4 and lower). - -#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) - wrap_exceptions = false; -#else - IntPtr m = PyImport_ImportModule("exceptions"); - Exceptions.ErrorCheck(m); - op = Runtime.PyObject_GetAttrString(m, "Exception"); - Exceptions.ErrorCheck(op); - if (Runtime.PyObject_TYPE(op) == PyClassType) { - wrap_exceptions = true; - } - Runtime.XDecref(op); - Runtime.XDecref(m); -#endif - // Initialize modules that depend on the runtime class. AssemblyManager.Initialize(); PyCLRMetaType = MetaType.Initialize(); diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index fcf23a07d..ecac9203c 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -128,25 +128,21 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) // XXX Hack, use a different base class for System.Exception // Python 2.5+ allows new style class exceptions but they *must* // subclass BaseException (or better Exception). -#if (PYTHON25 || PYTHON26 || PYTHON27 || PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if (typeof(System.Exception).IsAssignableFrom(clrType)) { - ob_size = ObjectOffset.Size(Exceptions.BaseException); - tp_dictoffset = ObjectOffset.DictOffset(Exceptions.BaseException); + ob_size = ObjectOffset.Size(Exceptions.Exception); + tp_dictoffset = ObjectOffset.DictOffset(Exceptions.Exception); } if (clrType == typeof(System.Exception)) { base_ = Exceptions.Exception; - Runtime.XIncref(base_); } - else -#endif - if (clrType.BaseType != null) - { - ClassBase bc = ClassManager.GetClass(clrType.BaseType); - base_ = bc.pyHandle; - } + else if (clrType.BaseType != null) + { + ClassBase bc = ClassManager.GetClass(clrType.BaseType); + base_ = bc.pyHandle; + } IntPtr type = AllocateTypeObject(name); From 82fdc393fcfc1c20df297578f37f513e2448c2b8 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 10:52:07 +0100 Subject: [PATCH 2/7] Fix crashes when trying to pickle CLR exceptions. The "args" slot of BaseException was not filled, instead we provided the args through our __getattr__ implementation. This fails in BaseException_reduce which depends on "args" being not NULL. We fix this by explicitly setting the "args" slot on all CLR exception objects on creation. This also makes tp_repr obsolete. --- src/runtime/clrobject.cs | 3 ++ src/runtime/exceptions.cs | 54 ++++++++++++++++++++---------------- src/tests/test_exceptions.py | 15 ++++++---- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 351c7c51b..3fc29bde2 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -30,6 +30,9 @@ internal CLRObject(Object ob, IntPtr tp) : base() this.pyHandle = py; this.gcHandle = gc; inst = ob; + + // Fix the BaseException args slot if wrapping a CLR exception + Exceptions.SetArgs(py); } diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 1652ad794..4026ba427 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -62,30 +62,6 @@ internal static Exception ToException(IntPtr ob) 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 @@ -198,6 +174,36 @@ internal static void Shutdown() } } + /// + /// 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. + /// + /// A CLR exception + /// The python object wrapping + internal static void SetArgs(IntPtr ob) + { + var e = ExceptionClassObject.ToException(ob); + if (e == null) + return; + + 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); + } + + Marshal.WriteIntPtr(ob, ExceptionOffset.args, args); + } + /// /// Shortcut for (pointer == NULL) -> throw PythonException /// diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index 4b6386447..238ead1c6 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -6,11 +6,6 @@ unicode = str -# Note: all of these tests are known to fail because Python currently -# doesn't allow new-style classes to be used as exceptions. I'm leaving -# the tests in place in to document 'how it ought to work' in the hopes -# that they'll all pass one day... - class ExceptionTests(unittest.TestCase): """Test exception support.""" @@ -336,6 +331,16 @@ def testExceptionIsInstanceOfSystemObject(self): else: self.assertFalse(isinstance(o, Object)) + def testPicklingExceptions(self): + from System import Exception + import pickle + + exc = Exception("test") + dumped = pickle.dumps(exc) + loaded = pickle.loads(dumped) + + self.assertEqual(repr(exc), repr(loaded)) + def test_suite(): return unittest.makeSuite(ExceptionTests) From d2bfa49952583deb95564a7e032b1f0c8d26a544 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 12:25:22 +0100 Subject: [PATCH 3/7] Fix Python 2 compatibility. --- src/runtime/interop.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs index f5fbe74b4..7288ff58c 100644 --- a/src/runtime/interop.cs +++ b/src/runtime/interop.cs @@ -89,41 +89,35 @@ static ObjectOffset() public static int magic(IntPtr ob) { -#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) { return ExceptionOffset.ob_data; } -#endif return ob_data; } public static int DictOffset(IntPtr ob) { -#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) { return ExceptionOffset.ob_dict; } -#endif return ob_dict; } public static int Size(IntPtr ob) { -#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) if ((Runtime.PyObject_TypeCheck(ob, Exceptions.BaseException) || (Runtime.PyType_Check(ob) && Runtime.PyType_IsSubtype(ob, Exceptions.BaseException)))) { return ExceptionOffset.Size(); } -#endif #if (Py_DEBUG) return 6 * IntPtr.Size; #else - return 4*IntPtr.Size; + return 4 * IntPtr.Size; #endif } @@ -137,7 +131,6 @@ public static int Size(IntPtr ob) private static int ob_data; } -#if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] internal class ExceptionOffset { @@ -161,15 +154,21 @@ public static int Size() // (start after PyObject_HEAD) public static int dict = 0; public static int args = 0; +#if (PYTHON25 || PYTHON26 || PYTHON27) + public static int message = 0; +#elif (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) public static int traceback = 0; public static int context = 0; public static int cause = 0; +#if !PYTHON32 + public static int suppress_context = 0; +#endif +#endif // extra c# data public static int ob_dict; public static int ob_data; } -#endif #if (PYTHON32 || PYTHON33 || PYTHON34 || PYTHON35) From 1a46d0e9397f5e890010ea807e48769edaf14625 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 14:48:57 +0100 Subject: [PATCH 4/7] Remove access to obsolete message attribute. --- src/runtime/exceptions.cs | 45 +++------------------------------------ 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 4026ba427..ec1b82175 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -55,51 +55,12 @@ internal static Exception ToException(IntPtr ob) { message = e.Message; } - if ((e.StackTrace != null) && (e.StackTrace != String.Empty)) + if (!string.IsNullOrEmpty(e.StackTrace)) { message = message + "\n" + e.StackTrace; } 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); - } } /// @@ -190,10 +151,10 @@ internal static void SetArgs(IntPtr ob) return; IntPtr args; - if (e.Message != String.Empty) + if (!string.IsNullOrEmpty(e.Message)) { args = Runtime.PyTuple_New(1); - IntPtr msg = Runtime.PyUnicode_FromString(e.Message); + var msg = Runtime.PyUnicode_FromString(e.Message); Runtime.PyTuple_SetItem(args, 0, msg); } else From 54868eccc080d7b3c566266b5b0fa9659a09b557 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 14:57:32 +0100 Subject: [PATCH 5/7] Fix the tests. --- src/tests/test_exceptions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index 238ead1c6..818837bd6 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -295,14 +295,15 @@ def testPythonCompatOfManagedExceptions(self): msg = "A simple message" e = OverflowException(msg) - self.assertEqual(e.message, msg) - self.assertTrue(isinstance(e.message, unicode)) # ??? self.assertEqual(str(e), msg) self.assertEqual(unicode(e), msg) self.assertEqual(e.args, (msg,)) self.assertTrue(isinstance(e.args, tuple)) - self.assertEqual(repr(e), "OverflowException('A simple message',)") + if six.PY2: + self.assertEqual(repr(e), "OverflowException(u'A simple message',)") + else: + self.assertEqual(repr(e), "OverflowException('A simple message',)") def testExceptionIsInstanceOfSystemObject(self): """Test behavior of isinstance(, System.Object).""" @@ -339,7 +340,7 @@ def testPicklingExceptions(self): dumped = pickle.dumps(exc) loaded = pickle.loads(dumped) - self.assertEqual(repr(exc), repr(loaded)) + self.assertEqual(exc.args, loaded.args) def test_suite(): From 47c0e1439b46f70d0f073d42b6bebe0bf95a2de3 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 21:18:54 +0100 Subject: [PATCH 6/7] Use cPickle for Python 2. --- src/tests/test_exceptions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index 818837bd6..2585de6f0 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -334,7 +334,10 @@ def testExceptionIsInstanceOfSystemObject(self): def testPicklingExceptions(self): from System import Exception - import pickle + try + import cPickle as pickle + except ImportError: + import pickle exc = Exception("test") dumped = pickle.dumps(exc) From 1af62ea8e417696dd58d3ebf7aae2bdb9d1505a0 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 8 Nov 2016 21:25:56 +0100 Subject: [PATCH 7/7] Fix typo. --- src/tests/test_exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/test_exceptions.py b/src/tests/test_exceptions.py index 2585de6f0..892d6f062 100644 --- a/src/tests/test_exceptions.py +++ b/src/tests/test_exceptions.py @@ -334,7 +334,7 @@ def testExceptionIsInstanceOfSystemObject(self): def testPicklingExceptions(self): from System import Exception - try + try: import cPickle as pickle except ImportError: import pickle