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

benedict.dicts.keylist.keylist_util   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 120
dl 0
loc 154
rs 8.48
c 0
b 0
f 0
wmc 49

10 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
D _get_item_key_and_value_for_parent_wildcard() 0 34 13
A get_item() 0 3 2
A get_items() 0 20 5
B _set_item_value() 0 16 6
B _get_item_key_and_value() 0 19 8
A _get_item_key_and_value_for_wildcard() 0 8 5

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