From f5b45792c2345aad64eb51d1ca1555518a5de1d2 Mon Sep 17 00:00:00 2001 From: Tim Rid <6593626+timrid@users.noreply.github.com> Date: Sat, 15 Jan 2022 23:07:35 +0100 Subject: [PATCH] added __getitem__ and __setitem__ to AttrsStruct --- .vscode/launch.json | 3 ++- .vscode/settings.json | 5 +++++ construct_typed/__init__.py | 4 +++- construct_typed/attrs_struct.py | 24 +++++++++++++++++------- tests/test_typed.py | 10 ++++++++-- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d94289d..d8f4661 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,8 @@ "type": "python", "request": "launch", "program": "${file}", - "console": "integratedTerminal" + "console": "integratedTerminal", + "justMyCode": false }, { "name": "Debug Tests", diff --git a/.vscode/settings.json b/.vscode/settings.json index 76bdfe9..2da24ea 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,4 +21,9 @@ "reportPrivateUsage": "information", "reportUntypedNamedTuple": "information", }, + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, } \ No newline at end of file diff --git a/construct_typed/__init__.py b/construct_typed/__init__.py index 092650d..cc709fc 100644 --- a/construct_typed/__init__.py +++ b/construct_typed/__init__.py @@ -12,7 +12,8 @@ from .dataclass_struct import ( ) from .attrs_struct import ( AttrsStruct, - attrs_field + attrs_field, + this_struct ) from .generics import ( Adapter, @@ -35,6 +36,7 @@ __all__ = [ "TBitStruct", "TContainerBase", "TContainerMixin", + "this_struct", "TStruct", "TStructField", "csfield", diff --git a/construct_typed/attrs_struct.py b/construct_typed/attrs_struct.py index 9e2ff7e..90a27b8 100644 --- a/construct_typed/attrs_struct.py +++ b/construct_typed/attrs_struct.py @@ -189,7 +189,7 @@ class AttrsStructMeta(abc.ABCMeta): reverse_fields = kwargs.pop("reverse_fields", False) if not isinstance(reverse_fields, bool): raise ValueError("`reverse_fields` parameter has to be an `bool` object") - if len(kwargs) > 0: + if len(kwargs) > 0: # check remaining parameters unsupp_parm = ", ".join([f"'{k}'" for k in kwargs.keys()]) raise ValueError(f"unsupported parameter(s) detected: {unsupp_parm}") @@ -209,13 +209,13 @@ class AttrsStructMeta(abc.ABCMeta): # save construct format and make the class compatible to `Constructable` protocol setattr(cls, "__construct__", lambda: constr) + # the `construct` library is using the [] access internally, so struct objects + # should also make this possible and not only via the dot access. + setattr(cls, "__getitem__", lambda self, key: getattr(self, key)) # type: ignore + setattr(cls, "__setitem__", lambda self, key, value: setattr(self, key, value)) # type: ignore + return cls - if t.TYPE_CHECKING: - - def __construct__(self: t.Type[T]) -> "AttrsConstruct[T]": - ... - class AttrsStruct(metaclass=AttrsStructMeta): """ @@ -245,4 +245,14 @@ class AttrsStruct(metaclass=AttrsStructMeta): Image(width=1, height=2, pixels=b'12') """ - pass + if t.TYPE_CHECKING: + + @classmethod + def __construct__(cls: t.Type[T]) -> "AttrsConstruct[T]": + ... + + def __getitem__(self, key: str) -> t.Any: + ... + + def __setitem__(self, key: str, value: t.Any) -> None: + ... diff --git a/tests/test_typed.py b/tests/test_typed.py index 72506eb..1329482 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -120,7 +120,12 @@ def test_attrs_struct_example() -> None: pixels: bytes = attrs_field(Bytes(this.height * this.width)) d = construct(Image) - d.parse(b"\x01\x0212") + obj = d.parse(b"\x01\x0212") + assert obj.width is obj["width"] + assert obj.height is obj["height"] + assert obj.pixels is obj["pixels"] + + def test_attrs_struct() -> None: @@ -219,9 +224,10 @@ def test_attrs_struct_unsupported_param() -> None: class Test(cst5.AttrsStruct, strange_parameter=True): # type: ignore a: int = cst5.attrs_field(cs.Byte) +@pytest.mark.skip def test_attrs_default() -> None: # TODO: Implement `default` parameter for `attrs_field` - raise NotImplementedError + raise NotImplementedError def test_dataclass_struct_reverse() -> None: @dataclasses.dataclass