Skip to content

Commit efad01c

Browse files
authored
provide __int__ instance method on .NET enum types to support int(Enum.Member) (#1661)
implements #1585
1 parent 7e5cc29 commit efad01c

File tree

7 files changed

+58
-11
lines changed

7 files changed

+58
-11
lines changed

src/runtime/classmanager.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -346,15 +346,25 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
346346
}
347347
}
348348

349-
// only [Flags] enums support bitwise operations
350-
if (type.IsEnum && type.IsFlagsEnum())
349+
if (type.IsEnum)
351350
{
352351
var opsImpl = typeof(EnumOps<>).MakeGenericType(type);
353352
foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags))
354353
{
355354
local.Add(op.Name);
356355
}
357356
info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
357+
358+
// only [Flags] enums support bitwise operations
359+
if (type.IsFlagsEnum())
360+
{
361+
opsImpl = typeof(FlagEnumOps<>).MakeGenericType(type);
362+
foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags))
363+
{
364+
local.Add(op.Name);
365+
}
366+
info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
367+
}
358368
}
359369

360370
// Now again to filter w/o losing overloaded member info

src/runtime/native/ITypeOffsets.cs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface ITypeOffsets
2121
int nb_multiply { get; }
2222
int nb_true_divide { get; }
2323
int nb_and { get; }
24+
int nb_int { get; }
2425
int nb_or { get; }
2526
int nb_xor { get; }
2627
int nb_lshift { get; }

src/runtime/native/TypeOffset.cs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static partial class TypeOffset
3030
internal static int nb_and { get; private set; }
3131
internal static int nb_or { get; private set; }
3232
internal static int nb_xor { get; private set; }
33+
internal static int nb_int { get; private set; }
3334
internal static int nb_lshift { get; private set; }
3435
internal static int nb_rshift { get; private set; }
3536
internal static int nb_remainder { get; private set; }

src/runtime/operatormethod.cs

+17-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Linq;
45
using System.Reflection;
5-
using System.Runtime.InteropServices;
66
using System.Text;
77

88
namespace Python.Runtime
@@ -51,6 +51,8 @@ static OperatorMethod()
5151
["op_OnesComplement"] = new SlotDefinition("__invert__", TypeOffset.nb_invert),
5252
["op_UnaryNegation"] = new SlotDefinition("__neg__", TypeOffset.nb_negative),
5353
["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive),
54+
55+
["__int__"] = new SlotDefinition("__int__", TypeOffset.nb_int),
5456
};
5557
ComparisonOpMap = new Dictionary<string, string>
5658
{
@@ -97,14 +99,11 @@ public static bool IsComparisonOp(MethodBase method)
9799
/// </summary>
98100
public static void FixupSlots(BorrowedReference pyType, Type clrType)
99101
{
100-
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
101102
Debug.Assert(_opType != null);
102103

103-
var staticMethods =
104-
clrType.IsEnum ? typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags)
105-
: clrType.GetMethods(flags);
104+
var operatorCandidates = GetOperatorCandidates(clrType);
106105

107-
foreach (var method in staticMethods)
106+
foreach (var method in operatorCandidates)
108107
{
109108
// We only want to override slots for operators excluding
110109
// comparison operators, which are handled by ClassBase.tp_richcompare.
@@ -124,6 +123,18 @@ public static void FixupSlots(BorrowedReference pyType, Type clrType)
124123
}
125124
}
126125

126+
static IEnumerable<MethodInfo> GetOperatorCandidates(Type clrType)
127+
{
128+
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
129+
if (clrType.IsEnum)
130+
{
131+
return typeof(EnumOps<>).MakeGenericType(clrType).GetMethods(flags)
132+
.Concat(typeof(FlagEnumOps<>).MakeGenericType(clrType).GetMethods(flags));
133+
}
134+
135+
return clrType.GetMethods(flags);
136+
}
137+
127138
public static string GetPyMethodName(string clrName)
128139
{
129140
if (OpMethodMap.ContainsKey(clrName))

src/runtime/opshelper.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static Expression EnumUnderlyingValue(Expression enumValue)
3838
internal class OpsAttribute: Attribute { }
3939

4040
[Ops]
41-
internal static class EnumOps<T> where T : Enum
41+
internal static class FlagEnumOps<T> where T : Enum
4242
{
4343
static readonly Func<T, T, T> and = BinaryOp(Expression.And);
4444
static readonly Func<T, T, T> or = BinaryOp(Expression.Or);
@@ -74,4 +74,16 @@ static Func<T, T> UnaryOp(Func<Expression, UnaryExpression> op)
7474
});
7575
}
7676
}
77+
78+
[Ops]
79+
internal static class EnumOps<T> where T : Enum
80+
{
81+
[ForbidPythonThreads]
82+
#pragma warning disable IDE1006 // Naming Styles - must match Python
83+
public static PyInt __int__(T value)
84+
#pragma warning restore IDE1006 // Naming Styles
85+
=> typeof(T).GetEnumUnderlyingType() == typeof(UInt64)
86+
? new PyInt(Convert.ToUInt64(value))
87+
: new PyInt(Convert.ToInt64(value));
88+
}
7789
}

src/testing/enumtest.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ public enum LongEnum : long
7272
Two,
7373
Three,
7474
Four,
75-
Five
75+
Five,
76+
Max = long.MaxValue,
77+
Min = long.MinValue,
7678
}
7779

7880
public enum ULongEnum : ulong
@@ -82,7 +84,8 @@ public enum ULongEnum : ulong
8284
Two,
8385
Three,
8486
Four,
85-
Five
87+
Five,
88+
Max = ulong.MaxValue,
8689
}
8790

8891
[Flags]

tests/test_enum.py

+9
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ def test_ulong_enum():
8787
assert Test.ULongEnum.Two == Test.ULongEnum(2)
8888

8989

90+
def test_long_enum_to_int():
91+
assert int(Test.LongEnum.Max) == 9223372036854775807
92+
assert int(Test.LongEnum.Min) == -9223372036854775808
93+
94+
95+
def test_ulong_enum_to_int():
96+
assert int(Test.ULongEnum.Max) == 18446744073709551615
97+
98+
9099
def test_instantiate_enum_fails():
91100
"""Test that instantiation of an enum class fails."""
92101
from System import DayOfWeek

0 commit comments

Comments
 (0)