Passed
Pull Request — master (#153)
by Ramon
01:12
created

default_dict_deserializer()   B

Complexity

Conditions 5

Size

Total Lines 42
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
dl 0
loc 42
rs 8.8373
c 0
b 0
f 0
cc 5
nop 5
1
from typing import Callable, Optional, Tuple
2
3
from jsons._common_impl import JSON_KEYS
4
from jsons._load_impl import load
5
from jsons.exceptions import DeserializationError
6
7
from typish import get_args
8
9
10
def default_dict_deserializer(
11
        obj: dict,
12
        cls: type,
13
        *,
14
        key_transformer: Optional[Callable[[str], str]] = None,
15
        **kwargs) -> dict:
16
    """
17
    Deserialize a dict by deserializing all instances of that dict.
18
    :param obj: the dict that needs deserializing.
19
    :param key_transformer: a function that transforms the keys to a different
20
    style (e.g. PascalCase).
21
    :param cls: not used.
22
    :param kwargs: any keyword arguments.
23
    :return: a deserialized dict instance.
24
    """
25
    key_tfr = key_transformer or (lambda key: key)
26
    cls_args = get_args(cls)
27
    kwargs_ = {**kwargs, 'key_transformer': key_transformer}
28
29
    (obj_, had_stored_keys) = _load_hashed_keys(obj, cls, cls_args,
30
                                                key_transformer=key_transformer, **kwargs)
31
32
    if len(cls_args) == 2:
33
        cls_k, cls_v = cls_args
34
        kwargs_k = {**kwargs_, 'cls': cls_k}
35
        kwargs_v = {**kwargs_, 'cls': cls_v}
36
        if (had_stored_keys and
37
                not any(issubclass(type(cls_k), json_key) for json_key in JSON_KEYS)):
38
            # There were hashed keys and the key is not a json key, therefore
39
            # the key must have been hashed and therefore loaded already during _load_hashed_keys
40
            # double deserializing under strict will fail, so avoid doing so.
41
            res = {key_tfr(k): load(obj_[k], **kwargs_v)
42
                   for k in obj_}
43
        else:
44
            # The key was either inherently json compatible or serialized to be so, thus
45
            # avoiding hashed keys
46
            res = {load(key_tfr(k), **kwargs_k): load(obj_[k], **kwargs_v)
47
                   for k in obj_}
48
    else:
49
        res = {key_tfr(key): load(obj_[key], **kwargs_)
50
               for key in obj_}
51
    return res
52
53
54
def _load_hashed_keys(obj: dict, cls: type, cls_args: tuple, **kwargs) -> Tuple[dict, bool]:
55
    # Load any hashed keys and return a copy of the given obj if any hashed
56
    # keys are unpacked.
57
    result = obj
58
59
    stored_keys = set(obj.get('-keys', set()))
60
    if stored_keys:
61
        # Apparently, there are stored hashed keys, we need to unpack them.
62
        if len(cls_args) != 2:
63
            raise DeserializationError('A detailed type is needed for cls of '
64
                                       'the form Dict[<type>, <type>] to '
65
                                       'deserialize a dict with hashed keys.',
66
                                       obj, cls)
67
        result = {**obj}
68
        key_type = cls_args[0]
69
        for key in stored_keys:
70
            # Get the original (unhashed) key and load it.
71
            original_key = result['-keys'][key]
72
            loaded_key = load(original_key, cls=key_type, **kwargs)
73
74
            # Replace the hashed key by the loaded key entirely.
75
            result[loaded_key] = result[key]
76
            del result['-keys'][key]
77
            del result[key]
78
79
        del result['-keys']
80
    return (result, len(stored_keys) > 0)
81