Skip to content

Commit 396aa7f

Browse files
authored
Adding __repr__ for unions (#41)
Adds `__repr__` and `__str__` to union to print out all members, also adjusts the formatting of structs to make it easier to follow: ``` Instruction: 0x12345678 (union): |- raw -> Unsigned Scalar[32]: 0x12345678 (32 bits) |- add -> Add: 0x12345678 |- [ 1: 0] op = 0x0 |- [ 6: 2] tgt = 0x1E |- [11: 7] src_a = 0xC |- [16:12] src_b = 0x5 |- add_imm -> Add: 0x12345678 |- [ 1: 0] op = 0x0 |- [ 6: 2] tgt = 0x1E |- [11: 7] src_a = 0xC |- [16:12] src_b = 0x5 ```
1 parent a925f16 commit 396aa7f

5 files changed

Lines changed: 58 additions & 13 deletions

File tree

packtype/types/assembly.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def _pt_lookup(self, field: type[Base] | Base) -> str:
4747

4848
@property
4949
@functools.lru_cache # noqa: B019
50-
def _pt_fields(self) -> dict:
50+
def _pt_fields(self) -> dict[Base, str]:
5151
return {getattr(self, x): x for x in self._PT_DEF.keys()}
5252

5353

@@ -136,8 +136,10 @@ def __str__(self) -> str:
136136
for fname in self._PT_DEF.keys():
137137
finst = getattr(self, fname)
138138
lsb, msb = self._PT_RANGES[fname]
139+
width = msb - lsb + 1
139140
lines.append(
140-
f" - [{msb:{max_bits}}:{lsb:{max_bits}}] {fname:{max_name}} = 0x{int(finst):X}"
141+
f" |- [{msb:{max_bits}}:{lsb:{max_bits}}] {fname:{max_name}} "
142+
f"= 0x{int(finst):0{(width + 3) // 4}X}"
141143
)
142144
return "\n".join(lines)
143145

packtype/types/scalar.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ def _pt_name(cls) -> str:
1515
if cls._PT_ATTACHED_TO is not None:
1616
return cls._PT_ATTACHED_TO._pt_lookup(cls)
1717
else:
18-
return NumericPrimitive._pt_name(cls)
18+
return f"{['Unsigned', 'Signed'][cls._PT_SIGNED]} Scalar[{cls._PT_WIDTH}]"
19+
20+
def __str__(self) -> str:
21+
return f"{type(self)._pt_name()}: 0x{int(self):0{(self._PT_WIDTH + 3) // 4}X}"
22+
23+
def __repr__(self):
24+
return str(self)
1925

2026

2127
class Scalar(NumericPrimitive):

packtype/types/union.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44

55
import functools
6+
import textwrap
67

78
from .array import ArraySpec
89
from .assembly import Assembly
@@ -30,6 +31,19 @@ def __init__(
3031
# NOTE: Fields are not constructed at this point, instead they are filled
3132
# in lazily as they are requested by the consumer
3233

34+
def __str__(self) -> str:
35+
parts = {}
36+
for finst, fname in self._pt_fields.items():
37+
parts[fname] = str(finst)
38+
max_prefix = max(map(len, parts.keys()))
39+
return f"{type(self).__name__}: 0x{int(self):X} (union):\n" + "\n".join(
40+
(f" |- {p:{max_prefix}s} -> " + textwrap.indent(v, " " * (max_prefix + 8)).lstrip())
41+
for p, v in parts.items()
42+
)
43+
44+
def __repr__(self) -> str:
45+
return self.__str__()
46+
3347
def __getattribute__(self, fname: str):
3448
# Attempt to resolve the attribute from existing properties
3549
try:

tests/pysyntax/test_struct.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,18 @@ class TestStruct:
9494
cd: Scalar[3]
9595
ef: Scalar[9]
9696

97-
inst = TestStruct._pt_unpack((39 << 15) | (5 << 12) | 123)
98-
assert inst.ab.value == 123
99-
assert inst.cd.value == 5
100-
assert inst.ef.value == 39
97+
value = (39 << 15) | (5 << 12) | 123
98+
inst = TestStruct._pt_unpack(value)
99+
assert inst.ab.value == (ab_value := 123)
100+
assert inst.cd.value == (cd_value := 5)
101+
assert inst.ef.value == (ef_value := 39)
102+
103+
assert str(inst) == (
104+
f"TestStruct: 0x{value:06X}\n"
105+
f" |- [11: 0] ab = 0x{ab_value:03X}\n"
106+
f" |- [14:12] cd = 0x{cd_value:01X}\n"
107+
f" |- [23:15] ef = 0x{ef_value:03X}"
108+
)
101109

102110

103111
def test_struct_unpacking_from_msb():

tests/pysyntax/test_union.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,27 @@ class Packet:
140140
raw: Scalar[32]
141141
header: Header
142142

143-
inst = Packet._pt_unpack((0x7 << 28) | (0x5 << 24) | (0x75 << 16) | 0x1234)
144-
assert int(inst.raw) == (0x7 << 28) | (0x5 << 24) | (0x75 << 16) | 0x1234
145-
assert int(inst.header.address) == 0x1234
146-
assert int(inst.header.length) == 0x75
147-
assert int(inst.header.mode) == 0x5
148-
assert int(inst.header.flags) == 0x7
143+
hdr_addr_val = 0x1234
144+
hdr_len_val = 0x75
145+
hdr_mode_val = 0x5
146+
hdr_flags_val = 0x7
147+
value = (hdr_flags_val << 28) | (hdr_mode_val << 24) | (hdr_len_val << 16) | hdr_addr_val
148+
inst = Packet._pt_unpack(value)
149+
assert int(inst.raw) == value
150+
assert int(inst.header.address) == hdr_addr_val
151+
assert int(inst.header.length) == hdr_len_val
152+
assert int(inst.header.mode) == hdr_mode_val
153+
assert int(inst.header.flags) == hdr_flags_val
154+
155+
assert str(inst) == (
156+
f"Packet: 0x{value:08X} (union):\n"
157+
f" |- raw -> Unsigned Scalar[32]: 0x{value:08X}\n"
158+
f" |- header -> Header: 0x{value:08X}\n"
159+
f" |- [15: 0] address = 0x{hdr_addr_val:04X}\n"
160+
f" |- [23:16] length = 0x{hdr_len_val:02X}\n"
161+
f" |- [27:24] mode = 0x{hdr_mode_val:01X}\n"
162+
f" |- [31:28] flags = 0x{hdr_flags_val:01X}"
163+
)
149164

150165

151166
def test_union_bad_widths():

0 commit comments

Comments
 (0)