From 944cf857973a8ef966720b6f1794ebf35c5659c3 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Fri, 1 Jan 2021 17:26:40 +0100 Subject: [PATCH] - added tests "test_tstruct_default_field" and "test_tstruct_const_field" - added setattrs --- construct_typed/__init__.py | 16 ++++++++++- construct_typed/tstruct.py | 9 +----- tests/declarativeunittest.py | 17 ++++++++--- tests/declarativeunittest.pyi | 2 ++ tests/test_typed.py | 53 ++++++++++++++++++++++++++++++++--- 5 files changed, 80 insertions(+), 17 deletions(-) diff --git a/construct_typed/__init__.py b/construct_typed/__init__.py index 15bb6fe..8dc8b6a 100644 --- a/construct_typed/__init__.py +++ b/construct_typed/__init__.py @@ -1,3 +1,11 @@ +from .generic_wrapper import ( + Construct, + Adapter, + ListContainer, + Context, + ConstantOrContextLambda, + PathType, +) from .tarray import TArray from .tenum import TEnum from .tstruct import TStruct, TBitStruct, TStructField @@ -10,5 +18,11 @@ __all__ = [ "TEnum", "TUnionField", "TUnion", - "TArray" + "TArray", + "Construct", + "Adapter", + "ListContainer", + "Context", + "ConstantOrContextLambda", + "PathType", ] diff --git a/construct_typed/tstruct.py b/construct_typed/tstruct.py index e6cb8f5..14c1c79 100644 --- a/construct_typed/tstruct.py +++ b/construct_typed/tstruct.py @@ -21,16 +21,9 @@ def TStructField( subcon = cs.Renamed(subcon, newdocs=doc, newparsed=parsed) if subcon.flagbuildnone is True: - # some subcons have a predefined default value. all other have "None" - default: t.Any = None - if isinstance(subcon, (cs.Const, cs.Default)): - if callable(subcon.value): # type: ignore - raise ValueError("lamda as default is not supported") - default = subcon.value # type: ignore - # if subcon builds from "None", set default to "None" field = dataclasses.field( - default=default, + default=None, init=False, metadata={"subcon": cs.Renamed(subcon, newdocs=doc)}, ) diff --git a/tests/declarativeunittest.py b/tests/declarativeunittest.py index 04447d1..d4c5adb 100644 --- a/tests/declarativeunittest.py +++ b/tests/declarativeunittest.py @@ -1,4 +1,5 @@ import pytest + xfail = pytest.mark.xfail skip = pytest.mark.skip skipif = pytest.mark.skipif @@ -8,16 +9,18 @@ import os, math, random, collections, itertools, io, hashlib, binascii from construct import * from construct.lib import * + class ZeroIO(io.BufferedIOBase): - def read(self, __size = None): + def read(self, __size=None): if __size is not None: return bytes(__size) else: return bytes(0) - def read1(self, __size = 0): + def read1(self, __size=0): return bytes(__size) + ident = lambda x: x devzero = ZeroIO() @@ -46,20 +49,26 @@ def common(format, datasample, objsample, sizesample=SizeofError, **kw): assert size == sizesample +def setattrs(obj, **kwargs): + for name, value in kwargs.items(): + setattr(obj, name, value) + return obj + + def commonhex(format, hexdata): commonbytes(format, binascii.unhexlify(hexdata)) def commondumpdeprecated(format, filename): filename = "tests/deprecated_gallery/blobs/" + filename - with open(filename,'rb') as f: + with open(filename, "rb") as f: data = f.read() commonbytes(format, data) def commondump(format, filename): filename = "tests/gallery/blobs/" + filename - with open(filename,'rb') as f: + with open(filename, "rb") as f: data = f.read() commonbytes(format, data) diff --git a/tests/declarativeunittest.pyi b/tests/declarativeunittest.pyi index fe8d4a4..83971cc 100644 --- a/tests/declarativeunittest.pyi +++ b/tests/declarativeunittest.pyi @@ -6,6 +6,7 @@ import construct_typed as cst Buffer = t.Union[bytes, memoryview, bytearray] ParsedType = t.TypeVar("ParsedType") BuildTypes = t.TypeVar("BuildTypes") +T = t.TypeVar("T") IdentType = t.TypeVar("IdentType") @@ -32,6 +33,7 @@ def common( sizesample: t.Union[int, t.Type[Exception]] = ..., **kw: t.Any ) -> None: ... +def setattrs(obj: T, **kwargs: t.Any) -> T: ... def commonhex(format: Construct[ParsedType, BuildTypes], hexdata: str) -> None: ... def commondumpdeprecated( format: Construct[ParsedType, BuildTypes], filename: str diff --git a/tests/test_typed.py b/tests/test_typed.py index b71212d..5394ba8 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -3,11 +3,12 @@ import enum import dataclasses import typing as t -from .declarativeunittest import common, raises +from .declarativeunittest import common, raises, setattrs import construct as cs import construct_typed as cst -def test_tstruct_1() -> None: + +def test_tstruct() -> None: @dataclasses.dataclass class TestDataclass: a: int = cst.TStructField(cs.Int16ub) @@ -50,6 +51,47 @@ def test_tstruct_nested() -> None: ) +def test_tstruct_default_field() -> None: + @dataclasses.dataclass + class Image: + width: int = cst.TStructField(cs.Int8ub) + height: int = cst.TStructField(cs.Int8ub) + pixels: t.Optional[bytes] = cst.TStructField( + cs.Default( + cs.Bytes(cs.this.width * cs.this.height), + lambda ctx: bytes(ctx.width * ctx.height), + ) + ) + + common( + cst.TStruct(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: + @dataclasses.dataclass + class TestDataclass: + const_field: t.Optional[bytes] = cst.TStructField(cs.Const(b"\x00")) + + common( + cst.TStruct(TestDataclass), + bytes(1), + setattrs(TestDataclass(), const_field=b"\x00"), + 1, + ) + + assert ( + raises( + cst.TStruct(TestDataclass).build, + setattrs(TestDataclass(), const_field=b"\x01"), + ) + == cs.ConstError + ) + + def test_tstruct_anonymus_fields_1() -> None: @dataclasses.dataclass class TestDataclass: @@ -61,7 +103,7 @@ def test_tstruct_anonymus_fields_1() -> None: common( cst.TStruct(TestDataclass), bytes(2), - TestDataclass(), + setattrs(TestDataclass(), _1=b"\x00"), cs.SizeofError, ) @@ -97,7 +139,10 @@ def test_tstruct_wrong_dataclass() -> None: a: int = cst.TStructField(cs.Int16ub) b: int = cst.TStructField(cs.Int8ub) - assert raises(cst.TStruct(TestDataclass1).build, TestDataclass2(a=1, b=2)) == TypeError + assert ( + raises(cst.TStruct(TestDataclass1).build, TestDataclass2(a=1, b=2)) == TypeError + ) + def test_tbitstruct() -> None: assert False