[plum.int.flag] Tutorial: Using Integer Flag Enumeration Types¶
The plum.int.flag
subpackage provides a plum type class for packing
and unpacking integer flag enumeration members. Compared to normal integers,
enumerations offer better representations within the bytes summary dumps.
Flag enumerations also support bitwise combinations that retain enumeration
membership. This tutorial teaches how to create and use integer flag
enumeration plum types.
Creating an Integer Enumeration Type¶
To create a plum integer flag enumeration type, subclass the Flag
class (similar to
subclassing IntFlag
from the enum
standard library module).
Use arguments to control the number of bytes and byte order. For example:
>>> from plum.int.flag import Flag
>>>
>>> class Color(Flag, nbytes=2, byteorder='big'):
... RED = 1
... GREEN = 2
... BLUE = 4
... WHITE = RED | GREEN | BLUE
...
Besides having plum behaviors (discussed in next sections), the enumeration behaves
as subclasses of enum.IntFlag
do. For example:
>>> Color.RED == 1
True
>>> Color(1)
<Color.RED: 1>
>>> Color['RED']
<Color.RED: 1>
>>> Color.RED | Color.GREEN | Color.BLUE
<Color.WHITE: 7>
Unpacking Bytes¶
plum
integer flag enumeration types convert bytes into an enumeration member instance
when used with the various plum unpacking mechanisms. For example (using the
enumeration from the previous section):
>>> from plum import unpack, Buffer
>>>
>>> # utility function
>>> unpack(Color, b'\x00\x01')
<Color.RED: 1>
>>>
>>> # class method
>>> Color.unpack(b'\x00\x01')
<Color.RED: 1>
>>>
>>> # bytes buffer
>>> with Buffer(b'\x00\x01\x00\x02') as buffer:
... a = buffer.unpack(Color)
... b = buffer.unpack(Color)
...
>>> a, b
(<Color.RED: 1>, <Color.GREEN: 2>)
Packing Bytes¶
plum
integer flag enumeration types convert integers (or anything int-like) into bytes
when used with the various plum packing mechanisms. For example (using the
enumeration from the previous sections):
>>> from plum import pack
>>>
>>> # utility function
>>> pack(Color, Color.RED)
bytearray(b'\x00\x01')
>>>
>>> # class method
>>> Color.pack(1)
bytearray(b'\x00\x01')
>>>
>>> # instance method
>>> Color.RED.pack()
bytearray(b'\x00\x01')
Strict vs. Tolerant¶
Similar to enum.IntFlag
standard library enumerations,
plum
integer flag enumerations tolerate undefined values:
>>> Color(8)
<Color.8: 8>
>>> Color.unpack(b'\x00\x08')
<Color.8: 8>
>>> Color.pack(8)
bytearray(b'\x00\x08')
No mechanism exists to force plum
integer flag enumerations
to limit values to the predefined enumeration members.
Inheriting Enumeration Members¶
To create a plum integer flag enumeration type with members inherited from another
flag enumeration, subclass the Flag
class and pass the enumeration with
the members to copy using the source
argument. For example:
>>> from enum import Flag as EnumFlag
>>> from plum.int.flag import Flag
>>>
>>> class Bit(EnumFlag):
... """Normal flag enumeration without pack/unpack properties."""
... ZERO = 1
... ONE = 2
...
>>>
>>> class Bit8(Flag, nbytes=1, source=Bit):
... """Plum enumeration with pack/unpack properties"""
... TWO = 4 # optional additional member
...
The resulting plum
flag enumeration contains members from the source plus
members defined in the class body:
>>> for member in Bit8:
... print(repr(member))
...
<Bit8.ZERO: 1>
<Bit8.ONE: 2>
<Bit8.TWO: 4>
>>>
>>> Bit8.ONE.dump()
+--------+--------+-------+-------+------+
| Offset | Access | Value | Bytes | Type |
+--------+--------+-------+-------+------+
| 0 | | 2 | 02 | Bit8 |
| [0] | .zero | False | | bool |
| [1] | .one | True | | bool |
| [2] | .two | False | | bool |
+--------+--------+-------+-------+------+