I have written a small utility that decodes log output consisting of bit-flags. I use enums when decoding those bit-flags.
python --version
Python 3.8.6
For brevity reasons, I only want to print the enumeration member name. I've distilled my problem down to this short snippet:
from enum import IntFlag
class Foo(IntFlag):
A = 0x01
B = 0x02
C = 0x04
def __str__(self):
return self.name
__repr__ = __str__
It works fine if I show them individually or if I print them as part of a list. But I can't print an or-expression. I want to display the flags on the form A|B|C.
>>> Foo.A
A
>>> Foo.B
B
>>> [Foo.A, Foo.B]
[A, C]
>>> Foo.A | Foo.B
TypeError: __repr__ returned non-string (type NoneType)
Update: I was looking at the wrong version of the source code. The IntFlag::__repr__ for the 3.8.6 tag does contain code that does print each member https://github.com/python/cpython/blob/v3.8.6/Lib/enum.py#L761
I tried reading CPythons Lib/enum.py but couldn't figure out how thedefault IntFlag (without my overriden __repr__) prints the output on the form Foo.A|Foo.B.~~
I can see that IntFlag.__or__ returns a new object by calling __class__ (I don't really know what __class__ does). And Enum.__repr__ just prints a single class name and enumeration name.
How do I write a __repr__ function and a __str__ function which returns a string representation of the enumeration member names that are currently set?
from enum import Flag, _power_of_two
class BetterFlag(Flag):
def __repr__(self):
if self.value == 0:
return "%s(0)" % self.__class__.__name__
return '|'.join(
m.name
for m in self.__class__
if m.value & self.value and _power_of_two(m.value)
)
__str__ = __repr__
Python 3.10 and earlier do this by testing if the name attribute is None, and if so then looping over the contents. The looping was flawed for more exotic enums, and has been redone in 3.11.
Note: The above is forwards and backwards compatible. Have your Flags inherit from BetterFlag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With