values from "cs.Const" and "cs.Default" are now set as default value from the dataclass

This commit is contained in:
Tim Rid 2021-04-13 21:33:11 +02:00
parent d092e48a68
commit 976e68d4b9
2 changed files with 38 additions and 10 deletions

View file

@ -61,18 +61,31 @@ def sfield(
"""
Create a dataclass field for a "TStruct" and "TBitStruct" from a subcon.
"""
orig_subcon = subcon
# Rename subcon, if doc or parsed are available
if (doc is not None) or (parsed is not None):
if doc is not None:
doc = textwrap.dedent(doc).strip("\n")
subcon = cs.Renamed(subcon, newdocs=doc, newparsed=parsed)
if subcon.flagbuildnone is True:
# if subcon builds from "None", set default to "None"
if isinstance(orig_subcon, (cs.Const, cs.Default)) and not callable(
orig_subcon.value
):
# Set simple values if cs.Const or cs.Default.
# If the value is a callable (or context lambda), then we cant set it because it
# is only defined at parsing/building
field = dataclasses.field(
default=orig_subcon.value,
init=False,
metadata={"subcon": subcon},
)
elif orig_subcon.flagbuildnone is True:
# If subcon builds from "None", set default to "None"
field = dataclasses.field(
default=None,
init=False,
metadata={"subcon": cs.Renamed(subcon, newdocs=doc)},
metadata={"subcon": subcon},
)
else:
field = dataclasses.field(metadata={"subcon": subcon})
@ -170,7 +183,7 @@ class _TStruct(Adapter[t.Any, t.Any, ContainerType, BuildTypes]):
fields = dataclasses.fields(self.container_type)
# extract all fields from the container, that are used for create the dataclass object
ret_dict = {}
ret_dict: t.Dict[str, t.Any] = {}
for field in fields:
value = getattr(obj, field.name)
ret_dict[field.name] = value

View file

@ -11,6 +11,19 @@ import typing as t
from .declarativeunittest import common, raises, setattrs
def test_tcontainer_const_default() -> None:
@dataclasses.dataclass
class ConstDefaultTest(cst.TContainerMixin):
const_bytes: bytes = cst.sfield(cs.Const(b"BMP"))
const_int: int = cst.sfield(cs.Const(5, cs.Int8ub))
default_int: int = cst.sfield(cs.Default(cs.Int8ub, 28))
a = ConstDefaultTest()
assert a.const_bytes == b"BMP"
assert a.const_int == 5
assert a.default_int == 28
def test_tcontainer_compare_with_dataclass() -> None:
@dataclasses.dataclass
class TestContainer:
@ -27,7 +40,7 @@ def test_tcontainer_compare_with_dataclass() -> None:
# ##### compare dot & dict access #####
# dataclass
assert datacls.a == None
assert datacls.a == 1
assert raises(lambda: datacls["a"]) == TypeError # type: ignore
assert datacls.b == 1
assert raises(lambda: datacls["b"]) == TypeError # type: ignore
@ -41,8 +54,8 @@ def test_tcontainer_compare_with_dataclass() -> None:
assert e.__class__ == TypeError
# tcontainer
assert tcontainer.a == None
assert tcontainer["a"] == None
assert tcontainer.a == 1
assert tcontainer["a"] == 1
assert tcontainer.b == 1
assert tcontainer["b"] == 1
@ -74,7 +87,8 @@ def test_tcontainer_order() -> None:
format = cst.TStruct(Image)
obj = Image(width=3, height=2)
assert (
str(obj) == "Container: \n signature = None\n width = 3\n height = 2"
str(obj)
== "Container: \n signature = b'BMP' (total 3)\n width = 3\n height = 2"
)
obj = format.parse(format.build(obj))
assert (
@ -90,7 +104,7 @@ def test_tstruct() -> None:
b: int = cst.sfield(cs.Int8ub)
common(cst.TStruct(TestContainer), b"\x00\x01\x02", TestContainer(a=1, b=2), 3)
# check __getattr__
c = cst.TStruct(TestContainer)
assert c.a.name == "a"
@ -193,6 +207,7 @@ def test_tstruct_const_field() -> None:
== cs.ConstError
)
def test_tstruct_array_field() -> None:
@dataclasses.dataclass
class TestContainer(cst.TContainerMixin):
@ -201,7 +216,7 @@ def test_tstruct_array_field() -> None:
common(
cst.TStruct(TestContainer),
bytes(5),
TestContainer(array_field=[0,0,0,0,0]),
TestContainer(array_field=[0, 0, 0, 0, 0]),
5,
)