[plum] Tutorial: Byte Breakdown Summaries

dump property

plum instances offer a dump property to help visualize the resulting byte structure, the associated value, and the method of accessing the value. The property returns a dump object that supports string conversion to facilitate logging and debugging as well as supports calls to print the summary to the console. For example:

>>> from plum.structure import Member, Structure
>>> from plum.int.little import UInt8, UInt16
>>>
>>> class MyStruct(Structure):
...     byte: int = Member(cls=UInt8)
...     word: int = Member(cls=UInt16)
...
>>> mystruct = MyStruct(byte=1, word=2)
>>> mystruct.dump()
+--------+-------------+-------+-------+----------+
| Offset | Access      | Value | Bytes | Type     |
+--------+-------------+-------+-------+----------+
|        |             |       |       | MyStruct |
| 0      | [0] (.byte) | 1     | 01    | UInt8    |
| 1      | [1] (.word) | 2     | 02 00 | UInt16   |
+--------+-------------+-------+-------+----------+

The “Offset” column shows the byte offset in overall byte structure for each structure member. The “Access” column shows the syntax to use to access the value. The “Value” column shows the representation of the member’s unpacked bytes. The “Memory” column contains hexidecimal representation of the member’s bytes.

When an exception occurs while packing or unpacking bytes, the exception message includes a dump to show the successful progress made and the point at which the failure occurred. In the following example, the structure requires one more byte than what was provided:

>>> from plum import unpack
>>>
>>> x = unpack(MyStruct, b'\x02\x01')
Traceback (most recent call last):
    ...
plum._exceptions.UnpackError:
<BLANKLINE>
+--------+-------------+----------------------+-------+----------+
| Offset | Access      | Value                | Bytes | Type     |
+--------+-------------+----------------------+-------+----------+
|        |             |                      |       | MyStruct |
| 0      | [0] (.byte) | 2                    | 02    | UInt8    |
| 1      | [1] (.word) | <insufficient bytes> | 01    | UInt16   |
+--------+-------------+----------------------+-------+----------+
<BLANKLINE>
InsufficientMemoryError occurred during unpack operation:
<BLANKLINE>
1 too few bytes to unpack UInt16 (2 needed, only 1 available)

Pack/Unpack Exceptions

When pack or unpack operations fail for any reason, the exception message always includes the byte breakdown summary showing the successful progress made before the problem occurred to facilitate debugging. In the following example, the bytes to unpack contains one unexpected extra byte:

>>> from plum import unpack
>>> from plum.int.little import UInt16
>>>
>>> unpack(UInt16, b'\x01\x00\x99')
Traceback (most recent call last):
    ...
plum._exceptions.UnpackError:
<BLANKLINE>
+--------+----------------+-------+--------+
| Offset | Value          | Bytes | Type   |
+--------+----------------+-------+--------+
| 0      | 1              | 01 00 | UInt16 |
+--------+----------------+-------+--------+
| 2      | <excess bytes> | 99    |        |
+--------+----------------+-------+--------+
<BLANKLINE>
ExcessMemoryError occurred during unpack operation:
<BLANKLINE>
1 unconsumed bytes

Pack/Unpack Dump Alternatives

This package offers identical alternatives to every pack and unpack function and method variation. The alternative produces a byte breakdown summary dump while performing the operation and supply the dump in the return value. For example, the unpack_and_dump() function mirrors the unpack() function except that it includes the dump in the return:

>>> from plum import unpack, unpack_and_dump
>>> from plum.int.little import UInt8
>>>
>>> unpack((UInt8, UInt8), b'\x01\x02')
(1, 2)
>>> value, dump = unpack_and_dump((UInt8, UInt8), b'\x01\x02')
>>> value
(1, 2)
>>> dump()
+--------+--------+-------+-------+-------+
| Offset | Access | Value | Bytes | Type  |
+--------+--------+-------+-------+-------+
| 0      | [0]    | 1     | 01    | UInt8 |
| 1      | [1]    | 2     | 02    | UInt8 |
+--------+--------+-------+-------+-------+

Besides simplifying your code, using these alternatives may improve performance a bit since when used as a separate operation, the dump mechanics perform a pack.

Function/Method Alternative
pack pack_and_dump
pack_into pack_into_and_dummp
unpack unpack_and_dump
unpack_from unpack_from_and_dump

Tip

For speed critical operations, don’t use the dump property or the dump function and method alternatives since they add substantial speed costs.