I am writing a program where I want to define enum with possible interpretation of certain value that is stored one byte. So it is more specific than IntEnum
as my value must be int
in range 0x00-0xFF.
There are two types of values in the enum:
Therefore, I am using aenum
instead of classic enum
package as my enum might register new members after the program is started.
I would like to add some code to IntEnum
to restrict possible member values, so it has to be both int
and 0x00 <= value <= 0xFF
or some exception would be raised.
Why? To stop user from defining improper values (eg -1
, 256
, 10.0
) that are not int
or not in range 0x00-0xFF.
I thought to update __new__
method of IntEnum
, but its nearly 500 lines long and it is rather hard to understand that. Though, I have notice it has boundary
parameter. Isn't what I need (lower boundar = 0x00, higher bondary = 0xFF)?
from aenum import IntEnum, unique
@unique
class SomeByteEnum(IntEnum):
FOO = 1
BAR = 2
...
After the update, following cases shall raise an exception:
@unique
class SomeByteEnum(IntEnum):
... # some magical code here
INVALID_VALUE = -1 # value is lower than minimal byte value 0x00, exception shall be raised
from aenum import extend_enum
extend_enum(SomeByteEnum, "INVALID_VALUE", 0x100) # value is greater than max byte value 0xFF, exception shall be raised
Enum
members are created when the class is created, so your
SomeByteEnum.INVALID_VALUE = 0x100
is not creating a new member, just an ordinary attribute. In others words:
>>> isinstance(SomeByteEnum.FOO, SomeByteEnum)
True
>>> isinstance(SomeByteEnum.INVALIDVALUE, SomeByteEnum)
False
It is possible to have both standard name/values and custom name/values:
class SomeByteEnum(IntEnum):
StdValue1 = 0x01
StdValue2 = 0x02
...
locals().update(get_user_name_value_pairs())
To implement your value restrictions you'll need to write your own __new__
:
def __new__(cls, value):
if value < 0x00 or value > 0xff:
raise ValueError('value must be between 0x00 and 0xff [got %r]' % (value, ))
member = int.__new__(cls, value)
member._value_ = value
return member
Note: the above is untested and may contain minor errors.
Wrt:
I thought to update
__new__
method ofIntEnum
, but its nearly 500 lines long and it is rather hard to understand that.
You will have to update __new__
but of your own enum class, not the actual IntEnum
.
This is what worked, notes below:
import aenum
@aenum.unique
class SomeByteEnum(aenum.IntEnum):
def __new__(cls, value):
if not 0 <= value <= 255:
raise ValueError('Value must be >=0 and <= 255')
if not isinstance(value, int):
raise TypeError('Value must be an int')
obj = int.__new__(cls, value)
obj._value_ = value
return obj
ZEROTH = 0
FIRST = 1
SECOND = 2
THIRD = 3
Usage:
>>> from aenum import extend_enum
>>>
>>> list(SomeByteEnum)
[<SomeByteEnum.ZEROTH: 0>, <SomeByteEnum.FIRST: 1>, <SomeByteEnum.SECOND: 2>,
<SomeByteEnum.THIRD: 3>]
>>>
>>> # allowed:
>>> extend_enum(SomeByteEnum, 'FOURTH', 4)
>>> list(SomeByteEnum)
[<SomeByteEnum.ZEROTH: 0>, <SomeByteEnum.FIRST: 1>, <SomeByteEnum.SECOND: 2>,
<SomeByteEnum.THIRD: 3>, <SomeByteEnum.FOURTH: 4>]
>>>
>>> # NOT allowed:
>>> extend_enum(SomeByteEnum, 'MeeeeeLION', 1_000_000)
Traceback (most recent call last):
...
ValueError: Value must be >=0 and <= 255
>>>
>>> # NOT allowed:
>>> extend_enum(SomeByteEnum, 'SIXFEETUNDER', -6)
Traceback (most recent call last):
...
ValueError: Value must be >=0 and <= 255
>>>
>>> # floats NOT allowed:
>>> extend_enum(SomeByteEnum, '5 point 5', 5.5)
Traceback (most recent call last):
...
TypeError: Value must be an int
>>>
>>> extend_enum(SomeByteEnum, 'TooTRUE', True) # True = 1 so is absorbed, no error
cls=<aenum 'SomeByteEnum'> value=True
>>>
>>> # check members
>>> for m in SomeByteEnum:
... print(m.name, m.value)
...
ZEROTH 0
FIRST 1
SECOND 2
THIRD 3
FOURTH 4
>>>
Notes:
start=0
like class SomeByteEnum(aenum.IntEnum, start=0):
breaks how value
works - extended members cannot be added.aenum=2.2.6
, had some trouble with 3.0.0, like for m in SomeByteEnum
not showing the added members.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.