From ae440407e944dc1be924a37f838c19e50f7ee269 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Tue, 16 Mar 2021 18:51:04 +0100 Subject: [PATCH] - renamed "TStructField" to "field" and "TUnionField" to "ufield". This makes the dataclass definitions shorter and more readable. - removed "Opt" and "List" because they are not necessary any more, because covariant is now included for ParsedType. - added test with typing.List as field - fix: changed dict to typing.Dict --- README.md | 10 ++-- construct-stubs/core.pyi | 2 +- construct-stubs/lib/hex.pyi | 4 +- construct_typed/__init__.py | 12 ++--- construct_typed/helper.py | 9 ---- construct_typed/tstruct.py | 3 +- construct_typed/tunion.py | 4 +- tests/declarativeunittest.py | 1 + tests/test_typed.py | 93 ++++++++++++++++++++---------------- 9 files changed, 72 insertions(+), 66 deletions(-) delete mode 100644 construct_typed/helper.py diff --git a/README.md b/README.md index fed1687..12f5692 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,11 @@ class Orientation(cst.EnumBase): @dataclasses.dataclass class Image(cst.TContainerBase): - signature: cst.Opt[bytes] = cst.TStructField(cs.Const(b"BMP")) - orientation: Orientation = cst.TStructField(cst.TEnum(cs.Int8ub, Orientation)) - width: int = cst.TStructField(cs.Int8ub) - height: int = cst.TStructField(cs.Int8ub) - pixels: cst.List[int] = cst.TStructField(cs.Array(cs.this.width * cs.this.height, cs.Byte)) + signature: cst.Opt[bytes] = cst.sfield(cs.Const(b"BMP")) + orientation: Orientation = cst.sfield(cst.TEnum(cs.Int8ub, Orientation)) + width: int = cst.sfield(cs.Int8ub) + height: int = cst.sfield(cs.Int8ub) + pixels: cst.List[int] = cst.sfield(cs.Array(cs.this.width * cs.this.height, cs.Byte)) format = cst.TStruct(Image) obj = Image(orientation=Orientation.VERTICAL, width=3, height=2, pixels=[7, 8, 9, 11, 12, 13]) diff --git a/construct-stubs/core.pyi b/construct-stubs/core.pyi index 2214e02..9117574 100644 --- a/construct-stubs/core.pyi +++ b/construct-stubs/core.pyi @@ -1049,7 +1049,7 @@ class Lazy( ] ): ... -class LazyContainer(t.Generic[ContainerType], dict[str, ContainerType]): +class LazyContainer(t.Generic[ContainerType], t.Dict[str, ContainerType]): def __getattr__(self, name: str) -> ContainerType: ... def __getitem__(self, index: t.Union[str, int]) -> ContainerType: ... def keys(self) -> t.Iterator[str]: ... diff --git a/construct-stubs/lib/hex.pyi b/construct-stubs/lib/hex.pyi index 052e390..afa985f 100644 --- a/construct-stubs/lib/hex.pyi +++ b/construct-stubs/lib/hex.pyi @@ -7,6 +7,6 @@ class HexDisplayedBytes(bytes): ... K = t.TypeVar("K") V = t.TypeVar("V") -class HexDisplayedDict(dict[K, V]): ... +class HexDisplayedDict(t.Dict[K, V]): ... class HexDumpDisplayedBytes(bytes): ... -class HexDumpDisplayedDict(dict[K, V]): ... +class HexDumpDisplayedDict(t.Dict[K, V]): ... diff --git a/construct_typed/__init__.py b/construct_typed/__init__.py index 19c09bd..d667d31 100644 --- a/construct_typed/__init__.py +++ b/construct_typed/__init__.py @@ -7,15 +7,16 @@ from .generic_wrapper import ( PathType, ) from .tenum import EnumBase, FlagsEnumBase, TEnum, TFlagsEnum -from .tstruct import TBitStruct, TStruct, TStructField, TContainerBase -from .tunion import TUnion, TUnionField -from .helper import List, Opt +from .tstruct import TBitStruct, TStruct, sfield, TStructField, TContainerBase +from .tunion import TUnion, ufield, TUnionField __all__ = [ + "sfield", "TStructField", "TStruct", "TBitStruct", "TEnum", + "ufield", "TUnionField", "TUnion", "EnumBase", @@ -27,7 +28,6 @@ __all__ = [ "ConstantOrContextLambda", "PathType", "TFlagsEnum", - "FlagsEnumBase", - "Opt", - "List" + "FlagsEnumBase" ] + diff --git a/construct_typed/helper.py b/construct_typed/helper.py deleted file mode 100644 index 1fac233..0000000 --- a/construct_typed/helper.py +++ /dev/null @@ -1,9 +0,0 @@ -import typing as t - -from .generic_wrapper import ListContainer - -OptType = t.TypeVar("OptType") -ListType = t.TypeVar("ListType") - -Opt = t.Optional[OptType] -List = t.Union[ListContainer[ListType], t.List[ListType]] \ No newline at end of file diff --git a/construct_typed/tstruct.py b/construct_typed/tstruct.py index fab73e1..4f25d88 100644 --- a/construct_typed/tstruct.py +++ b/construct_typed/tstruct.py @@ -50,7 +50,7 @@ class TContainerBase(_TContainerBase): self.move_to_end(field.name) -def TStructField( +def sfield( subcon: Construct[ParsedType, t.Any], doc: t.Optional[str] = None, parsed: t.Optional[t.Callable[[t.Any, Context], None]] = None, @@ -76,6 +76,7 @@ def TStructField( return field # type: ignore +TStructField = sfield # also support legacy name ContainerType = t.TypeVar("ContainerType", bound=TContainerBase) diff --git a/construct_typed/tunion.py b/construct_typed/tunion.py index 4651563..5aa8640 100644 --- a/construct_typed/tunion.py +++ b/construct_typed/tunion.py @@ -1,5 +1,4 @@ import dataclasses -import enum import textwrap import typing as t @@ -10,7 +9,7 @@ from .generic_wrapper import * DataclassType = t.TypeVar("DataclassType") -def TUnionField( +def ufield( subcon: Construct[ParsedType, t.Any], doc: t.Optional[str] = None, parsed: t.Optional[t.Callable[[t.Any, "cs.Context"], None]] = None, @@ -43,6 +42,7 @@ def TUnionField( return field # type: ignore +TUnionField = ufield # also support legacy name class TUnion(Adapter[t.Any, t.Any, DataclassType, DataclassType]): pass # TODO diff --git a/tests/declarativeunittest.py b/tests/declarativeunittest.py index d7512da..1d1be0c 100644 --- a/tests/declarativeunittest.py +++ b/tests/declarativeunittest.py @@ -49,6 +49,7 @@ def common(format, datasample, objsample, sizesample=SizeofError, **kw): def setattrs(obj, **kwargs): + """ Set multiple named values of an object """ for name, value in kwargs.items(): setattr(obj, name, value) return obj diff --git a/tests/test_typed.py b/tests/test_typed.py index 38b4467..5964553 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -6,6 +6,7 @@ import enum import construct as cs import construct_typed as cst import pytest +import typing as t from .declarativeunittest import common, raises, setattrs @@ -13,13 +14,13 @@ from .declarativeunittest import common, raises, setattrs def test_tcontainer_compare_with_dataclass() -> None: @dataclasses.dataclass class TestContainer: - a: cst.Opt[int] = cst.TStructField(cs.Const(1, cs.Byte)) - b: int = cst.TStructField(cs.Int8ub) + a: t.Optional[int] = cst.sfield(cs.Const(1, cs.Byte)) + b: int = cst.sfield(cs.Int8ub) @dataclasses.dataclass class TestTContainer(cst.TContainerBase): - a: cst.Opt[int] = cst.TStructField(cs.Const(1, cs.Byte)) - b: int = cst.TStructField(cs.Int8ub) + a: t.Optional[int] = cst.sfield(cs.Const(1, cs.Byte)) + b: int = cst.sfield(cs.Int8ub) datacls = TestContainer(b=1) tcontainer = TestTContainer(b=1) @@ -66,9 +67,9 @@ def test_tcontainer_compare_with_dataclass() -> None: def test_tcontainer_order() -> None: @dataclasses.dataclass class Image(cst.TContainerBase): - signature: cst.Opt[bytes] = cst.TStructField(cs.Const(b"BMP")) - width: int = cst.TStructField(cs.Int8ub) - height: int = cst.TStructField(cs.Int8ub) + signature: t.Optional[bytes] = cst.sfield(cs.Const(b"BMP")) + width: int = cst.sfield(cs.Int8ub) + height: int = cst.sfield(cs.Int8ub) format = cst.TStruct(Image) obj = Image(width=3, height=2) @@ -85,8 +86,8 @@ def test_tcontainer_order() -> None: def test_tstruct() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + 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) @@ -101,8 +102,8 @@ def test_tstruct() -> None: def test_tstruct_swapped() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) common( cst.TStruct(TestContainer, swapped=True), @@ -118,8 +119,8 @@ def test_tstruct_swapped() -> None: def test_tstruct_add_offsets() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) common( cst.TStruct(TestContainer, add_offsets=True), @@ -140,9 +141,9 @@ def test_tstruct_nested() -> None: class TestContainer(cst.TContainerBase): @dataclasses.dataclass class InnerDataclass(cst.TContainerBase): - b: int = cst.TStructField(cs.Byte) + b: int = cst.sfield(cs.Byte) - a: InnerDataclass = cst.TStructField(cst.TStruct(InnerDataclass)) + a: InnerDataclass = cst.sfield(cst.TStruct(InnerDataclass)) common( cst.TStruct(TestContainer), @@ -155,9 +156,9 @@ def test_tstruct_nested() -> None: def test_tstruct_default_field() -> None: @dataclasses.dataclass class Image(cst.TContainerBase): - width: int = cst.TStructField(cs.Int8ub) - height: int = cst.TStructField(cs.Int8ub) - pixels: cst.Opt[bytes] = cst.TStructField( + width: int = cst.sfield(cs.Int8ub) + height: int = cst.sfield(cs.Int8ub) + pixels: t.Optional[bytes] = cst.sfield( cs.Default( cs.Bytes(cs.this.width * cs.this.height), lambda ctx: bytes(ctx.width * ctx.height), @@ -175,7 +176,7 @@ def test_tstruct_default_field() -> None: def test_tstruct_const_field() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - const_field: cst.Opt[bytes] = cst.TStructField(cs.Const(b"\x00")) + const_field: t.Optional[bytes] = cst.sfield(cs.Const(b"\x00")) common( cst.TStruct(TestContainer), @@ -192,14 +193,26 @@ def test_tstruct_const_field() -> None: == cs.ConstError ) +def test_tstruct_array_field() -> None: + @dataclasses.dataclass + class TestContainer(cst.TContainerBase): + array_field: t.List[int] = cst.sfield(cs.Array(5, cs.Int8ub)) + + common( + cst.TStruct(TestContainer), + bytes(5), + TestContainer(array_field=[0,0,0,0,0]), + 5, + ) + def test_tstruct_anonymus_fields_1() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - _1: cst.Opt[bytes] = cst.TStructField(cs.Const(b"\x00")) - _2: None = cst.TStructField(cs.Padding(1)) - _3: None = cst.TStructField(cs.Pass) - _4: None = cst.TStructField(cs.Terminated) + _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) common( cst.TStruct(TestContainer), @@ -212,10 +225,10 @@ def test_tstruct_anonymus_fields_1() -> None: def test_tstruct_anonymus_fields_2() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - _1: int = cst.TStructField(cs.Computed(7)) - _2: cst.Opt[bytes] = cst.TStructField(cs.Const(b"JPEG")) - _3: None = cst.TStructField(cs.Pass) - _4: None = cst.TStructField(cs.Terminated) + _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) d = cst.TStruct(TestContainer) assert d.build(TestContainer()) == d.build(TestContainer()) @@ -223,8 +236,8 @@ def test_tstruct_anonymus_fields_2() -> None: def test_tstruct_no_dataclass() -> None: class TestContainer(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) assert raises(lambda: cst.TStruct(TestContainer)) == TypeError @@ -232,8 +245,8 @@ def test_tstruct_no_dataclass() -> None: def test_tstruct_no_tcontainerbase() -> None: @dataclasses.dataclass class TestContainer: - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) assert raises(lambda: cst.TStruct(TestContainer)) == TypeError @@ -241,13 +254,13 @@ def test_tstruct_no_tcontainerbase() -> None: def test_tstruct_wrong_container() -> None: @dataclasses.dataclass class TestContainer1(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) @dataclasses.dataclass class TestContainer2(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub) - b: int = cst.TStructField(cs.Int8ub) + a: int = cst.sfield(cs.Int16ub) + b: int = cst.sfield(cs.Int8ub) assert ( raises(cst.TStruct(TestContainer1).build, TestContainer2(a=1, b=2)) == TypeError @@ -257,11 +270,11 @@ def test_tstruct_wrong_container() -> None: def test_tstruct_doc() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - a: int = cst.TStructField(cs.Int16ub, "This is the documentation of a") - b: int = cst.TStructField( + a: int = cst.sfield(cs.Int16ub, "This is the documentation of a") + b: int = cst.sfield( cs.Int8ub, doc="This is the documentation of b\nwhich is multiline" ) - c: int = cst.TStructField( + c: int = cst.sfield( cs.Int8ub, """ This is the documentation of c @@ -332,8 +345,8 @@ def test_tenum_in_tstruct() -> None: @dataclasses.dataclass class TestContainer(cst.TContainerBase): - a: TestEnum = cst.TStructField(cst.TEnum(cs.Int8ub, TestEnum)) - b: int = cst.TStructField(cs.Int8ub) + a: TestEnum = cst.sfield(cst.TEnum(cs.Int8ub, TestEnum)) + b: int = cst.sfield(cs.Int8ub) common( cst.TStruct(TestContainer),