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