Passed
Pull Request — master (#114)
by
unknown
01:42
created

_set_item_value()   B

Complexity

Conditions 6

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 16
rs 8.6666
c 0
b 0
f 0
cc 6
nop 3
1
# -*- coding: utf-8 -*-
2
3
from itertools import chain
4
5
from benedict.utils import type_util
6
7
8
def _get_index(key):
9
    if type_util.is_integer(key):
10
        return key
11
    return None
12
13
14
def _get_item_key_and_value_for_parent_wildcard(item, index, parent, child):
15
    if type_util.is_list_of_dicts(item) and any(
16
        index in _item.keys() for _item in item
17
    ):
18
        data = [_item.get(index) for _item in item if index in _item.keys()]
19
        if type_util.is_list_of_list(data):
20
            data = list(chain.from_iterable(data))
21
        # eject dict from list to be able to access dict properties
22
        if (
23
            len(data) == 1
24
            and len(item) == 1
25
            and type_util.is_wildcard(parent)
26
            and not type_util.is_wildcard(index)
27
            and not type_util.is_wildcard(child)
28
        ):
29
            data = data[0]
30
        return index, data
31
    elif type_util.is_list_of_list(item):
32
        if type_util.is_integer(index):
33
            data = [_item[index] for _item in item if index < len(_item)]
34
            return index, data
35
        else:
36
            data = [
37
                _item.get(index)
38
                for _item in chain.from_iterable(item)
39
                if index in _item.keys()
40
            ]
41
            return index, data
42
    elif type_util.is_wildcard(index):
43
        return index, item
44
    return index, None
45
46
47
def _get_item_key_and_value_for_wildcard(item, index):
48
    if type_util.is_list_of_dicts(item) and not type_util.is_integer(index):
49
        data = [_item.get(index) for _item in item if index in _item.keys()]
50
        if type_util.is_list_of_list(data):
51
            data = list(chain.from_iterable(data))
52
        if any(data):
53
            return index, data
54
    return index, item
55
56
57
def _get_item_key_and_value(item, index, parent=None, child=None):
58
    if type_util.is_list_or_tuple(item):
59
        if type_util.is_wildcard(parent):
60
            index, item = _get_item_key_and_value_for_parent_wildcard(
61
                item, index, parent, child
62
            )
63
            if item:
64
                return index, item
65
        elif type_util.is_wildcard(index):
66
            index, item = _get_item_key_and_value_for_wildcard(item, index)
67
            if item:
68
                return index, item
69
        else:
70
            index = _get_index(index)
71
            if index is not None:
72
                return index, item[index]
73
    elif type_util.is_dict(item):
74
        return index, item[index]
75
    raise KeyError(f"Invalid key: '{index}'")
76
77
78
def _get_or_new_item_value(item, key, subkey):
79
    try:
80
        _, value = _get_item_key_and_value(item, key)
81
        if not type_util.is_dict_or_list_or_tuple(value):
82
            raise TypeError
83
    except (IndexError, KeyError, TypeError):
84
        value = _new_item_value(subkey)
85
        _set_item_value(item, key, value)
86
    return value
87
88
89
def _new_item_value(key):
90
    index = _get_index(key)
91
    return {} if index is None else []
92
93
94
def _set_item_value(item, key, value):
95
    index = _get_index(key)
96
    if index is not None:
97
        try:
98
            # overwrite existing index
99
            item[index] = value
100
        except IndexError:
101
            # insert index
102
            item += [None] * (index - len(item))
103
            item.insert(index, value)
104
    elif type_util.is_list(item):
105
        for idx, _item in enumerate(value):
106
            if _item is not None:
107
                item[idx].update({key: _item})
108
    else:
109
        item[key] = value
110
111
112
def get_item(d, keys):
113
    items = get_items(d, keys)
114
    return items[-1] if items else (None, None, None)
115
116
117
def get_items(d, keys):
118
    items = []
119
    item = d
120
    for index, key in enumerate(keys):
121
        try:
122
            if any(items):
123
                parent = items[-1][1]
124
            else:
125
                parent = None
126
            if index < len(keys) - 1:
127
                child = keys[index + 1]
128
            else:
129
                child = None
130
            item_key, item_value = _get_item_key_and_value(item, key, parent, child)
131
            items.append((item, item_key, item_value))
132
            item = item_value
133
        except (IndexError, KeyError):
134
            items.append((None, None, None))
135
            break
136
    return items
137
138
139
def set_item(d, keys, value):
140
    item = d
141
    i = 0
142
    j = len(keys)
143
    while i < j:
144
        key = keys[i]
145
        if i < (j - 1):
146
            item = _get_or_new_item_value(item, key, keys[i + 1])
147
            i += 1
148
            continue
149
        _set_item_value(item, key, value)
150
        break
151