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

benedict.dicts.keylist.keylist_util   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 102
dl 0
loc 129
rs 8.8798
c 0
b 0
f 0
wmc 44

8 Functions

Rating   Name   Duplication   Size   Complexity  
A _get_index() 0 4 2
A set_item() 0 12 3
A _new_item_value() 0 3 2
A _get_or_new_item_value() 0 9 3
A get_item() 0 3 2
A get_items() 0 20 5
B _set_item_value() 0 16 6
F _get_item_key_and_value() 0 40 21

How to fix   Complexity   

Complexity

Complex classes like benedict.dicts.keylist.keylist_util often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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