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:
Victor 2022-01-06 01:24:15 -08:00 коммит произвёл GitHub
Родитель 7e5cc29a62
Коммит efad01cf2e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 58 добавлений и 11 удалений

Просмотреть файл

@ -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