Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Description
Bug report
According to theenum documentation, it is possible to customize the enumerationvalue via a custom__new__ method and the enumerationmember (e.g., by adding an attribute) via a custom__init__ method. However, the implementation of theenum.Flag class in 3.11.0 (and probably in 3.11.1) introduces some issues compared to the one in 3.10.3, especially in the case of anenum.IntFlag:
$read -r -d'' code<<EOMfrom enum import IntFlagclass Flag(IntFlag): def __new__(cls, ch: str, *args): value = 1 << ord(ch) self = int.__new__(cls, value) self._value_ = value return self def __init__(self, _, *args): super().__init__() # do something with the positional arguments a = ('a', 'A')print(repr(Flag.a ^ Flag.a))EOM$ python3.10 -c"$code"<Flag.0:0>$ python3.11 -c"$code"ValueError: 0 is not a valid FlagDuring handling of the above exception, another exception occurred:Traceback (most recent call last): File"<string>", line 16,in<module> File"/Lib/enum.py", line 1501,in __xor__return self.__class__(value ^ other) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File"/Lib/enum.py", line 695,in __call__return cls.__new__(cls, value) ^^^^^^^^^^^^^^^^^^^^^^^ File"/Lib/enum.py", line 1119,in __new__ raise exc File"/Lib/enum.py", line 1096,in __new__ result = cls._missing_(value) ^^^^^^^^^^^^^^^^^^^^ File"/Lib/enum.py", line 1416,in _missing_ pseudo_member = (__new__ or cls._member_type_.__new__)(cls, value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File"<string>", line 5,in __new__TypeError:ord() expected string of length 1, but int found
This also results in the impossibility of writingFlag.a | i fori != 0 (fori = 0, it does work ! and this is confusing !), which IMHO is a regression compared to what was proposed in 3.10.3. It also clashes with the following assumption:
If a Flag operation is performed with an IntFlag member and:
- the result is a valid IntFlag: an IntFlag is returned
- result is not a valid IntFlag: the result depends on the FlagBoundary setting
Currently, the FlagBoundary for IntFlag is KEEP, soFlag.a | 12 is expected to beFlag.a|8|4 as in 3.10.3.
In order to avoid this issue, users need to write something like:
def__new__(cls,ch,*args):value=chifisinstance(ch,int)else1<<ord(ch)self=int.__new__(cls,value)self._value_=valuereturnself
Neverthless, this is only possible if__new__ converts an inputU to an entirely different typeV (enum member type) or ifargs is non-empty when declaring enumeration members. However, this fails in the following example:
classFlagFromChar(IntFlag):def__new__(cls,e:int):value=1<<eself=int.__new__(cls,value)self._value_=valuereturnselfa=ord('a')# in Python 3.10.3repr(FlagFromChar.a^FlagFromChar.a)=='<FlagFromChar.0: 0>'# in Python 3.11.1repr(FlagFromChar.a^FlagFromChar.a)=='<FlagFromChar: 1>'
Environment
- CPython versions tested on: 3.10.3 and 3.11.0 (compiled from sources with GCC 7.5.0)
- Operating System: openSUSE Leap 15.2 x86_64
- Kernel: 5.3.18-lp152.106-default