From 5a39c51321920349594b0ea7a8f4bbb665adbacb Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Sat, 22 May 2021 23:36:54 +0200 Subject: [PATCH] - renamed classes/methods/file: - "tstruct.py" -> "dataclass_struct.py" - "TStruct" -> "DataclassStruct" - "TBitStruct" -> "DataclassBitStruct" - "sfield" -> "csfield" - "TContainerMixin" -> "DataclassMixin" - extended documentation --- construct_typed/__init__.py | 6 +- .../{tstruct.py => dataclass_struct.py} | 119 +++++--- tests/test_typed.py | 267 +++++++++--------- 3 files changed, 226 insertions(+), 166 deletions(-) rename construct_typed/{tstruct.py => dataclass_struct.py} (60%) diff --git a/construct_typed/__init__.py b/construct_typed/__init__.py index 017a86f..cf82919 100644 --- a/construct_typed/__init__.py +++ b/construct_typed/__init__.py @@ -7,10 +7,14 @@ from .generic_wrapper import ( PathType, ) from .tenum import EnumBase, FlagsEnumBase, TEnum, TFlagsEnum -from .tstruct import TBitStruct, TStruct, sfield, TStructField, TContainerMixin, TContainerBase +from .dataclass_struct import DataclassStruct, csfield, TBitStruct, TStruct, sfield, TStructField, TContainerMixin, TContainerBase, DataclassBitStruct, DataclassMixin from .tunion import TUnion, ufield, TUnionField __all__ = [ + "DataclassStruct", + "DataclassBitStruct", + "csfield", + "DataclassMixin", "sfield", "TStructField", "TStruct", diff --git a/construct_typed/tstruct.py b/construct_typed/dataclass_struct.py similarity index 60% rename from construct_typed/tstruct.py rename to construct_typed/dataclass_struct.py index dd3bfe6..a590a1e 100644 --- a/construct_typed/tstruct.py +++ b/construct_typed/dataclass_struct.py @@ -13,10 +13,9 @@ from construct.lib.py3compat import bytestringtype, reprstring, unicodestringtyp from .generic_wrapper import Adapter, Construct, Context, ParsedType, PathType -class TContainerMixin: +class DataclassMixin: """ - Base class for a Container of a TStruct and a TBitStruct. This class has always to be mixed - with "dataclasses.dataclass". + Mixin for the dataclasses which are passed to "DataclassStruct" and "DataclassBitStruct". Note: This implementation is different to the 'cs.Container' of the original 'construct' library. In the original 'cs.Container' some names like "update", "keys", "items", ... can @@ -72,13 +71,15 @@ class TContainerMixin: return "".join(text) -def sfield( +def csfield( subcon: Construct[ParsedType, t.Any], doc: t.Optional[str] = None, parsed: t.Optional[t.Callable[[t.Any, Context], None]] = None, ) -> ParsedType: """ - Create a dataclass field for a "TStruct" and "TBitStruct" from a subcon. + Helper method for "DataclassStruct" and "DataclassBitStruct" to create the dataclass fields. + + This method also processes Const and Default, to pass these values als default values to the dataclass. """ orig_subcon = subcon @@ -116,12 +117,35 @@ def sfield( ) -ContainerType = t.TypeVar("ContainerType", bound=TContainerMixin) +DataclassType = t.TypeVar("DataclassType", bound=DataclassMixin) -class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): +class DataclassStruct(Adapter[t.Any, t.Any, DataclassType, DataclassType]): """ - Typed struct, based on standard dataclasses. + Adapter for a dataclasses for optimised type hints / static autocompletion in comparision to the original Struct. + + Before this construct can be created a dataclasses.dataclass type must be created, which must also derive from DataclassMixin. In this dataclass all fields must be assigned to a construct type using csfield. + + Internally, all fields are converted to a Struct, which does the actual parsing/building. + + Parses to a dataclasses.dataclass instance, and builds from such instance (although it also builds from dicts). Size is the sum of all subcon sizes, unless any subcon raises SizeofError. + + :param dc_type: Type of the dataclass, which also inherits from DataclassMixin + :param reverse: Flag if the fields of the dataclass should be reversed + + Example:: + + >>> import dataclasses + >>> from construct import Bytes, Int8ub, this + >>> from construct_typed import DataclassMixin, DataclassStruct, csfield + >>> @dataclasses.dataclass + ... class Image(DataclassMixin): + ... width: int = csfield(Int8ub) + ... height: int = csfield(Int8ub) + ... pixels: bytes = csfield(Bytes(this.height * this.width)) + >>> d = DataclassStruct(Image) + >>> d.parse(b"\x01\x0212") + Image(width=1, height=2, pixels=b'12') """ subcon: "cs.Struct[t.Any, t.Any]" @@ -129,29 +153,25 @@ class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): def __new__( cls, - container_type: t.Type[ContainerType], + dc_type: t.Type[DataclassType], reverse: bool = False, - ) -> "TStruct[ContainerType]": + ) -> "DataclassStruct[DataclassType]": ... def __init__( self, - container_type: t.Type[ContainerType], + dc_type: t.Type[DataclassType], reverse: bool = False, ) -> None: - if not issubclass(container_type, TContainerMixin): - raise TypeError( - f"'{repr(container_type)}' has to be a '{repr(TContainerMixin)}'" - ) - if not dataclasses.is_dataclass(container_type): - raise TypeError( - f"'{repr(container_type)}' has to be a 'dataclasses.dataclass'" - ) - self.container_type = container_type + if not issubclass(dc_type, DataclassMixin): + raise TypeError(f"'{repr(dc_type)}' has to be a '{repr(DataclassMixin)}'") + if not dataclasses.is_dataclass(dc_type): + raise TypeError(f"'{repr(dc_type)}' has to be a 'dataclasses.dataclass'") + self.dc_type = dc_type self.reverse = reverse # get all fields from the dataclass - fields = dataclasses.fields(self.container_type) + fields = dataclasses.fields(self.dc_type) if self.reverse: fields = tuple(reversed(fields)) @@ -168,9 +188,9 @@ class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): def _decode( self, obj: "cs.Container[t.Any]", context: Context, path: PathType - ) -> ContainerType: + ) -> DataclassType: # get all fields from the dataclass - fields = dataclasses.fields(self.container_type) + fields = dataclasses.fields(self.dc_type) # extract all fields from the container, that are used for create the dataclass object dc_init = {} @@ -180,7 +200,7 @@ class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): dc_init[field.name] = value # create object of dataclass - dc = self.container_type(**dc_init) # type: ignore + dc = self.dc_type(**dc_init) # type: ignore # extract all other values from the container, an pass it to the dataclass for field in fields: @@ -191,11 +211,11 @@ class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): return dc def _encode( - self, obj: ContainerType, context: Context, path: PathType + self, obj: DataclassType, context: Context, path: PathType ) -> t.Dict[str, t.Any]: - if isinstance(obj, self.container_type): + if isinstance(obj, self.dc_type): # get all fields from the dataclass - fields = dataclasses.fields(self.container_type) + fields = dataclasses.fields(self.dc_type) # extract all fields from the container, that are used for create the dataclass object ret_dict: t.Dict[str, t.Any] = {} @@ -205,18 +225,47 @@ class TStruct(Adapter[t.Any, t.Any, ContainerType, ContainerType]): return ret_dict raise TypeError( - "'{}' has to be of type {}".format(repr(obj), repr(self.container_type)) + "'{}' has to be of type {}".format(repr(obj), repr(self.dc_type)) ) -def TBitStruct( - container_type: t.Type[ContainerType], reverse: bool = False +def DataclassBitStruct( + dc_type: t.Type[DataclassType], reverse: bool = False ) -> t.Union[ - "cs.Transformed[ContainerType, ContainerType]", - "cs.Restreamed[ContainerType, ContainerType]", + "cs.Transformed[DataclassType, DataclassType]", + "cs.Restreamed[DataclassType, DataclassType]", ]: - return cs.Bitwise(TStruct(container_type, reverse)) + r""" + Makes a DataclassStruct inside a Bitwise. + + See :class:`~construct.core.Bitwise` and :class:`~construct_typed.dataclass_struct.DatclassStruct` for semantics and raisable exceptions. + + :param dc_type: Type of the dataclass, which also inherits from DataclassMixin + :param reverse: Flag if the fields of the dataclass should be reversed + + Example:: + + DataclassBitStruct <--> Bitwise(DataclassStruct(...)) + >>> import dataclasses + >>> from construct import BitsInteger, Flag, Nibble, Padding + >>> from construct_typed import DataclassBitStruct, DataclassMixin, csfield + >>> @dataclasses.dataclass + ... class TestDataclass(DataclassMixin): + ... a: int = csfield(Flag) + ... b: int = csfield(Nibble) + ... c: int = csfield(BitsInteger(10)) + ... d: None = csfield(Padding(1)) + >>> d = DataclassBitStruct(TestDataclass) + >>> d.parse(b"\x01\x02") + TestDataclass(a=False, b=0, c=129, d=None) + """ + return cs.Bitwise(DataclassStruct(dc_type, reverse)) -TContainerBase = TContainerMixin # also support legacy name -TStructField = sfield # also support legacy name +# support legacy names +TStruct = DataclassStruct +TBitStruct = DataclassBitStruct +TContainerMixin = DataclassMixin +TContainerBase = DataclassMixin +TStructField = csfield +sfield = csfield \ No newline at end of file diff --git a/tests/test_typed.py b/tests/test_typed.py index f5d5ef3..4f948c2 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -4,20 +4,20 @@ import dataclasses import enum import construct as cs +from construct_typed import csfield, DataclassMixin, DataclassStruct, DataclassBitStruct import construct_typed as cst -import pytest import typing as t from .declarativeunittest import common, raises, setattrs -def test_tcontainer_const_default() -> None: +def test_dataclass_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)) - default_lambda: bytes = cst.sfield( + class ConstDefaultTest(DataclassMixin): + const_bytes: bytes = csfield(cs.Const(b"BMP")) + const_int: int = csfield(cs.Const(5, cs.Int8ub)) + default_int: int = csfield(cs.Default(cs.Int8ub, 28)) + default_lambda: bytes = csfield( cs.Default(cs.Bytes(cs.this.const_int), lambda ctx: bytes(ctx.const_int)) ) @@ -28,11 +28,11 @@ def test_tcontainer_const_default() -> None: assert a.default_lambda == None -def test_tcontainer_access() -> None: +def test_dataclass_access() -> None: @dataclasses.dataclass - class TestTContainer(cst.TContainerMixin): - a: t.Optional[int] = cst.sfield(cs.Const(1, cs.Byte)) - b: int = cst.sfield(cs.Int8ub) + class TestTContainer(DataclassMixin): + a: t.Optional[int] = csfield(cs.Const(1, cs.Byte)) + b: int = csfield(cs.Int8ub) tcontainer = TestTContainer(b=2) @@ -53,14 +53,14 @@ def test_tcontainer_access() -> None: assert raises(lambda: TestTContainer(a=0, b=1)) == TypeError -def test_tcontainer_str_repr() -> None: +def test_dataclass_str_repr() -> None: @dataclasses.dataclass - class Image(cst.TContainerMixin): - signature: t.Optional[bytes] = cst.sfield(cs.Const(b"BMP")) - width: int = cst.sfield(cs.Int8ub) - height: int = cst.sfield(cs.Int8ub) + class Image(DataclassMixin): + signature: t.Optional[bytes] = csfield(cs.Const(b"BMP")) + width: int = csfield(cs.Int8ub) + height: int = csfield(cs.Int8ub) - format = cst.TStruct(Image) + format = DataclassStruct(Image) obj = Image(width=3, height=2) assert ( str(obj) @@ -73,63 +73,68 @@ def test_tcontainer_str_repr() -> None: ) -def test_tstruct() -> None: +def test_dataclass_struct() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub) - 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" - assert c.b.name == "b" - assert c.a.subcon is cs.Int16ub - assert c.b.subcon is cs.Int8ub - - -def test_tstruct_reverse() -> None: - @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub) - b: int = cst.sfield(cs.Int8ub) + class Image(DataclassMixin): + width: int = csfield(cs.Int8ub) + height: int = csfield(cs.Int8ub) + pixels: bytes = csfield(cs.Bytes(cs.this.height * cs.this.width)) common( - cst.TStruct(TestContainer, reverse=True), + cst.DataclassStruct(Image), + b"\x01\x0212", + Image(width=1, height=2, pixels=b"12"), + ) + + # check __getattr__ + c = cst.DataclassStruct(Image) + assert c.width.name == "width" + assert c.height.name == "height" + assert c.width.subcon is cs.Int8ub + assert c.height.subcon is cs.Int8ub + + +def test_dataclass_struct_reverse() -> None: + @dataclasses.dataclass + class TestContainer(DataclassMixin): + a: int = csfield(cs.Int16ub) + b: int = csfield(cs.Int8ub) + + common( + DataclassStruct(TestContainer, reverse=True), b"\x02\x00\x01", TestContainer(a=1, b=2), 3, ) - normal = cst.TStruct(TestContainer) - reverse = cst.TStruct(TestContainer, reverse=True) + normal = DataclassStruct(TestContainer) + reverse = DataclassStruct(TestContainer, reverse=True) assert str(normal.parse(b"\x00\x01\x02")) == str(reverse.parse(b"\x02\x00\x01")) -def test_tstruct_nested() -> None: +def test_dataclass_struct_nested() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): + class TestContainer(DataclassMixin): @dataclasses.dataclass - class InnerDataclass(cst.TContainerMixin): - b: int = cst.sfield(cs.Byte) - c: bytes = cst.sfield(cs.Bytes(cs.this._.length)) + class InnerDataclass(DataclassMixin): + b: int = csfield(cs.Byte) + c: bytes = csfield(cs.Bytes(cs.this._.length)) - length: int = cst.sfield(cs.Byte) - a: InnerDataclass = cst.sfield(cst.TStruct(InnerDataclass)) + length: int = csfield(cs.Byte) + a: InnerDataclass = csfield(DataclassStruct(InnerDataclass)) common( - cst.TStruct(TestContainer), + DataclassStruct(TestContainer), b"\x02\x01\xF1\xF2", TestContainer(length=2, a=TestContainer.InnerDataclass(b=1, c=b"\xF1\xF2")), ) -def test_tstruct_default_field() -> None: +def test_dataclass_struct_default_field() -> None: @dataclasses.dataclass - class Image(cst.TContainerMixin): - width: int = cst.sfield(cs.Int8ub) - height: int = cst.sfield(cs.Int8ub) - pixels: t.Optional[bytes] = cst.sfield( + class Image(DataclassMixin): + width: int = csfield(cs.Int8ub) + height: int = csfield(cs.Int8ub) + pixels: t.Optional[bytes] = csfield( cs.Default( cs.Bytes(cs.this.width * cs.this.height), lambda ctx: bytes(ctx.width * ctx.height), @@ -137,20 +142,20 @@ def test_tstruct_default_field() -> None: ) common( - cst.TStruct(Image), + DataclassStruct(Image), b"\x02\x03\x00\x00\x00\x00\x00\x00", setattrs(Image(2, 3), pixels=bytes(6)), sample_building=Image(2, 3), ) -def test_tstruct_const_field() -> None: +def test_dataclass_struct_const_field() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - const_field: t.Optional[bytes] = cst.sfield(cs.Const(b"\x00")) + class TestContainer(DataclassMixin): + const_field: t.Optional[bytes] = csfield(cs.Const(b"\x00")) common( - cst.TStruct(TestContainer), + DataclassStruct(TestContainer), bytes(1), setattrs(TestContainer(), const_field=b"\x00"), 1, @@ -158,75 +163,75 @@ def test_tstruct_const_field() -> None: assert ( raises( - cst.TStruct(TestContainer).build, + DataclassStruct(TestContainer).build, setattrs(TestContainer(), const_field=b"\x01"), ) == cs.ConstError ) -def test_tstruct_array_field() -> None: +def test_dataclass_struct_array_field() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - array_field: t.List[int] = cst.sfield(cs.Array(5, cs.Int8ub)) + class TestContainer(DataclassMixin): + array_field: t.List[int] = csfield(cs.Array(5, cs.Int8ub)) common( - cst.TStruct(TestContainer), + DataclassStruct(TestContainer), bytes(5), TestContainer(array_field=[0, 0, 0, 0, 0]), 5, ) -def test_tstruct_anonymus_fields_1() -> None: +def test_dataclass_struct_anonymus_fields_1() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - _1: t.Optional[bytes] = cst.sfield(cs.Const(b"\x00")) - _2: None = cst.sfield(cs.Padding(1)) - _3: None = cst.sfield(cs.Pass) - _4: None = cst.sfield(cs.Terminated) + class TestContainer(DataclassMixin): + _1: t.Optional[bytes] = csfield(cs.Const(b"\x00")) + _2: None = csfield(cs.Padding(1)) + _3: None = csfield(cs.Pass) + _4: None = csfield(cs.Terminated) common( - cst.TStruct(TestContainer), + DataclassStruct(TestContainer), bytes(2), setattrs(TestContainer(), _1=b"\x00"), cs.SizeofError, ) -def test_tstruct_anonymus_fields_2() -> None: +def test_dataclass_struct_anonymus_fields_2() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - _1: int = cst.sfield(cs.Computed(7)) - _2: t.Optional[bytes] = cst.sfield(cs.Const(b"JPEG")) - _3: None = cst.sfield(cs.Pass) - _4: None = cst.sfield(cs.Terminated) + class TestContainer(DataclassMixin): + _1: int = csfield(cs.Computed(7)) + _2: t.Optional[bytes] = csfield(cs.Const(b"JPEG")) + _3: None = csfield(cs.Pass) + _4: None = csfield(cs.Terminated) - d = cst.TStruct(TestContainer) + d = DataclassStruct(TestContainer) assert d.build(TestContainer()) == d.build(TestContainer()) -def test_tstruct_overloaded_method() -> None: +def test_dataclass_struct_overloaded_method() -> None: # Test dot access to some names that are not accessable via dot # in the original 'cs.Container'. @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - clear: int = cst.sfield(cs.Int8ul) - copy: int = cst.sfield(cs.Int8ul) - fromkeys: int = cst.sfield(cs.Int8ul) - get: int = cst.sfield(cs.Int8ul) - items: int = cst.sfield(cs.Int8ul) - keys: int = cst.sfield(cs.Int8ul) - move_to_end: int = cst.sfield(cs.Int8ul) - pop: int = cst.sfield(cs.Int8ul) - popitem: int = cst.sfield(cs.Int8ul) - search: int = cst.sfield(cs.Int8ul) - search_all: int = cst.sfield(cs.Int8ul) - setdefault: int = cst.sfield(cs.Int8ul) - update: int = cst.sfield(cs.Int8ul) - values: int = cst.sfield(cs.Int8ul) + class TestContainer(DataclassMixin): + clear: int = csfield(cs.Int8ul) + copy: int = csfield(cs.Int8ul) + fromkeys: int = csfield(cs.Int8ul) + get: int = csfield(cs.Int8ul) + items: int = csfield(cs.Int8ul) + keys: int = csfield(cs.Int8ul) + move_to_end: int = csfield(cs.Int8ul) + pop: int = csfield(cs.Int8ul) + popitem: int = csfield(cs.Int8ul) + search: int = csfield(cs.Int8ul) + search_all: int = csfield(cs.Int8ul) + setdefault: int = csfield(cs.Int8ul) + update: int = csfield(cs.Int8ul) + values: int = csfield(cs.Int8ul) - d = cst.TStruct(TestContainer) + d = DataclassStruct(TestContainer) obj = d.parse( d.build( TestContainer( @@ -263,47 +268,48 @@ def test_tstruct_overloaded_method() -> None: assert obj.values == 14 -def test_tstruct_no_dataclass() -> None: - class TestContainer(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub) - b: int = cst.sfield(cs.Int8ub) +def test_dataclass_struct_no_dataclass() -> None: + class TestContainer(DataclassMixin): + a: int = csfield(cs.Int16ub) + b: int = csfield(cs.Int8ub) - assert raises(lambda: cst.TStruct(TestContainer)) == TypeError + assert raises(lambda: DataclassStruct(TestContainer)) == TypeError -def test_tstruct_no_TContainerMixin() -> None: +def test_dataclass_struct_no_DataclassMixin() -> None: @dataclasses.dataclass class TestContainer: - a: int = cst.sfield(cs.Int16ub) - b: int = cst.sfield(cs.Int8ub) + a: int = csfield(cs.Int16ub) + b: int = csfield(cs.Int8ub) - assert raises(lambda: cst.TStruct(TestContainer)) == TypeError + assert raises(lambda: DataclassStruct(TestContainer)) == TypeError -def test_tstruct_wrong_container() -> None: +def test_dataclass_struct_wrong_container() -> None: @dataclasses.dataclass - class TestContainer1(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub) - b: int = cst.sfield(cs.Int8ub) + class TestContainer1(DataclassMixin): + a: int = csfield(cs.Int16ub) + b: int = csfield(cs.Int8ub) @dataclasses.dataclass - class TestContainer2(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub) - b: int = cst.sfield(cs.Int8ub) + class TestContainer2(DataclassMixin): + a: int = csfield(cs.Int16ub) + b: int = csfield(cs.Int8ub) assert ( - raises(cst.TStruct(TestContainer1).build, TestContainer2(a=1, b=2)) == TypeError + raises(DataclassStruct(TestContainer1).build, TestContainer2(a=1, b=2)) + == TypeError ) -def test_tstruct_doc() -> None: +def test_dataclass_struct_doc() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - a: int = cst.sfield(cs.Int16ub, "This is the documentation of a") - b: int = cst.sfield( + class TestContainer(DataclassMixin): + a: int = csfield(cs.Int16ub, "This is the documentation of a") + b: int = csfield( cs.Int8ub, doc="This is the documentation of b\nwhich is multiline" ) - c: int = cst.sfield( + c: int = csfield( cs.Int8ub, """ This is the documentation of c @@ -311,7 +317,7 @@ def test_tstruct_doc() -> None: """, ) - format = cst.TStruct(TestContainer) + format = DataclassStruct(TestContainer) common(format, b"\x00\x01\x02\x03", TestContainer(a=1, b=2, c=3), 4) assert format.subcon.a.docs == "This is the documentation of a" @@ -322,23 +328,24 @@ def test_tstruct_doc() -> None: ) -# @pytest.mark.xfail(reason="not implemented yet") -def test_tbitstruct() -> None: +def test_dataclass_bitstruct() -> None: @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - a: int = cst.sfield(cs.BitsInteger(7)) - b: int = cst.sfield(cs.Bit) - c: int = cst.sfield(cs.BitsInteger(8)) + class TestContainer(DataclassMixin): + a: int = csfield(cs.BitsInteger(7)) + b: int = csfield(cs.Bit) + c: int = csfield(cs.BitsInteger(8)) + + print("") common( - cst.TBitStruct(TestContainer), + DataclassBitStruct(TestContainer), b"\xFD\x12", TestContainer(a=0x7E, b=1, c=0x12), 2, ) # check __getattr__ - c = cst.TStruct(TestContainer) + c = DataclassStruct(TestContainer) assert c.a.name == "a" assert c.b.name == "b" assert c.c.name == "c" @@ -375,7 +382,7 @@ def test_tenum_no_enumbase() -> None: assert raises(lambda: cst.TEnum(cs.Byte, E)) == TypeError -def test_tstruct_wrong_enumbase() -> None: +def test_dataclass_struct_wrong_enumbase() -> None: class E1(cst.EnumBase): a = 1 b = 2 @@ -393,12 +400,12 @@ def test_tenum_in_tstruct() -> None: b = 2 @dataclasses.dataclass - class TestContainer(cst.TContainerMixin): - a: TestEnum = cst.sfield(cst.TEnum(cs.Int8ub, TestEnum)) - b: int = cst.sfield(cs.Int8ub) + class TestContainer(DataclassMixin): + a: TestEnum = csfield(cst.TEnum(cs.Int8ub, TestEnum)) + b: int = csfield(cs.Int8ub) common( - cst.TStruct(TestContainer), + DataclassStruct(TestContainer), b"\x01\x02", TestContainer(a=TestEnum.a, b=2), 2,