| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 32fdc9c commit 92932fd
22 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,7 @@ details about the cause of the failure | |||
| 31 | 31 | - `PyObject` now implements `IEnumerable<PyObject>` in addition to `IEnumerable` | |
| 32 | 32 | - floating point values passed from Python are no longer silently truncated | |
| 33 | 33 | when .NET expects an integer [#1342][i1342] | |
| 34 | + - More specific error messages for method argument mismatch | ||
| 34 | 35 | ||
| 35 | 36 | ### Fixed | |
| 36 | 37 | ||
@@ -49,6 +50,7 @@ when .NET expects an integer [#1342][i1342] | |||
| 49 | 50 | - Fixed objects returned by enumerating `PyObject` being disposed too soon | |
| 50 | 51 | - Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException | |
| 51 | 52 | - `import` may now raise errors with more detail than "No module named X" | |
| 53 | + - Providing an invalid type parameter to a generic type or method produces a helpful Python error | ||
| 52 | 54 | ||
| 53 | 55 | ### Removed | |
| 54 | 56 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,9 +62,9 @@ public static IntPtr PyInit_clr() | |||
| 62 | 62 | pythonRuntime = Assembly.Load(pythonRuntimeName); | |
| 63 | 63 | DebugPrint("Success loading 'Python.Runtime' using standard binding rules."); | |
| 64 | 64 | } | |
| 65 | - catch (IOException) | ||
| 65 | + catch (IOException ex) | ||
| 66 | 66 | { | |
| 67 | - DebugPrint("'Python.Runtime' not found using standard binding rules."); | ||
| 67 | + DebugPrint($"'Python.Runtime' not found using standard binding rules: {ex}"); | ||
| 68 | 68 | try | |
| 69 | 69 | { | |
| 70 | 70 | // If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -57,6 +57,7 @@ def test_property_visibility_change(): | |||
| 57 | 57 | def test_class_visibility_change(): | |
| 58 | 58 | _run_test("class_visibility_change") | |
| 59 | 59 | ||
| 60 | + @pytest.mark.skip(reason='FIXME: Domain reload fails when Python points to a .NET object which points back to Python objects') | ||
| 60 | 61 | @pytest.mark.skipif(platform.system() == 'Darwin', reason='FIXME: macos can\'t find the python library') | |
| 61 | 62 | def test_method_parameters_change(): | |
| 62 | 63 | _run_test("method_parameters_change") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -97,6 +97,7 @@ public void TestPythonExceptionFormatNormalized() | |||
| 97 | 97 | try | |
| 98 | 98 | { | |
| 99 | 99 | PythonEngine.Exec("a=b\n"); | |
| 100 | + Assert.Fail("Exception should have been raised"); | ||
| 100 | 101 | } | |
| 101 | 102 | catch (PythonException ex) | |
| 102 | 103 | { | |
@@ -135,5 +136,14 @@ def __init__(self, val): | |||
| 135 | 136 | } | |
| 136 | 137 | } | |
| 137 | 138 | } | |
| 139 | + | ||
| 140 | + [Test] | ||
| 141 | + public void TestPythonException_Normalize_ThrowsWhenErrorSet() | ||
| 142 | + { | ||
| 143 | + Exceptions.SetError(Exceptions.TypeError, "Error!"); | ||
| 144 | + var pythonException = new PythonException(); | ||
| 145 | + Exceptions.SetError(Exceptions.TypeError, "Another error"); | ||
| 146 | + Assert.Throws<InvalidOperationException>(() => pythonException.Normalize()); | ||
| 147 | + } | ||
| 138 | 148 | } | |
| 139 | 149 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,7 +66,16 @@ public virtual IntPtr type_subscript(IntPtr idx) | |||
| 66 | 66 | ||
| 67 | 67 | if (target != null) | |
| 68 | 68 | { | |
| 69 | - Type t = target.MakeGenericType(types); | ||
| 69 | + Type t; | ||
| 70 | + try | ||
| 71 | + { | ||
| 72 | + // MakeGenericType can throw ArgumentException | ||
| 73 | + t = target.MakeGenericType(types); | ||
| 74 | + } | ||
| 75 | + catch (ArgumentException e) | ||
| 76 | + { | ||
| 77 | + return Exceptions.RaiseTypeError(e.Message); | ||
| 78 | + } | ||
| 70 | 79 | ManagedType c = ClassManager.GetClass(t); | |
| 71 | 80 | Runtime.XIncref(c.pyHandle); | |
| 72 | 81 | return c.pyHandle; | |
@@ -263,14 +272,14 @@ public static IntPtr tp_iter(IntPtr ob) | |||
| 263 | 272 | /// <summary> | |
| 264 | 273 | /// Standard __hash__ implementation for instances of reflected types. | |
| 265 | 274 | /// </summary> | |
| 266 | - public static IntPtr tp_hash(IntPtr ob) | ||
| 275 | + public static nint tp_hash(IntPtr ob) | ||
| 267 | 276 | { | |
| 268 | 277 | var co = GetManagedObject(ob) as CLRObject; | |
| 269 | 278 | if (co == null) | |
| 270 | 279 | { | |
| 271 | 280 | return Exceptions.RaiseTypeError("unhashable type"); | |
| 272 | 281 | } | |
| 273 | - return new IntPtr(co.inst.GetHashCode()); | ||
| 282 | + return co.inst.GetHashCode(); | ||
| 274 | 283 | } | |
| 275 | 284 | ||
| 276 | 285 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -303,6 +303,11 @@ internal static IntPtr ToPythonImplicit(object value) | |||
| 303 | 303 | /// Return a managed object for the given Python object, taking funny | |
| 304 | 304 | /// byref types into account. | |
| 305 | 305 | /// </summary> | |
| 306 | + /// <param name="value">A Python object</param> | ||
| 307 | + /// <param name="type">The desired managed type</param> | ||
| 308 | + /// <param name="result">Receives the managed object</param> | ||
| 309 | + /// <param name="setError">If true, call <c>Exceptions.SetError</c> with the reason for failure.</param> | ||
| 310 | + /// <returns>True on success</returns> | ||
| 306 | 311 | internal static bool ToManaged(IntPtr value, Type type, | |
| 307 | 312 | out object result, bool setError) | |
| 308 | 313 | { | |
@@ -341,7 +346,10 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 341 | 346 | result = tmp; | |
| 342 | 347 | return true; | |
| 343 | 348 | } | |
| 344 | - Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}"); | ||
| 349 | + if (setError) | ||
| 350 | + { | ||
| 351 | + Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}"); | ||
| 352 | + } | ||
| 345 | 353 | return false; | |
| 346 | 354 | } | |
| 347 | 355 | if (mt is ClassBase) | |
@@ -376,6 +384,15 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 376 | 384 | obType = obType.GetGenericArguments()[0]; | |
| 377 | 385 | } | |
| 378 | 386 | ||
| 387 | + if (obType.ContainsGenericParameters) | ||
| 388 | + { | ||
| 389 | + if (setError) | ||
| 390 | + { | ||
| 391 | + Exceptions.SetError(Exceptions.TypeError, $"Cannot create an instance of the open generic type {obType}"); | ||
| 392 | + } | ||
| 393 | + return false; | ||
| 394 | + } | ||
| 395 | + | ||
| 379 | 396 | if (obType.IsArray) | |
| 380 | 397 | { | |
| 381 | 398 | return ToArray(value, obType, out result, setError); | |
@@ -777,7 +794,7 @@ private static void SetConversionError(IntPtr value, Type target) | |||
| 777 | 794 | IntPtr ob = Runtime.PyObject_Repr(value); | |
| 778 | 795 | string src = Runtime.GetManagedString(ob); | |
| 779 | 796 | Runtime.XDecref(ob); | |
| 780 | - Exceptions.SetError(Exceptions.TypeError, $"Cannot convert {src} to {target}"); | ||
| 797 | + Exceptions.RaiseTypeError($"Cannot convert {src} to {target}"); | ||
| 781 | 798 | } | |
| 782 | 799 | ||
| 783 | 800 | ||
@@ -791,32 +808,58 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s | |||
| 791 | 808 | Type elementType = obType.GetElementType(); | |
| 792 | 809 | result = null; | |
| 793 | 810 | ||
| 794 | - bool IsSeqObj = Runtime.PySequence_Check(value); | ||
| 795 | - var len = IsSeqObj ? Runtime.PySequence_Size(value) : -1; | ||
| 796 | - | ||
| 797 | 811 | IntPtr IterObject = Runtime.PyObject_GetIter(value); | |
| 798 | - | ||
| 799 | - if(IterObject==IntPtr.Zero) { | ||
| 812 | + if (IterObject == IntPtr.Zero) | ||
| 813 | + { | ||
| 800 | 814 | if (setError) | |
| 801 | 815 | { | |
| 802 | 816 | SetConversionError(value, obType); | |
| 803 | 817 | } | |
| 818 | + else | ||
| 819 | + { | ||
| 820 | + // PyObject_GetIter will have set an error | ||
| 821 | + Exceptions.Clear(); | ||
| 822 | + } | ||
| 804 | 823 | return false; | |
| 805 | 824 | } | |
| 806 | 825 | ||
| 807 | - Array items; | ||
| 826 | + IList list; | ||
| 827 | + try | ||
| 828 | + { | ||
| 829 | + // MakeGenericType can throw because elementType may not be a valid generic argument even though elementType[] is a valid array type. | ||
| 830 | + // For example, if elementType is a pointer type. | ||
| 831 | + // See https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype#System_Type_MakeGenericType_System_Type | ||
| 832 | + var constructedListType = typeof(List<>).MakeGenericType(elementType); | ||
| 833 | + bool IsSeqObj = Runtime.PySequence_Check(value); | ||
| 834 | + if (IsSeqObj) | ||
| 835 | + { | ||
| 836 | + var len = Runtime.PySequence_Size(value); | ||
| 837 | + list = (IList)Activator.CreateInstance(constructedListType, new Object[] { (int)len }); | ||
| 838 | + } | ||
| 839 | + else | ||
| 840 | + { | ||
| 841 | + // CreateInstance can throw even if MakeGenericType succeeded. | ||
| 842 | + // See https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance#System_Activator_CreateInstance_System_Type_ | ||
| 843 | + list = (IList)Activator.CreateInstance(constructedListType); | ||
| 844 | + } | ||
| 845 | + } | ||
| 846 | + catch (Exception e) | ||
| 847 | + { | ||
| 848 | + if (setError) | ||
| 849 | + { | ||
| 850 | + Exceptions.SetError(e); | ||
| 851 | + SetConversionError(value, obType); | ||
| 852 | + } | ||
| 853 | + return false; | ||
| 854 | + } | ||
| 808 | 855 | ||
| 809 | - var listType = typeof(List<>); | ||
| 810 | - var constructedListType = listType.MakeGenericType(elementType); | ||
| 811 | - IList list = IsSeqObj ? (IList) Activator.CreateInstance(constructedListType, new Object[] {(int) len}) : | ||
| 812 | - (IList) Activator.CreateInstance(constructedListType); | ||
| 813 | 856 | IntPtr item; | |
| 814 | 857 | ||
| 815 | 858 | while ((item = Runtime.PyIter_Next(IterObject)) != IntPtr.Zero) | |
| 816 | 859 | { | |
| 817 | - object obj = null; | ||
| 860 | + object obj; | ||
| 818 | 861 | ||
| 819 | - if (!Converter.ToManaged(item, elementType, out obj, true)) | ||
| 862 | + if (!Converter.ToManaged(item, elementType, out obj, setError)) | ||
| 820 | 863 | { | |
| 821 | 864 | Runtime.XDecref(item); | |
| 822 | 865 | return false; | |
@@ -827,7 +870,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s | |||
| 827 | 870 | } | |
| 828 | 871 | Runtime.XDecref(IterObject); | |
| 829 | 872 | ||
| 830 | - items = Array.CreateInstance(elementType, list.Count); | ||
| 873 | + Array items = Array.CreateInstance(elementType, list.Count); | ||
| 831 | 874 | list.CopyTo(items, 0); | |
| 832 | 875 | ||
| 833 | 876 | result = items; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,35 +68,27 @@ public static IntPtr nb_inplace_subtract(IntPtr ob, IntPtr arg) | |||
| 68 | 68 | /// <summary> | |
| 69 | 69 | /// EventBinding __hash__ implementation. | |
| 70 | 70 | /// </summary> | |
| 71 | - public static IntPtr tp_hash(IntPtr ob) | ||
| 71 | + public static nint tp_hash(IntPtr ob) | ||
| 72 | 72 | { | |
| 73 | 73 | var self = (EventBinding)GetManagedObject(ob); | |
| 74 | - long x = 0; | ||
| 75 | - long y = 0; | ||
| 74 | + nint x = 0; | ||
| 76 | 75 | ||
| 77 | 76 | if (self.target != IntPtr.Zero) | |
| 78 | 77 | { | |
| 79 | - x = Runtime.PyObject_Hash(self.target).ToInt64(); | ||
| 78 | + x = Runtime.PyObject_Hash(self.target); | ||
| 80 | 79 | if (x == -1) | |
| 81 | 80 | { | |
| 82 | - return new IntPtr(-1); | ||
| 81 | + return x; | ||
| 83 | 82 | } | |
| 84 | 83 | } | |
| 85 | 84 | ||
| 86 | - y = Runtime.PyObject_Hash(self.e.pyHandle).ToInt64(); | ||
| 85 | + nint y = Runtime.PyObject_Hash(self.e.pyHandle); | ||
| 87 | 86 | if (y == -1) | |
| 88 | 87 | { | |
| 89 | - return new IntPtr(-1); | ||
| 88 | + return y; | ||
| 90 | 89 | } | |
| 91 | 90 | ||
| 92 | - x ^= y; | ||
| 93 | - | ||
| 94 | - if (x == -1) | ||
| 95 | - { | ||
| 96 | - x = -1; | ||
| 97 | - } | ||
| 98 | - | ||
| 99 | - return new IntPtr(x); | ||
| 91 | + return x ^ y; | ||
| 100 | 92 | } | |
| 101 | 93 | ||
| 102 | 94 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -72,17 +72,22 @@ internal bool AddEventHandler(IntPtr target, IntPtr handler) | |||
| 72 | 72 | /// </summary> | |
| 73 | 73 | internal bool RemoveEventHandler(IntPtr target, IntPtr handler) | |
| 74 | 74 | { | |
| 75 | + if (reg == null) | ||
| 76 | + { | ||
| 77 | + Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); | ||
| 78 | + return false; | ||
| 79 | + } | ||
| 80 | + | ||
| 75 | 81 | object obj = null; | |
| 76 | 82 | if (target != IntPtr.Zero) | |
| 77 | 83 | { | |
| 78 | 84 | var co = (CLRObject)GetManagedObject(target); | |
| 79 | 85 | obj = co.inst; | |
| 80 | 86 | } | |
| 81 | 87 | ||
| 82 | - IntPtr hash = Runtime.PyObject_Hash(handler); | ||
| 83 | - if (Exceptions.ErrorOccurred() || reg == null) | ||
| 88 | + nint hash = Runtime.PyObject_Hash(handler); | ||
| 89 | + if (hash == -1 && Exceptions.ErrorOccurred()) | ||
| 84 | 90 | { | |
| 85 | - Exceptions.SetError(Exceptions.ValueError, "unknown event handler"); | ||
| 86 | 91 | return false; | |
| 87 | 92 | } | |
| 88 | 93 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,7 @@ | |||
| 1 | 1 | using System; | |
| 2 | 2 | using System.Reflection; | |
| 3 | 3 | using System.Runtime.InteropServices; | |
| 4 | + using System.Text; | ||
| 4 | 5 | ||
| 5 | 6 | namespace Python.Runtime | |
| 6 | 7 | { | |
@@ -71,14 +72,16 @@ internal static Exception ToException(IntPtr ob) | |||
| 71 | 72 | return Exceptions.RaiseTypeError("invalid object"); | |
| 72 | 73 | } | |
| 73 | 74 | ||
| 74 | - string message = string.Empty; | ||
| 75 | - if (e.Message != string.Empty) | ||
| 75 | + string message = e.ToString(); | ||
| 76 | + string fullTypeName = e.GetType().FullName; | ||
| 77 | + string prefix = fullTypeName + ": "; | ||
| 78 | + if (message.StartsWith(prefix)) | ||
| 76 | 79 | { | |
| 77 | - message = e.Message; | ||
| 80 | + message = message.Substring(prefix.Length); | ||
| 78 | 81 | } | |
| 79 | - if (!string.IsNullOrEmpty(e.StackTrace)) | ||
| 82 | + else if (message.StartsWith(fullTypeName)) | ||
| 80 | 83 | { | |
| 81 | - message = message + "\n" + e.StackTrace; | ||
| 84 | + message = message.Substring(fullTypeName.Length); | ||
| 82 | 85 | } | |
| 83 | 86 | return Runtime.PyUnicode_FromString(message); | |
| 84 | 87 | } | |
@@ -181,6 +184,7 @@ internal static void SetArgsAndCause(IntPtr ob) | |||
| 181 | 184 | ||
| 182 | 185 | if (e.InnerException != null) | |
| 183 | 186 | { | |
| 187 | + // Note: For an AggregateException, InnerException is only the first of the InnerExceptions. | ||
| 184 | 188 | IntPtr cause = CLRObject.GetInstHandle(e.InnerException); | |
| 185 | 189 | Marshal.WriteIntPtr(ob, ExceptionOffset.cause, cause); | |
| 186 | 190 | } | |
@@ -290,6 +294,20 @@ public static void SetError(Exception e) | |||
| 290 | 294 | Runtime.XDecref(op); | |
| 291 | 295 | } | |
| 292 | 296 | ||
| 297 | + /// <summary> | ||
| 298 | + /// When called after SetError, sets the cause of the error. | ||
| 299 | + /// </summary> | ||
| 300 | + /// <param name="cause">The cause of the current error</param> | ||
| 301 | + public static void SetCause(PythonException cause) | ||
| 302 | + { | ||
| 303 | + var currentException = new PythonException(); | ||
| 304 | + currentException.Normalize(); | ||
| 305 | + cause.Normalize(); | ||
| 306 | + Runtime.XIncref(cause.PyValue); | ||
| 307 | + Runtime.PyException_SetCause(currentException.PyValue, cause.PyValue); | ||
| 308 | + currentException.Restore(); | ||
| 309 | + } | ||
| 310 | + | ||
| 293 | 311 | /// <summary> | |
| 294 | 312 | /// ErrorOccurred Method | |
| 295 | 313 | /// </summary> | |
@@ -368,17 +386,31 @@ public static void deprecation(string message) | |||
| 368 | 386 | // Internal helper methods for common error handling scenarios. | |
| 369 | 387 | //==================================================================== | |
| 370 | 388 | ||
| 389 | + /// <summary> | ||
| 390 | + /// Raises a TypeError exception and attaches any existing exception as its cause. | ||
| 391 | + /// </summary> | ||
| 392 | + /// <param name="message">The exception message</param> | ||
| 393 | + /// <returns><c>IntPtr.Zero</c></returns> | ||
| 371 | 394 | internal static IntPtr RaiseTypeError(string message) | |
| 372 | 395 | { | |
| 396 | + PythonException previousException = null; | ||
| 397 | + if (ErrorOccurred()) | ||
| 398 | + { | ||
| 399 | + previousException = new PythonException(); | ||
| 400 | + } | ||
| 373 | 401 | Exceptions.SetError(Exceptions.TypeError, message); | |
| 402 | + if (previousException != null) | ||
| 403 | + { | ||
| 404 | + SetCause(previousException); | ||
| 405 | + } | ||
| 374 | 406 | return IntPtr.Zero; | |
| 375 | 407 | } | |
| 376 | 408 | ||
| 377 | 409 | // 2010-11-16: Arranged in python (2.6 & 2.7) source header file order | |
| 378 | 410 | /* Predefined exceptions are | |
| 379 | - puplic static variables on the Exceptions class filled in from | ||
| 411 | + public static variables on the Exceptions class filled in from | ||
| 380 | 412 | the python class using reflection in Initialize() looked up by | |
| 381 | - name, not posistion. */ | ||
| 413 | + name, not position. */ | ||
| 382 | 414 | public static IntPtr BaseException; | |
| 383 | 415 | public static IntPtr Exception; | |
| 384 | 416 | public static IntPtr StopIteration; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments