From 9b66f7d7b6fcf3ec004d0269f97709b6e3f85a74 Mon Sep 17 00:00:00 2001 From: Nandan Rao <nandanmarkrao@gmail.com> Date: Fri, 26 Feb 2021 12:39:54 +0100 Subject: [PATCH] Added support for Any and Dict types. rebased against 0.10.4 --- tests/test_decoding.py | 25 +++++++++++++++++++++++++ typedjson/annotation.py | 5 ++--- typedjson/decoding.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/tests/test_decoding.py b/tests/test_decoding.py index 71abb5d..9825932 100644 --- a/tests/test_decoding.py +++ b/tests/test_decoding.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from typing import Any +from typing import Dict from typing import Generic from typing import List from typing import Optional @@ -118,6 +119,30 @@ def test_can_decode_heterogeneous_list() -> None: assert typedjson.decode(List[Union[str, int]], json) == json +def test_can_decode_any_list() -> None: + json = [1, "foo"] + assert typedjson.decode(List[Any], json) == json + + +def test_can_decode_string_any_dict() -> None: + json = {"foo": 1, "bar": "baz"} + assert typedjson.decode(Dict[str, Any], json) == json + + +def test_cannot_decode_dict_with_bad_key() -> None: + json = {"foo": 1, 5: "baz"} + + expectation = DecodingError(TypeMismatch(("5",))) + assert typedjson.decode(Dict[str, Any], json) == expectation + + +def test_cannot_decode_dict_with_bad_value() -> None: + json = {"foo": 1, "baz": "baz"} + + expectation = DecodingError(TypeMismatch(("foo",))) + assert typedjson.decode(Dict[str, str], json) == expectation + + def test_can_decode_dataclass() -> None: json = {"id": "test-user", "age": 28, "name": {"first": "Tomoya", "last": "Kose"}} diff --git a/typedjson/annotation.py b/typedjson/annotation.py index 818efe9..fdd717e 100644 --- a/typedjson/annotation.py +++ b/typedjson/annotation.py @@ -20,9 +20,8 @@ def hints_of(type_: Type) -> Optional[Dict[str, Type]]: type__ = type_ if origin is None else origin mapping = dict(zip(parameters_of(type_), args)) - # if hasattr(type__, '__annotations__'): - if hasattr(type__, "__init__"): - annotations = get_type_hints(type__.__init__) + if hasattr(type__, "__annotations__"): + annotations = get_type_hints(type__) if len(mapping) > 0: annotations_: Dict[str, Type] = {} for n, t in annotations.items(): diff --git a/typedjson/decoding.py b/typedjson/decoding.py index 5467386..9c92d7f 100644 --- a/typedjson/decoding.py +++ b/typedjson/decoding.py @@ -2,6 +2,7 @@ from typing import Any from typing import Iterable +from typing import Dict from typing import Iterator from typing import List from typing import Optional @@ -10,6 +11,7 @@ from typing import TypeVar from typing import Union + Decoded = TypeVar("Decoded") Value = TypeVar("Value") @@ -82,6 +84,8 @@ def decode( decode_as_list, decode_as_primitive, decode_as_class, + decode_as_dict, + decode_as_any, ) result_final: Union[Decoded, DecodingError] = DecodingError( @@ -123,6 +127,42 @@ def decode_as_primitive( return DecodingError(UnsupportedDecoding(path)) +def decode_as_dict( + type_: Type[Decoded], json: Any, path: Path +) -> Union[Decoded, DecodingError]: + from typedjson.annotation import args_of + from typedjson.annotation import origin_of + + if isinstance(json, dict) and origin_of(type_) is dict: + KeyElement, ValueElement = args_of(type_) + dict_decoded: Dict[Any, Any] = {} + + for key, element in json.items(): + decoded_value = decode(ValueElement, element, path + (str(key),)) + if isinstance(decoded_value, DecodingError): + return decoded_value + + decoded_key = decode(KeyElement, key, path + (str(key),)) + if isinstance(decoded_key, DecodingError): + return decoded_key + + dict_decoded[decoded_key] = decoded_value + + return dict_decoded # type: ignore + else: + return DecodingError(UnsupportedDecoding(path)) + + +def decode_as_any( + type_: Type[Decoded], json: Any, path: Path +) -> Union[Decoded, DecodingError]: + + if type_ == Any: + return json # type: ignore + else: + return DecodingError(UnsupportedDecoding(path)) + + def decode_as_class( type_: Type[Decoded], json: Any, path: Path ) -> Union[Decoded, DecodingError]: