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