Reputation: 8296
I've observed that I can convert certain integer values into their flag decomposed types, and that I can convert flag string names into flags, but I haven't yet figured out if there's a means to convert a string union of flag names to an enum.Flag
.
import enum
flag = enum.Flag('flg' , ['a', 'b', 'c', 'd'] )
# Valid 1:1 conversions...
>>> flag["a"]
<flg.a: 1>
>>> flag["b"]
<flg.b: 2>
>>> flag(1)
<flg.a: 1>
>>> flag(2)
<flg.b: 2>
# Valid union int conversion
>>> flag(7)
<flg.c|b|a: 7>
>>> flag(15) # largest integer flag can represent (1+2+4+8)
<flg.d|c|b|a: 15>
# Out of bounds
>>> flag(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Anaconda3\lib\enum.py", line 291, in __call__
return cls.__new__(cls, value)
File "C:\Anaconda3\lib\enum.py", line 533, in __new__
return cls._missing_(value)
File "C:\Anaconda3\lib\enum.py", line 673, in _missing_
possible_member = cls._create_pseudo_member_(value)
File "C:\Anaconda3\lib\enum.py", line 688, in _create_pseudo_member_
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 16 is not a valid flg
I was hoping I might be able to pull off something in the spirit of
flag["d|c|b|a"]
, but being a dictionary this conversion only works for distinct flag member names, not any aggregation of them. In the mean time I'm doing the split, convert, union merge manually but I'm curious if there's a more direct route like the int conversion case was capable of.
EDIT: Looking at the source for enum.py, it appears the correct terminology is not union but composite
# enum.py
def _create_pseudo_member_(cls, value):
"""
Create a composite member iff value contains only members.
"""
Upvotes: 3
Views: 667
Reputation: 59238
You could subclass enum.Flag
and intercept its _missing_()
method:
from functools import reduce
import enum
class UnionFlag(enum.Flag):
@classmethod
def _missing_(cls, value):
if isinstance(value, str):
return reduce(cls.__or__, (cls[s] for s in value.split('|')))
else:
return super()._missing_(value)
>>> flag = UnionFlag('uflg', ['a', 'b', 'c', 'd'])
>>> flag.a
<uflg.a: 1>
>>> flag(3)
<uflg.b|a: 3>
>>> flag['c']
<uflg.c: 4>
>>> flag('a|b|d')
<uflg.d|b|a: 11>
Upvotes: 3