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

benedict.dicts.keylist.keylist_util.get_items()   A

Complexity

Conditions 4

Size

Total Lines 16
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 16
rs 9.65
c 0
b 0
f 0
cc 4
nop 2
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):
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(index in _item.keys() for _item in item):
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
                return index, data
22
            elif type_util.is_list_of_list(item):
23
                data = [_item.get(index) for _item in chain.from_iterable(item) if index in _item.keys()]
24
                return index, data
25
            elif type_util.is_wildcard(index):
26
                return index, item
27
        elif type_util.is_wildcard(index):
28
            return index, item
29
        elif type_util.is_list_of_dicts(item) and not type_util.is_integer(index):
30
            data = [_item.get(index) for _item in item if index in _item.keys()]
31
            if type_util.is_list_of_list(data):
32
                data = list(chain.from_iterable(data))
33
            if any(data):
34
                return index, data
35
        else:
36
            index = _get_index(index)
37
            if index is not None:
38
                return index, item[index]
39
    elif type_util.is_dict(item):
40
        return index, item[index]
41
    raise KeyError(f"Invalid key: '{index}'")
42
43
44
def _get_or_new_item_value(item, key, subkey):
45
    try:
46
        _, value = _get_item_key_and_value(item, key)
47
        if not type_util.is_dict_or_list_or_tuple(value):
48
            raise TypeError
49
    except (IndexError, KeyError, TypeError):
50
        value = _new_item_value(subkey)
51
        _set_item_value(item, key, value)
52
    return value
53
54
55
def _new_item_value(key):
56
    index = _get_index(key)
57
    return {} if index is None else []
58
59
60
def _set_item_value(item, key, value):
61
    index = _get_index(key)
62
    if index is not None:
63
        try:
64
            # overwrite existing index
65
            item[index] = value
66
        except IndexError:
67
            # insert index
68
            item += [None] * (index - len(item))
69
            item.insert(index, value)
70
    elif type_util.is_list(item):
71
        for idx, _item in enumerate(value):
72
            if _item is not None:
73
                item[idx].update({key: _item})
74
    else:
75
        item[key] = value
76
77
78
def get_item(d, keys):
79
    items = get_items(d, keys)
80
    return items[-1] if items else (None, None, None)
81
82
83
def get_items(d, keys):
84
    items = []
85
    item = d
86
    for key in keys:
87
        try:
88
            if any(items):
89
                parent = items[-1][1]
90
            else:
91
                parent = None
92
            item_key, item_value = _get_item_key_and_value(item, key, parent)
93
            items.append((item, item_key, item_value))
94
            item = item_value
95
        except (IndexError, KeyError):
96
            items.append((None, None, None))
97
            break
98
    return items
99
100
101
def set_item(d, keys, value):
102
    item = d
103
    i = 0
104
    j = len(keys)
105
    while i < j:
106
        key = keys[i]
107
        if i < (j - 1):
108
            item = _get_or_new_item_value(item, key, keys[i + 1])
109
            i += 1
110
            continue
111
        _set_item_value(item, key, value)
112
        break
113