ironpython2
ironpython2 copied to clipboard
Make CLR enum behave more like python IntEnum
Description
The CLR enum have interesting characteristics that makes it too similar to python IntEnum
and IntFlag
.
however not all of those characteristics are supported in IronPython.
Examples: given the following DaysInt
:
[Flags]
public enum DaysInt : int {
Mon = 0x01,
Tue = 0x02,
Wed = 0x04,
Thu = 0x08,
Fri = 0x10,
Sat = 0x20,
Sun = 0x40,
Weekdays = Mon | Tue | Wed | Thu | Fri,
Weekend = Sat | Sun,
All = Weekdays | Weekend
}
the below C# code will work just fine:
Console.WriteLine((DaysInt) 8); // Thu
Console.WriteLine((DaysInt) 0x100); // 256
Console.WriteLine(DaysInt.Tue < DaysInt.Wed); // 6
if DaysInt
was implemented purely in python, it would look something like this:
from enum import IntFlag
class DaysInt(IntFlag):
Mon = 0x01,
Tue = 0x02,
Wed = 0x04,
Thu = 0x08,
Fri = 0x10,
Sat = 0x20,
Sun = 0x40,
Weekdays = 0x1F,
Weekend = 0x20 | 0x40,
All = 0x7F,
print(DaysInt(8)) # DaysInt.Thu
print(DaysInt(0x100)) # DaysInt.256
print(DaysInt.Tue < DaysInt.Wed) # True
Steps to Reproduce
Now if we take CLR implimentation of DaysInt
, and import it to IronPython
print(DaysInt(8)) # throws
print(DaysInt(0x100)) # throws
print(DaysInt.Tue < DaysInt.Wed) # throws
After some digging, I noticed that none of the methods to support this is implemented (__new__
, __gt___
,...) in EnumOps.cs
.
I am open to creating the PR myself, if the idea itself is accepted.
Versions
IronPython 2.7.8
I know that IntEnum
is for Python 3.4+ but it was back-ported to Python 2 as enum34 and it's widely adopted by the Python community.
We would definitely be open to a PR, please make sure there are tests for the behavior when submitting the PR.
@slide after some digging, I found that patching EnumOps
was not getting me anywhere, the methods defined there are not called automatically but rather only explicitly. for example when doing EnumType.__call__(1)
I found that making the changes at the DLR level are much more easier: adding EnumType(int number)
constructor to CompilerHelpers.GetConstructors(...)
.
however I am not sure if this is a desired change at the DLR level (possible side effects for IronRuby?)
for other changes (like changing the str()
, repr()
, and __eq__
), a small push in the right direction may help. debugging the code got me nowhere, due the noise caused by stepping through Microsoft.Scripting.Interpreter
classes, plus the documentation is nonexistent.
@kayoub5 You could try modifying IsStandardDotNetType
and add something like !typeof(Enum).IsAssignableFrom(Value.UnderlyingSystemType)
to the list of checks. That way the __new__
methods defined in EnumOps
would be picked up. I'm not sure if this will cause issues elsewhere but it's a good place to start.
A side note: it looks like the comparison operators already work for enums? DaysInt.Tue < DaysInt.Wed
works for me.
@slozier
You could try modifying IsStandardDotNetType and add something like !typeof(Enum).IsAssignableFrom(Value.UnderlyingSystemType) to the list of checks. That way the new methods defined in EnumOps would be picked up. I'm not sure if this will cause issues elsewhere but it's a good place to start.
Good tip
A side note: it looks like the comparison operators already work for enums? DaysInt.Tue < DaysInt.Wed works for me.
I double checked, yes it works for DaysInt.Tue < DaysInt.Wed
but not 2 < DaysInt.Wed
(returns False
). Any ideas on how to override this?
I am getting a strange behavior, where DaysInt.Mon == DaysInt.Mon
returns False
(on master
).
I don't seem to be able to debug it, any idea what method is being used for equality checking?
For 2 < DaysInt.Wed
I'm not sure what comparison method it's currently calling, I'm not sure what sort of comparison would decide to return False
. I'm guessing you could probably fix it with some overloads in EnumOps
but there may be some better way.
As for DaysInt.Mon == DaysInt.Mon
returning False
I'm not seeing this behavior over here so I'm not sure...
We can close this via #424 correct?
@slide the PR was just about constructing enum from their integer value. but there is more to python enum compatibility, for example:
# Get enum by str name
color = Color['RED']
# enum member properties
member = Color.RED
print member.name # RED
print member.value # 1
# listing enum members
print list(Shape) # [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
Those points could be addressed in separate issues, or in this one.