| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 8d93c39 commit 682fcce
12 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -71,6 +71,9 @@ See [Mixins/collections.py](src/runtime/Mixins/collections.py). | |||
| 71 | 71 | - BREAKING: When trying to convert Python `int` to `System.Object`, result will | |
| 72 | 72 | be of type `PyInt` instead of `System.Int32` due to possible loss of information. | |
| 73 | 73 | Python `float` will continue to be converted to `System.Double`. | |
| 74 | + - BREAKING: Python.NET will no longer implicitly convert types like `numpy.float64`, that implement `__float__` to | ||
| 75 | + `System.Single` and `System.Double`. An explicit conversion is required on Python or .NET side. | ||
| 76 | + - BREAKING: Python.NET will no longer implicitly convert any Python object to `System.Boolean`. | ||
| 74 | 77 | - BREAKING: `PyObject.GetAttr(name, default)` now only ignores `AttributeError` (previously ignored all exceptions). | |
| 75 | 78 | - BREAKING: `PyObject` no longer implements `IEnumerable<PyObject>`. | |
| 76 | 79 | Instead, `PyIterable` does that. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,7 +77,7 @@ Example | |||
| 77 | 77 | dynamic sin = np.sin; | |
| 78 | 78 | Console.WriteLine(sin(5)); | |
| 79 | 79 | ||
| 80 | - double c = np.cos(5) + sin(5); | ||
| 80 | + double c = (double)(np.cos(5) + sin(5)); | ||
| 81 | 81 | Console.WriteLine(c); | |
| 82 | 82 | ||
| 83 | 83 | dynamic a = np.array(new List<float> { 1, 2, 3 }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,7 +40,7 @@ public void TestReadme() | |||
| 40 | 40 | dynamic sin = np.sin; | |
| 41 | 41 | StringAssert.StartsWith("-0.95892", sin(5).ToString()); | |
| 42 | 42 | ||
| 43 | - double c = np.cos(5) + sin(5); | ||
| 43 | + double c = (double)(np.cos(5) + sin(5)); | ||
| 44 | 44 | Assert.AreEqual(-0.675262, c, 0.01); | |
| 45 | 45 | ||
| 46 | 46 | dynamic a = np.array(new List<float> { 1, 2, 3 }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -116,6 +116,13 @@ public void ConvertOverflow() | |||
| 116 | 116 | } | |
| 117 | 117 | } | |
| 118 | 118 | ||
| 119 | + [Test] | ||
| 120 | + public void NoImplicitConversionToBool() | ||
| 121 | + { | ||
| 122 | + var pyObj = new PyList(items: new[] { 1.ToPython(), 2.ToPython() }).ToPython(); | ||
| 123 | + Assert.Throws<InvalidCastException>(() => pyObj.As<bool>()); | ||
| 124 | + } | ||
| 125 | + | ||
| 119 | 126 | [Test] | |
| 120 | 127 | public void ToNullable() | |
| 121 | 128 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -33,8 +33,8 @@ public void Dispose() | |||
| 33 | 33 | static IEnumerable<string[]> PythonTestCases() | |
| 34 | 34 | { | |
| 35 | 35 | // Add the test that you want to debug here. | |
| 36 | - yield return new[] { "test_enum", "test_enum_standard_attrs" }; | ||
| 37 | - yield return new[] { "test_generic", "test_missing_generic_type" }; | ||
| 36 | + yield return new[] { "test_indexer", "test_boolean_indexer" }; | ||
| 37 | + yield return new[] { "test_delegate", "test_bool_delegate" }; | ||
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | 40 | /// <summary> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | using System; | |
| 3 | 3 | using System.Collections; | |
| 4 | 4 | using System.Collections.Generic; | |
| 5 | + using System.Diagnostics; | ||
| 5 | 6 | using System.Globalization; | |
| 6 | 7 | using System.Reflection; | |
| 7 | 8 | using System.Runtime.InteropServices; | |
@@ -501,6 +502,44 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 501 | 502 | return ToPrimitive(value, obType, out result, setError); | |
| 502 | 503 | } | |
| 503 | 504 | ||
| 505 | + /// <remarks> | ||
| 506 | + /// Unlike <see cref="ToManaged(BorrowedReference, Type, out object?, bool)"/>, | ||
| 507 | + /// this method does not have a <c>setError</c> parameter, because it should | ||
| 508 | + /// only be called after <see cref="ToManaged(BorrowedReference, Type, out object?, bool)"/>. | ||
| 509 | + /// </remarks> | ||
| 510 | + internal static bool ToManagedExplicit(BorrowedReference value, Type obType, | ||
| 511 | + out object? result) | ||
| 512 | + { | ||
| 513 | + result = null; | ||
| 514 | + | ||
| 515 | + // this method would potentially clean any existing error resulting in information loss | ||
| 516 | + Debug.Assert(Runtime.PyErr_Occurred() == null); | ||
| 517 | + | ||
| 518 | + string? converterName = | ||
| 519 | + IsInteger(obType) ? "__int__" | ||
| 520 | + : IsFloatingNumber(obType) ? "__float__" | ||
| 521 | + : null; | ||
| 522 | + | ||
| 523 | + if (converterName is null) return false; | ||
| 524 | + | ||
| 525 | + Debug.Assert(obType.IsPrimitive); | ||
| 526 | + | ||
| 527 | + using var converter = Runtime.PyObject_GetAttrString(value, converterName); | ||
| 528 | + if (converter.IsNull()) | ||
| 529 | + { | ||
| 530 | + Exceptions.Clear(); | ||
| 531 | + return false; | ||
| 532 | + } | ||
| 533 | + | ||
| 534 | + using var explicitlyCoerced = Runtime.PyObject_CallObject(converter, BorrowedReference.Null); | ||
| 535 | + if (explicitlyCoerced.IsNull()) | ||
| 536 | + { | ||
| 537 | + Exceptions.Clear(); | ||
| 538 | + return false; | ||
| 539 | + } | ||
| 540 | + return ToPrimitive(explicitlyCoerced, obType, out result, false); | ||
| 541 | + } | ||
| 542 | + | ||
| 504 | 543 | static object? ToPyObjectSubclass(ConstructorInfo ctor, PyObject instance, bool setError) | |
| 505 | 544 | { | |
| 506 | 545 | try | |
@@ -544,6 +583,8 @@ internal static int ToInt32(BorrowedReference value) | |||
| 544 | 583 | return checked((int)num); | |
| 545 | 584 | } | |
| 546 | 585 | ||
| 586 | + private static bool ToPrimitive(BorrowedReference value, Type obType, out object? result, bool setError) | ||
| 587 | + => ToPrimitive(value.DangerousGetAddress(), obType, out result, setError); | ||
| 547 | 588 | /// <summary> | |
| 548 | 589 | /// Convert a Python value to an instance of a primitive managed type. | |
| 549 | 590 | /// </summary> | |
@@ -590,7 +631,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object? result, b | |||
| 590 | 631 | } | |
| 591 | 632 | ||
| 592 | 633 | case TypeCode.Boolean: | |
| 593 | - result = Runtime.PyObject_IsTrue(value) != 0; | ||
| 634 | + if (value == Runtime.PyTrue) | ||
| 635 | + { | ||
| 636 | + result = true; | ||
| 637 | + } | ||
| 638 | + else if (value == Runtime.PyFalse) | ||
| 639 | + { | ||
| 640 | + result = false; | ||
| 641 | + } | ||
| 642 | + else if (setError) | ||
| 643 | + { | ||
| 644 | + goto type_error; | ||
| 645 | + } | ||
| 594 | 646 | return true; | |
| 595 | 647 | ||
| 596 | 648 | case TypeCode.Byte: | |
@@ -768,6 +820,10 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object? result, b | |||
| 768 | 820 | ||
| 769 | 821 | case TypeCode.Single: | |
| 770 | 822 | { | |
| 823 | + if (!Runtime.PyFloat_Check(value) && !Runtime.PyInt_Check(value)) | ||
| 824 | + { | ||
| 825 | + goto type_error; | ||
| 826 | + } | ||
| 771 | 827 | double num = Runtime.PyFloat_AsDouble(value); | |
| 772 | 828 | if (num == -1.0 && Exceptions.ErrorOccurred()) | |
| 773 | 829 | { | |
@@ -786,6 +842,10 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object? result, b | |||
| 786 | 842 | ||
| 787 | 843 | case TypeCode.Double: | |
| 788 | 844 | { | |
| 845 | + if (!Runtime.PyFloat_Check(value) && !Runtime.PyInt_Check(value)) | ||
| 846 | + { | ||
| 847 | + goto type_error; | ||
| 848 | + } | ||
| 789 | 849 | double num = Runtime.PyFloat_AsDouble(value); | |
| 790 | 850 | if (num == -1.0 && Exceptions.ErrorOccurred()) | |
| 791 | 851 | { | |
@@ -933,6 +993,13 @@ private static bool ToArray(IntPtr value, Type obType, out object? result, bool | |||
| 933 | 993 | result = items; | |
| 934 | 994 | return true; | |
| 935 | 995 | } | |
| 996 | + | ||
| 997 | + internal static bool IsFloatingNumber(Type type) => type == typeof(float) || type == typeof(double); | ||
| 998 | + internal static bool IsInteger(Type type) | ||
| 999 | + => type == typeof(Byte) || type == typeof(SByte) | ||
| 1000 | + || type == typeof(Int16) || type == typeof(UInt16) | ||
| 1001 | + || type == typeof(Int32) || type == typeof(UInt32) | ||
| 1002 | + || type == typeof(Int64) || type == typeof(UInt64); | ||
| 936 | 1003 | } | |
| 937 | 1004 | ||
| 938 | 1005 | public static class ConverterExtension | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1293,7 +1293,21 @@ public override bool TryInvoke(InvokeBinder binder, object[] args, out object re | |||
| 1293 | 1293 | ||
| 1294 | 1294 | public override bool TryConvert(ConvertBinder binder, out object result) | |
| 1295 | 1295 | { | |
| 1296 | - return Converter.ToManaged(this.obj, binder.Type, out result, false); | ||
| 1296 | + // always try implicit conversion first | ||
| 1297 | + if (Converter.ToManaged(this.obj, binder.Type, out result, false)) | ||
| 1298 | + { | ||
| 1299 | + return true; | ||
| 1300 | + } | ||
| 1301 | + | ||
| 1302 | + if (binder.Explicit) | ||
| 1303 | + { | ||
| 1304 | + Runtime.PyErr_Fetch(out var errType, out var errValue, out var tb); | ||
| 1305 | + bool converted = Converter.ToManagedExplicit(Reference, binder.Type, out result); | ||
| 1306 | + Runtime.PyErr_Restore(errType.StealNullable(), errValue.StealNullable(), tb.StealNullable()); | ||
| 1307 | + return converted; | ||
| 1308 | + } | ||
| 1309 | + | ||
| 1310 | + return false; | ||
| 1297 | 1311 | } | |
| 1298 | 1312 | ||
| 1299 | 1313 | public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,53 +13,36 @@ def test_bool_conversion(): | |||
| 13 | 13 | """Test bool conversion.""" | |
| 14 | 14 | ob = ConversionTest() | |
| 15 | 15 | assert ob.BooleanField is False | |
| 16 | - assert ob.BooleanField is False | ||
| 17 | 16 | assert ob.BooleanField == 0 | |
| 18 | 17 | ||
| 19 | 18 | ob.BooleanField = True | |
| 20 | 19 | assert ob.BooleanField is True | |
| 21 | - assert ob.BooleanField is True | ||
| 22 | 20 | assert ob.BooleanField == 1 | |
| 23 | 21 | ||
| 24 | 22 | ob.BooleanField = False | |
| 25 | 23 | assert ob.BooleanField is False | |
| 26 | - assert ob.BooleanField is False | ||
| 27 | 24 | assert ob.BooleanField == 0 | |
| 28 | 25 | ||
| 29 | - ob.BooleanField = 1 | ||
| 30 | - assert ob.BooleanField is True | ||
| 31 | - assert ob.BooleanField is True | ||
| 32 | - assert ob.BooleanField == 1 | ||
| 33 | - | ||
| 34 | - ob.BooleanField = 0 | ||
| 35 | - assert ob.BooleanField is False | ||
| 36 | - assert ob.BooleanField is False | ||
| 37 | - assert ob.BooleanField == 0 | ||
| 26 | + with pytest.raises(TypeError): | ||
| 27 | + ob.BooleanField = 1 | ||
| 28 | + | ||
| 29 | + with pytest.raises(TypeError): | ||
| 30 | + ob.BooleanField = 0 | ||
| 38 | 31 | ||
| 39 | - ob.BooleanField = System.Boolean(None) | ||
| 40 | - assert ob.BooleanField is False | ||
| 41 | - assert ob.BooleanField is False | ||
| 42 | - assert ob.BooleanField == 0 | ||
| 32 | + with pytest.raises(TypeError): | ||
| 33 | + ob.BooleanField = None | ||
| 43 | 34 | ||
| 44 | - ob.BooleanField = System.Boolean('') | ||
| 45 | - assert ob.BooleanField is False | ||
| 46 | - assert ob.BooleanField is False | ||
| 47 | - assert ob.BooleanField == 0 | ||
| 35 | + with pytest.raises(TypeError): | ||
| 36 | + ob.BooleanField = '' | ||
| 48 | 37 | ||
| 49 | - ob.BooleanField = System.Boolean(0) | ||
| 50 | - assert ob.BooleanField is False | ||
| 51 | - assert ob.BooleanField is False | ||
| 52 | - assert ob.BooleanField == 0 | ||
| 38 | + with pytest.raises(TypeError): | ||
| 39 | + ob.BooleanField = System.Boolean(0) | ||
| 53 | 40 | ||
| 54 | - ob.BooleanField = System.Boolean(1) | ||
| 55 | - assert ob.BooleanField is True | ||
| 56 | - assert ob.BooleanField is True | ||
| 57 | - assert ob.BooleanField == 1 | ||
| 41 | + with pytest.raises(TypeError): | ||
| 42 | + ob.BooleanField = System.Boolean(1) | ||
| 58 | 43 | ||
| 59 | - ob.BooleanField = System.Boolean('a') | ||
| 60 | - assert ob.BooleanField is True | ||
| 61 | - assert ob.BooleanField is True | ||
| 62 | - assert ob.BooleanField == 1 | ||
| 44 | + with pytest.raises(TypeError): | ||
| 45 | + ob.BooleanField = System.Boolean('a') | ||
| 63 | 46 | ||
| 64 | 47 | ||
| 65 | 48 | def test_sbyte_conversion(): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -247,7 +247,7 @@ def test_bool_delegate(): | |||
| 247 | 247 | from Python.Test import BoolDelegate | |
| 248 | 248 | ||
| 249 | 249 | def always_so_negative(): | |
| 250 | - return 0 | ||
| 250 | + return False | ||
| 251 | 251 | ||
| 252 | 252 | d = BoolDelegate(always_so_negative) | |
| 253 | 253 | ob = DelegateTest() | |
@@ -256,6 +256,12 @@ def always_so_negative(): | |||
| 256 | 256 | assert not d() | |
| 257 | 257 | assert not ob.CallBoolDelegate(d) | |
| 258 | 258 | ||
| 259 | + def always_so_positive(): | ||
| 260 | + return 1 | ||
| 261 | + bad = BoolDelegate(always_so_positive) | ||
| 262 | + with pytest.raises(TypeError): | ||
| 263 | + ob.CallBoolDelegate(bad) | ||
| 264 | + | ||
| 259 | 265 | def test_object_delegate(): | |
| 260 | 266 | """Test object delegate.""" | |
| 261 | 267 | from Python.Test import ObjectDelegate | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -200,11 +200,11 @@ def test_boolean_field(): | |||
| 200 | 200 | ob.BooleanField = False | |
| 201 | 201 | assert ob.BooleanField is False | |
| 202 | 202 | ||
| 203 | - ob.BooleanField = 1 | ||
| 204 | - assert ob.BooleanField is True | ||
| 203 | + with pytest.raises(TypeError): | ||
| 204 | + ob.BooleanField = 1 | ||
| 205 | 205 | ||
| 206 | - ob.BooleanField = 0 | ||
| 207 | - assert ob.BooleanField is False | ||
| 206 | + with pytest.raises(TypeError): | ||
| 207 | + ob.BooleanField = 0 | ||
| 208 | 208 | ||
| 209 | 209 | ||
| 210 | 210 | def test_sbyte_field(): | |
| Back | FazBrowse Home | New Git URL |
0 commit comments