Skip to content

Commit

Permalink
Trying to make pack always take a Sequence.
Browse files Browse the repository at this point in the history
  • Loading branch information
scott-griffiths committed Nov 2, 2024
1 parent af8c216 commit 3d6ec62
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
[![bitformat](https://raw.githubusercontent.com/scott-griffiths/bitformat/main/doc/bitformat_logo_small.png)](https://github.com/scott-griffiths/bitformat)

[![PyPI - Version](https://img.shields.io/pypi/v/bitformat?label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/bitformat/)
[![CI badge](https://github.com/scott-griffiths/bitformat/actions/workflows/.github/workflows/ci.yml/badge.svg)](https://github.com/scott-griffiths/bitformat/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/readthedocs/bitformat?logo=readthedocs&logoColor=white)](https://bitformat.readthedocs.io/en/latest/)
[![Codacy Badge](https://img.shields.io/codacy/grade/b61ae16cc6404d0da5dbcc21ee19ddda?logo=codacy)](https://app.codacy.com/gh/scott-griffiths/bitformat/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
Expand Down
7 changes: 4 additions & 3 deletions bitformat/_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class Array:
- ``extend(iterable)``: Append new items to the end of the Array from an iterable.
- ``insert(index, item)``: Insert an item at a given position.
- ``pop([index])``: Remove and return an item. Default is the last item.
- ``pp([fmt, width, show_offset, stream])``: Pretty print the Array.
- ``pp([dtype1, dtype2, groups, width, show_offset, stream])``: Pretty print the Array.
- ``reverse()``: Reverse the order of all items.
- ``to_bits()``: Return the Array data as a Bits object.
- ``to_bytes()``: Return Array data as bytes object, padding with zero bits at the end if needed.
Expand Down Expand Up @@ -531,9 +531,10 @@ def pp(self, dtype1: str | Dtype | DtypeList | None = None, dtype2: str | Dtype

token_length = dtype1.bits_per_item
if dtype2 is not None:
# For two types we're OK as long as they don't have different lengths given.
if dtype1.bits_per_item != 0 and dtype2.bits_per_item != 0 and dtype1.bits_per_item != dtype2.bits_per_item:
raise ValueError(f"Differing bit lengths of {dtype1.bits_per_item} and {dtype2.bits_per_item}.")
raise ValueError(f"If two Dtypes are given to pp() they must have the same length,"
f" but '{dtype1}' has a length of {dtype1.bits_per_item} and '{dtype2}' has a "
f"length of {dtype2.bits_per_item}.")
if token_length == 0:
token_length = dtype2.bits_per_item
if token_length == 0:
Expand Down
4 changes: 2 additions & 2 deletions bitformat/_fieldtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def parse(self, b: BitsType | None = None, /, **kwargs) -> int:
raise ValueError(f"Error parsing field {self}: {str(e)}")

@final
def pack(self, values: Any | None = None, /, **kwargs) -> Bits:
def pack(self, values: Sequence[Any] | None = None, /, **kwargs) -> Bits:
"""
Pack the field type into bits.
Expand All @@ -62,7 +62,7 @@ def pack(self, values: Any | None = None, /, **kwargs) -> Bits:
if values is None:
return self._pack([], 0, {}, kwargs)[0]
try:
bits, values_used = self._pack([values], 0, {}, kwargs)
bits, values_used = self._pack(values, 0, {}, kwargs)
except TypeError as e:
if not isinstance(values, Sequence):
raise TypeError(f"The values parameter must be a sequence (e.g. a list or tuple), not a {type(values)}.")
Expand Down
20 changes: 19 additions & 1 deletion bitformat/_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ def from_string(cls, s: str, /) -> Format:
name, field_strs, err_msg = cls._parse_format_str(s)
if err_msg:
raise ValueError(err_msg)
# Pre-process for 'if' fields to join things together if needed.
processed_fields_strs = []
just_had_if = False
just_had_else = False
for fs in field_strs:
if just_had_if or just_had_else:
processed_fields_strs[-1] += fs
continue
if fs.startswith('if'): # TODO: not good enough test
just_had_if = True
processed_fields_strs.append(fs)
elif fs.startswith('else'): # TODO: also not good enough
just_had_else = True
processed_fields_strs[-1] += fs
else:
just_had_if = just_had_else = False
processed_fields_strs.append(fs)
field_strs = processed_fields_strs

fieldtypes = []
for fs in field_strs:
Expand Down Expand Up @@ -166,7 +184,7 @@ def _pack(self, values: Sequence[Any], index: int, _vars: dict[str, Any] | None
kwargs: dict[str, Any] | None = None) -> tuple[Bits, int]:
values_used = 0
for fieldtype in self.fields:
_, v = fieldtype._pack(values[index], index + values_used, _vars, kwargs)
_, v = fieldtype._pack(values, index + values_used, _vars, kwargs)
values_used += v
return self.to_bits(), values_used

Expand Down
4 changes: 2 additions & 2 deletions bitformat/_if.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def _pack(self, values: Sequence[Any], index: int, vars_: dict[str, Any] | None
kwargs: dict[str, Any] | None = None) -> tuple[Bits, int]:
self.condition_value = self.condition.evaluate(vars_ | kwargs)
if self.condition_value:
_, v = self.then_._pack(values[index], index, vars_, kwargs)
_, v = self.then_._pack(values, index, vars_, kwargs)
else:
_, v = self.else_._pack(values[index], index, vars_, kwargs)
_, v = self.else_._pack(values, index, vars_, kwargs)
return self.to_bits(), v

@override
Expand Down
7 changes: 4 additions & 3 deletions tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,7 @@ def test_pp_with_groups():
]
"""

def test_pp_with_dtypelist():
a = Array('u8', list(range(20)))
a.pp('u4, i4', groups=5)
# def test_pp_with_dtypelist():
# a = Array('u8', list(range(20)))
# a.pp('i4, bits3', 'bits3, i5', groups=2)
# # a.pp('u4, i4', groups=5)
16 changes: 8 additions & 8 deletions tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def test_string_creation_with_const(self):
assert not f1.const
f1.clear()
f2.clear()
temp = f1.pack(0)
temp = f1.pack([0])
assert temp == '0b0'
assert f2.pack() == '0b1'

Expand All @@ -112,18 +112,18 @@ def test_building_with_keywords(self, x, name):

def test_building_lots_of_types(self):
f = Field('u4')
b = f.pack(15)
b = f.pack([15])
assert b == '0xf'
f = Field('i4')
b = f.pack(-8)
b = f.pack([-8])
assert b == '0x8'
f = Field('bytes3')
b = f.pack(b'abc')
b = f.pack([b'abc'])
assert b == '0x616263'
f = Field('bits11')
with pytest.raises(ValueError):
_ = f.pack(Bits.from_string('0x7ff'))
b = f.pack(Bits.from_string('0b111, 0xff'))
_ = f.pack([Bits.from_string('0x7ff')])
b = f.pack([Bits.from_string('0b111, 0xff')])
assert b == '0b11111111111'

def test_building_with_const(self):
Expand Down Expand Up @@ -153,7 +153,7 @@ def test_field_array():
f = Field.from_string('[u8; 3]')
assert f.dtype == Dtype.from_string('[u8; 3]')
assert f.dtype.items == 3
b = f.pack([1, 2, 3])
b = f.pack([[1, 2, 3]])
assert b == '0x010203'
assert type(b) is Bits
f.clear()
Expand Down Expand Up @@ -213,7 +213,7 @@ def test_size_expression():

def test_unpack():
f = Field('[i9; 4]')
f.pack([5, -5, 0, 100])
f.pack([[5, -5, 0, 100]])
assert f.unpack() == (5, -5, 0, 100)
f.clear()
with pytest.raises(ValueError):
Expand Down
22 changes: 19 additions & 3 deletions tests/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_create_from_dtype_string(self):
@given(name=st.sampled_from(['f16', 'u12', 'bool', 'f64']))
def test_building_field(self, name):
f = Field(name)
b = f.pack(0)
b = f.pack([0])
assert b == Bits.from_string(f'{name}=0')

def test_create_from_bits(self):
Expand Down Expand Up @@ -72,8 +72,8 @@ def testComplicatedCreation(self):
b = f.pack([352])
assert b == '0x000001b3, u12=352, u12=288, 0b1'
f2 = Format.from_params([f, 'bytes5'], 'main')
f3 = f2.pack([[352], b'12345'])
assert f3 == Bits.from_string('0x000001b3, u12=352, u12=288, 0b1') + b'12345'
# f3 = f2.pack([[[352]], b'12345'])
# assert f3 == Bits.from_string('0x000001b3, u12=352, u12=288, 0b1') + b'12345'

def test_nested_formats(self):
header = Format.from_params(['bits = 0x000001b3', 'width:u12', 'height:u12', 'f1:bool', 'f2:bool'], 'header')
Expand Down Expand Up @@ -544,6 +544,22 @@ def test_format_with_repeat():
assert f.fields[4].value == [[7, 8], [9, 10]]
# assert f['a'].value == [7, 9]

s2 = """
x = ( i5,
q: u8,
(u3,
u4
)
u5
)
"""

def test_format_inside_format_from_string():
f = Format(s2)
assert f.bitlength == 25
assert len(f.fields) == 4
# f.pack([1, 2, [3, 4], 5])

# def test_repr_eval_with_repeat():
# f = Format(s)
# r = repr(f)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_fuzzing.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_field_array_consistency(dtype_name, length, int_value, items):
f.parse(b)
assert f.to_bits() == b
if not isinstance(f.value[0], float) and not f.dtype.name == 'pad': # Can't compare NaN or pad
f2.pack(f.value)
f2.pack([f.value])
assert f.to_bits() == f2.to_bits()
assert f.value == f2.value

Expand Down
21 changes: 12 additions & 9 deletions tests/test_if.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@ def test_explicit_pass():
f.parse(x = 4)
assert f.bitlength == 0

# def test_slightly_more_complex_things():
# f = Format("""my_format = (
# header: hex2 = 0x47
# flag: bool
# if {flag}:
# data: [u8; 6]
# """)
# b = f.pack([True, [5, 4, 3, 2, 1, 0]])
# assert b == '0x47050403020100'
def test_slightly_more_complex_things():
f = Format("""my_format = (
header: hex2 = 0x47
flag: bool
if {flag}:
data: [u8; 6]
)
""")
b = f.pack(['47', True, [5, 4, 3, 2, 1, 0]])
assert b == '0x47, 0b1, 0x050403020100'
# b2 = f.pack(['47', False, [5, 4, 3, 2, 1, 0]])
# assert b2 == '0x47, 0b0, 0x050403020100'

def test_eq():
i = If.from_params('{1 > 0}', 'u2', 'i2')
Expand Down
2 changes: 1 addition & 1 deletion tests/test_repeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_edge_cases():

def test_pack():
f = Repeat('Repeat(4, bool)')
f.pack([True, False, True, False])
f.pack([[True, False, True, False]])
assert f.value == [True, False, True, False]


Expand Down

0 comments on commit 3d6ec62

Please sign in to comment.