provide `__int__` instance method on .NET enum types to support int(Enum.Member) (#1661)
implements https://github.com/pythonnet/pythonnet/issues/1585
This commit is contained in:
Родитель
7e5cc29a62
Коммит
efad01cf2e
|
@ -346,8 +346,7 @@ namespace Python.Runtime
|
|||
}
|
||||
}
|
||||
|
||||
// only [Flags] enums support bitwise operations
|
||||
if (type.IsEnum && type.IsFlagsEnum())
|
||||
if (type.IsEnum)
|
||||
{
|
||||
var opsImpl = typeof(EnumOps<>).MakeGenericType(type);
|
||||
foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags))
|
||||
|
@ -355,6 +354,17 @@ namespace Python.Runtime
|
|||
local.Add(op.Name);
|
||||
}
|
||||
info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
|
||||
|
||||
// only [Flags] enums support bitwise operations
|
||||
if (type.IsFlagsEnum())
|
||||
{
|
||||
opsImpl = typeof(FlagEnumOps<>).MakeGenericType(type);
|
||||
foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags))
|
||||
{
|
||||
local.Add(op.Name);
|
||||
}
|
||||
info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Now again to filter w/o losing overloaded member info
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace Python.Runtime.Native
|
|||
int nb_multiply { get; }
|
||||
int nb_true_divide { get; }
|
||||
int nb_and { get; }
|
||||
int nb_int { get; }
|
||||
int nb_or { get; }
|
||||
int nb_xor { get; }
|
||||
int nb_lshift { get; }
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace Python.Runtime
|
|||
internal static int nb_and { get; private set; }
|
||||
internal static int nb_or { get; private set; }
|
||||
internal static int nb_xor { get; private set; }
|
||||
internal static int nb_int { get; private set; }
|
||||
internal static int nb_lshift { get; private set; }
|
||||
internal static int nb_rshift { get; private set; }
|
||||
internal static int nb_remainder { get; private set; }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Python.Runtime
|
||||
|
@ -51,6 +51,8 @@ namespace Python.Runtime
|
|||
["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert),
|
||||
["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative),
|
||||
["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive),
|
||||
|
||||
["__int__"] = new SlotDefinition("__int__", TypeOffset.nb_int),
|
||||
};
|
||||
ComparisonOpMap = new Dictionary<string, string>
|
||||
{
|
||||
|
@ -97,14 +99,11 @@ namespace Python.Runtime
|
|||
/// </summary>
|
||||
public static void FixupSlots(BorrowedReference pyType, Type clrType)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
|
||||
Debug.Assert(_opType != null);
|
||||
|
||||
var staticMethods =
|
||||
clrType.IsEnum ? typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags)
|
||||
: clrType.GetMethods(flags);
|
||||
var operatorCandidates = GetOperatorCandidates(clrType);
|
||||
|
||||
foreach (var method in staticMethods)
|
||||
foreach (var method in operatorCandidates)
|
||||
{
|
||||
// We only want to override slots for operators excluding
|
||||
// comparison operators, which are handled by ClassBase.tp_richcompare.
|
||||
|
@ -124,6 +123,18 @@ namespace Python.Runtime
|
|||
}
|
||||
}
|
||||
|
||||
static IEnumerable<MethodInfo> GetOperatorCandidates(Type clrType)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
|
||||
if (clrType.IsEnum)
|
||||
{
|
||||
return typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags)
|
||||
.Concat(typeof(FlagEnumOps<>).MakeGenericType(clrType).GetMethods(flags));
|
||||
}
|
||||
|
||||
return clrType.GetMethods(flags);
|
||||
}
|
||||
|
||||
public static string GetPyMethodName(string clrName)
|
||||
{
|
||||
if (OpMethodMap.ContainsKey(clrName))
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Python.Runtime
|
|||
internal class OpsAttribute: Attribute { }
|
||||
|
||||
[Ops]
|
||||
internal static class EnumOps<T> where T : Enum
|
||||
internal static class FlagEnumOps<T> where T : Enum
|
||||
{
|
||||
static readonly Func<T, T, T> and = BinaryOp(Expression.And);
|
||||
static readonly Func<T, T, T> or = BinaryOp(Expression.Or);
|
||||
|
@ -74,4 +74,16 @@ namespace Python.Runtime
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Ops]
|
||||
internal static class EnumOps<T> where T : Enum
|
||||
{
|
||||
[ForbidPythonThreads]
|
||||
#pragma warning disable IDE1006 // Naming Styles - must match Python
|
||||
public static PyInt __int__(T value)
|
||||
#pragma warning restore IDE1006 // Naming Styles
|
||||
=> typeof(T).GetEnumUnderlyingType() == typeof(UInt64)
|
||||
? new PyInt(Convert.ToUInt64(value))
|
||||
: new PyInt(Convert.ToInt64(value));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,9 @@ namespace Python.Test
|
|||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five
|
||||
Five,
|
||||
Max = long.MaxValue,
|
||||
Min = long.MinValue,
|
||||
}
|
||||
|
||||
public enum ULongEnum : ulong
|
||||
|
@ -82,7 +84,8 @@ namespace Python.Test
|
|||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five
|
||||
Five,
|
||||
Max = ulong.MaxValue,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
|
|
@ -87,6 +87,15 @@ def test_ulong_enum():
|
|||
assert Test.ULongEnum.Two == Test.ULongEnum(2)
|
||||
|
||||
|
||||
def test_long_enum_to_int():
|
||||
assert int(Test.LongEnum.Max) == 9223372036854775807
|
||||
assert int(Test.LongEnum.Min) == -9223372036854775808
|
||||
|
||||
|
||||
def test_ulong_enum_to_int():
|
||||
assert int(Test.ULongEnum.Max) == 18446744073709551615
|
||||
|
||||
|
||||
def test_instantiate_enum_fails():
|
||||
"""Test that instantiation of an enum class fails."""
|
||||
from System import DayOfWeek
|
||||
|
|
Загрузка…
Ссылка в новой задаче