| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 95d5e35 commit 6379221
17 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,6 +38,9 @@ when .NET expects an integer [#1342][i1342] | |||
| 38 | 38 | - BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name | |
| 39 | 39 | or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions. | |
| 40 | 40 | - BREAKING: `PyObject.Length()` now raises a `PythonException` when object does not support a concept of length. | |
| 41 | + - BREAKING: disabled implicit conversion from C# enums to Python `int` and back. | ||
| 42 | + One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor | ||
| 43 | + (e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42). | ||
| 41 | 44 | - Sign Runtime DLL with a strong name | |
| 42 | 45 | - Implement loading through `clr_loader` instead of the included `ClrModule`, enables | |
| 43 | 46 | support for .NET Core | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -335,6 +335,14 @@ public void SymmetricalOperatorOverloads() | |||
| 335 | 335 | "); | |
| 336 | 336 | } | |
| 337 | 337 | ||
| 338 | + [Test] | ||
| 339 | + public void EnumOperator() | ||
| 340 | + { | ||
| 341 | + PythonEngine.Exec($@" | ||
| 342 | + from System.IO import FileAccess | ||
| 343 | + c = FileAccess.Read | FileAccess.Write"); | ||
| 344 | + } | ||
| 345 | + | ||
| 338 | 346 | [Test] | |
| 339 | 347 | public void OperatorOverloadMissingArgument() | |
| 340 | 348 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,68 @@ | |||
| 1 | + using System; | ||
| 2 | + | ||
| 3 | + namespace Python.Runtime.Codecs | ||
| 4 | + { | ||
| 5 | + [Obsolete] | ||
| 6 | + public sealed class EnumPyLongCodec : IPyObjectEncoder, IPyObjectDecoder | ||
| 7 | + { | ||
| 8 | + public static EnumPyLongCodec Instance { get; } = new EnumPyLongCodec(); | ||
| 9 | + | ||
| 10 | + public bool CanDecode(PyObject objectType, Type targetType) | ||
| 11 | + { | ||
| 12 | + return targetType.IsEnum | ||
| 13 | + && objectType.IsSubclass(new BorrowedReference(Runtime.PyLongType)); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + public bool CanEncode(Type type) | ||
| 17 | + { | ||
| 18 | + return type == typeof(object) || type == typeof(ValueType) || type.IsEnum; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public bool TryDecode<T>(PyObject pyObj, out T value) | ||
| 22 | + { | ||
| 23 | + value = default; | ||
| 24 | + if (!typeof(T).IsEnum) return false; | ||
| 25 | + | ||
| 26 | + Type etype = Enum.GetUnderlyingType(typeof(T)); | ||
| 27 | + | ||
| 28 | + if (!PyLong.IsLongType(pyObj)) return false; | ||
| 29 | + | ||
| 30 | + object result; | ||
| 31 | + try | ||
| 32 | + { | ||
| 33 | + result = pyObj.AsManagedObject(etype); | ||
| 34 | + } | ||
| 35 | + catch (InvalidCastException) | ||
| 36 | + { | ||
| 37 | + return false; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + if (Enum.IsDefined(typeof(T), result) || typeof(T).IsFlagsEnum()) | ||
| 41 | + { | ||
| 42 | + value = (T)Enum.ToObject(typeof(T), result); | ||
| 43 | + return true; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + return false; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public PyObject TryEncode(object value) | ||
| 50 | + { | ||
| 51 | + if (value is null) return null; | ||
| 52 | + | ||
| 53 | + var enumType = value.GetType(); | ||
| 54 | + if (!enumType.IsEnum) return null; | ||
| 55 | + | ||
| 56 | + try | ||
| 57 | + { | ||
| 58 | + return new PyLong((long)value); | ||
| 59 | + } | ||
| 60 | + catch (InvalidCastException) | ||
| 61 | + { | ||
| 62 | + return new PyLong((ulong)value); | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + private EnumPyLongCodec() { } | ||
| 67 | + } | ||
| 68 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -403,6 +403,17 @@ private static ClassInfo GetClassInfo(Type type) | |||
| 403 | 403 | } | |
| 404 | 404 | } | |
| 405 | 405 | ||
| 406 | + // only [Flags] enums support bitwise operations | ||
| 407 | + if (type.IsEnum && type.IsFlagsEnum()) | ||
| 408 | + { | ||
| 409 | + var opsImpl = typeof(EnumOps<>).MakeGenericType(type); | ||
| 410 | + foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags)) | ||
| 411 | + { | ||
| 412 | + local[op.Name] = 1; | ||
| 413 | + } | ||
| 414 | + info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray(); | ||
| 415 | + } | ||
| 416 | + | ||
| 406 | 417 | // Now again to filter w/o losing overloaded member info | |
| 407 | 418 | for (i = 0; i < info.Length; i++) | |
| 408 | 419 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -50,8 +50,9 @@ internal NewReference GetDocString() | |||
| 50 | 50 | /// <summary> | |
| 51 | 51 | /// Implements __new__ for reflected classes and value types. | |
| 52 | 52 | /// </summary> | |
| 53 | - public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) | ||
| 53 | + public static IntPtr tp_new(IntPtr tpRaw, IntPtr args, IntPtr kw) | ||
| 54 | 54 | { | |
| 55 | + var tp = new BorrowedReference(tpRaw); | ||
| 55 | 56 | var self = GetManagedObject(tp) as ClassObject; | |
| 56 | 57 | ||
| 57 | 58 | // Sanity check: this ensures a graceful error if someone does | |
@@ -87,7 +88,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) | |||
| 87 | 88 | return IntPtr.Zero; | |
| 88 | 89 | } | |
| 89 | 90 | ||
| 90 | - return CLRObject.GetInstHandle(result, tp); | ||
| 91 | + return CLRObject.GetInstHandle(result, tp).DangerousMoveToPointerOrNull(); | ||
| 91 | 92 | } | |
| 92 | 93 | ||
| 93 | 94 | if (type.IsAbstract) | |
@@ -98,8 +99,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) | |||
| 98 | 99 | ||
| 99 | 100 | if (type.IsEnum) | |
| 100 | 101 | { | |
| 101 | - Exceptions.SetError(Exceptions.TypeError, "cannot instantiate enumeration"); | ||
| 102 | - return IntPtr.Zero; | ||
| 102 | + return NewEnum(type, new BorrowedReference(args), tp).DangerousMoveToPointerOrNull(); | ||
| 103 | 103 | } | |
| 104 | 104 | ||
| 105 | 105 | object obj = self.binder.InvokeRaw(IntPtr.Zero, args, kw); | |
@@ -108,7 +108,44 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) | |||
| 108 | 108 | return IntPtr.Zero; | |
| 109 | 109 | } | |
| 110 | 110 | ||
| 111 | - return CLRObject.GetInstHandle(obj, tp); | ||
| 111 | + return CLRObject.GetInstHandle(obj, tp).DangerousMoveToPointerOrNull(); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + private static NewReference NewEnum(Type type, BorrowedReference args, BorrowedReference tp) | ||
| 115 | + { | ||
| 116 | + nint argCount = Runtime.PyTuple_Size(args); | ||
| 117 | + bool allowUnchecked = false; | ||
| 118 | + if (argCount == 2) | ||
| 119 | + { | ||
| 120 | + var allow = Runtime.PyTuple_GetItem(args, 1); | ||
| 121 | + if (!Converter.ToManaged(allow, typeof(bool), out var allowObj, true) || allowObj is null) | ||
| 122 | + { | ||
| 123 | + Exceptions.RaiseTypeError("second argument to enum constructor must be a boolean"); | ||
| 124 | + return default; | ||
| 125 | + } | ||
| 126 | + allowUnchecked |= (bool)allowObj; | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + if (argCount < 1 || argCount > 2) | ||
| 130 | + { | ||
| 131 | + Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); | ||
| 132 | + return default; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + var op = Runtime.PyTuple_GetItem(args, 0); | ||
| 136 | + if (!Converter.ToManaged(op, type.GetEnumUnderlyingType(), out object result, true)) | ||
| 137 | + { | ||
| 138 | + return default; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + if (!allowUnchecked && !Enum.IsDefined(type, result) && !type.IsFlagsEnum()) | ||
| 142 | + { | ||
| 143 | + Exceptions.SetError(Exceptions.ValueError, "Invalid enumeration value. Pass True as the second argument if unchecked conversion is desired"); | ||
| 144 | + return default; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + object enumValue = Enum.ToObject(type, result); | ||
| 148 | + return CLRObject.GetInstHandle(enumValue, tp); | ||
| 112 | 149 | } | |
| 113 | 150 | ||
| 114 | 151 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -27,7 +27,6 @@ private Converter() | |||
| 27 | 27 | private static Type int16Type; | |
| 28 | 28 | private static Type int32Type; | |
| 29 | 29 | private static Type int64Type; | |
| 30 | - private static Type flagsType; | ||
| 31 | 30 | private static Type boolType; | |
| 32 | 31 | private static Type typeType; | |
| 33 | 32 | ||
@@ -42,7 +41,6 @@ static Converter() | |||
| 42 | 41 | singleType = typeof(Single); | |
| 43 | 42 | doubleType = typeof(Double); | |
| 44 | 43 | decimalType = typeof(Decimal); | |
| 45 | - flagsType = typeof(FlagsAttribute); | ||
| 46 | 44 | boolType = typeof(Boolean); | |
| 47 | 45 | typeType = typeof(Type); | |
| 48 | 46 | } | |
@@ -148,7 +146,8 @@ internal static IntPtr ToPython(object value, Type type) | |||
| 148 | 146 | return result; | |
| 149 | 147 | } | |
| 150 | 148 | ||
| 151 | - if (Type.GetTypeCode(type) == TypeCode.Object && value.GetType() != typeof(object)) { | ||
| 149 | + if (Type.GetTypeCode(type) == TypeCode.Object && value.GetType() != typeof(object) | ||
| 150 | + || type.IsEnum) { | ||
| 152 | 151 | var encoded = PyObjectConversions.TryEncode(value, type); | |
| 153 | 152 | if (encoded != null) { | |
| 154 | 153 | result = encoded.Handle; | |
@@ -203,6 +202,11 @@ internal static IntPtr ToPython(object value, Type type) | |||
| 203 | 202 | ||
| 204 | 203 | type = value.GetType(); | |
| 205 | 204 | ||
| 205 | + if (type.IsEnum) | ||
| 206 | + { | ||
| 207 | + return CLRObject.GetInstHandle(value, type); | ||
| 208 | + } | ||
| 209 | + | ||
| 206 | 210 | TypeCode tc = Type.GetTypeCode(type); | |
| 207 | 211 | ||
| 208 | 212 | switch (tc) | |
@@ -317,6 +321,18 @@ internal static bool ToManaged(IntPtr value, Type type, | |||
| 317 | 321 | } | |
| 318 | 322 | return Converter.ToManagedValue(value, type, out result, setError); | |
| 319 | 323 | } | |
| 324 | + /// <summary> | ||
| 325 | + /// Return a managed object for the given Python object, taking funny | ||
| 326 | + /// byref types into account. | ||
| 327 | + /// </summary> | ||
| 328 | + /// <param name="value">A Python object</param> | ||
| 329 | + /// <param name="type">The desired managed type</param> | ||
| 330 | + /// <param name="result">Receives the managed object</param> | ||
| 331 | + /// <param name="setError">If true, call <c>Exceptions.SetError</c> with the reason for failure.</param> | ||
| 332 | + /// <returns>True on success</returns> | ||
| 333 | + internal static bool ToManaged(BorrowedReference value, Type type, | ||
| 334 | + out object result, bool setError) | ||
| 335 | + => ToManaged(value.DangerousGetAddress(), type, out result, setError); | ||
| 320 | 336 | ||
| 321 | 337 | internal static bool ToManagedValue(BorrowedReference value, Type obType, | |
| 322 | 338 | out object result, bool setError) | |
@@ -398,11 +414,6 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 398 | 414 | return ToArray(value, obType, out result, setError); | |
| 399 | 415 | } | |
| 400 | 416 | ||
| 401 | - if (obType.IsEnum) | ||
| 402 | - { | ||
| 403 | - return ToEnum(value, obType, out result, setError); | ||
| 404 | - } | ||
| 405 | - | ||
| 406 | 417 | // Conversion to 'Object' is done based on some reasonable default | |
| 407 | 418 | // conversions (Python string -> managed string, Python int -> Int32 etc.). | |
| 408 | 419 | if (obType == objectType) | |
@@ -497,7 +508,7 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 497 | 508 | } | |
| 498 | 509 | ||
| 499 | 510 | TypeCode typeCode = Type.GetTypeCode(obType); | |
| 500 | - if (typeCode == TypeCode.Object) | ||
| 511 | + if (typeCode == TypeCode.Object || obType.IsEnum) | ||
| 501 | 512 | { | |
| 502 | 513 | IntPtr pyType = Runtime.PyObject_TYPE(value); | |
| 503 | 514 | if (PyObjectConversions.TryDecode(value, pyType, obType, out result)) | |
@@ -516,8 +527,17 @@ internal static bool ToManagedValue(IntPtr value, Type obType, | |||
| 516 | 527 | /// </summary> | |
| 517 | 528 | private static bool ToPrimitive(IntPtr value, Type obType, out object result, bool setError) | |
| 518 | 529 | { | |
| 519 | - TypeCode tc = Type.GetTypeCode(obType); | ||
| 520 | 530 | result = null; | |
| 531 | + if (obType.IsEnum) | ||
| 532 | + { | ||
| 533 | + if (setError) | ||
| 534 | + { | ||
| 535 | + Exceptions.SetError(Exceptions.TypeError, "since Python.NET 3.0 int can not be converted to Enum implicitly. Use Enum(int_value)"); | ||
| 536 | + } | ||
| 537 | + return false; | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + TypeCode tc = Type.GetTypeCode(obType); | ||
| 521 | 541 | IntPtr op = IntPtr.Zero; | |
| 522 | 542 | ||
| 523 | 543 | switch (tc) | |
@@ -876,40 +896,6 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s | |||
| 876 | 896 | result = items; | |
| 877 | 897 | return true; | |
| 878 | 898 | } | |
| 879 | - | ||
| 880 | - | ||
| 881 | - /// <summary> | ||
| 882 | - /// Convert a Python value to a correctly typed managed enum instance. | ||
| 883 | - /// </summary> | ||
| 884 | - private static bool ToEnum(IntPtr value, Type obType, out object result, bool setError) | ||
| 885 | - { | ||
| 886 | - Type etype = Enum.GetUnderlyingType(obType); | ||
| 887 | - result = null; | ||
| 888 | - | ||
| 889 | - if (!ToPrimitive(value, etype, out result, setError)) | ||
| 890 | - { | ||
| 891 | - return false; | ||
| 892 | - } | ||
| 893 | - | ||
| 894 | - if (Enum.IsDefined(obType, result)) | ||
| 895 | - { | ||
| 896 | - result = Enum.ToObject(obType, result); | ||
| 897 | - return true; | ||
| 898 | - } | ||
| 899 | - | ||
| 900 | - if (obType.GetCustomAttributes(flagsType, true).Length > 0) | ||
| 901 | - { | ||
| 902 | - result = Enum.ToObject(obType, result); | ||
| 903 | - return true; | ||
| 904 | - } | ||
| 905 | - | ||
| 906 | - if (setError) | ||
| 907 | - { | ||
| 908 | - Exceptions.SetError(Exceptions.ValueError, "invalid enumeration value"); | ||
| 909 | - } | ||
| 910 | - | ||
| 911 | - return false; | ||
| 912 | - } | ||
| 913 | 899 | } | |
| 914 | 900 | ||
| 915 | 901 | public static class ConverterExtension | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -340,7 +340,7 @@ public static void Clear() | |||
| 340 | 340 | public static void warn(string message, IntPtr exception, int stacklevel) | |
| 341 | 341 | { | |
| 342 | 342 | if (exception == IntPtr.Zero || | |
| 343 | - (Runtime.PyObject_IsSubclass(exception, Exceptions.Warning) != 1)) | ||
| 343 | + (Runtime.PyObject_IsSubclass(new BorrowedReference(exception), new BorrowedReference(Exceptions.Warning)) != 1)) | ||
| 344 | 344 | { | |
| 345 | 345 | Exceptions.RaiseTypeError("Invalid exception"); | |
| 346 | 346 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,7 +51,6 @@ static OperatorMethod() | |||
| 51 | 51 | ["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), | |
| 52 | 52 | ["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative), | |
| 53 | 53 | ["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive), | |
| 54 | - ["op_OneComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert), | ||
| 55 | 54 | }; | |
| 56 | 55 | ComparisonOpMap = new Dictionary<string, string> | |
| 57 | 56 | { | |
@@ -80,7 +79,7 @@ public static void Shutdown() | |||
| 80 | 79 | ||
| 81 | 80 | public static bool IsOperatorMethod(MethodBase method) | |
| 82 | 81 | { | |
| 83 | - if (!method.IsSpecialName) | ||
| 82 | + if (!method.IsSpecialName && !method.IsOpsHelper()) | ||
| 84 | 83 | { | |
| 85 | 84 | return false; | |
| 86 | 85 | } | |
@@ -102,7 +101,12 @@ public static void FixupSlots(IntPtr pyType, Type clrType) | |||
| 102 | 101 | { | |
| 103 | 102 | const BindingFlags flags = BindingFlags.Public | BindingFlags.Static; | |
| 104 | 103 | Debug.Assert(_opType != null); | |
| 105 | - foreach (var method in clrType.GetMethods(flags)) | ||
| 104 | + | ||
| 105 | + var staticMethods = | ||
| 106 | + clrType.IsEnum ? typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags) | ||
| 107 | + : clrType.GetMethods(flags); | ||
| 108 | + | ||
| 109 | + foreach (var method in staticMethods) | ||
| 106 | 110 | { | |
| 107 | 111 | // We only want to override slots for operators excluding | |
| 108 | 112 | // comparison operators, which are handled by ClassBase.tp_richcompare. | |
@@ -170,9 +174,11 @@ public static string ReversePyMethodName(string pyName) | |||
| 170 | 174 | /// <returns></returns> | |
| 171 | 175 | public static bool IsReverse(MethodInfo method) | |
| 172 | 176 | { | |
| 173 | - Type declaringType = method.DeclaringType; | ||
| 177 | + Type primaryType = method.IsOpsHelper() | ||
| 178 | + ? method.DeclaringType.GetGenericArguments()[0] | ||
| 179 | + : method.DeclaringType; | ||
| 174 | 180 | Type leftOperandType = method.GetParameters()[0].ParameterType; | |
| 175 | - return leftOperandType != declaringType; | ||
| 181 | + return leftOperandType != primaryType; | ||
| 176 | 182 | } | |
| 177 | 183 | ||
| 178 | 184 | public static void FilterMethods(MethodInfo[] methods, out MethodInfo[] forwardMethods, out MethodInfo[] reverseMethods) | |
| Back | FazBrowse Home | New Git URL |
0 commit comments