Passed
Push — master ( ec479b...376308 )
by sosei
01:02
created

multivalued_dict   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 100
dl 0
loc 244
rs 8.8798
c 0
b 0
f 0
wmc 44

17 Methods

Rating   Name   Duplication   Size   Complexity  
B multivalued_dict.__init__() 0 29 7
A multivalued_dict.items() 0 5 1
A multivalued_dict.__lenvalue__() 0 12 2
A multivalued_dict.count() 0 7 1
A multivalued_dict.values() 0 5 1
A multivalued_dict.copy() 0 5 1
B multivalued_dict.__delkv__() 0 33 7
A multivalued_dict.__is_multivalued_dict__() 0 8 3
C multivalued_dict.update() 0 24 10
A multivalued_dict.fromkeys() 0 10 1
A multivalued_dict.__matchkv__() 0 11 1
A multivalued_dict.__mvdict_init__() 0 19 4
A KeyValuePairsError.__repr__() 0 2 1
A multivalued_dict.keys() 0 5 1
A multivalued_dict.reverse() 0 5 1
A KeyValuePairsError.__init__() 0 2 1
A multivalued_dict.__repr__() 0 2 1

How to fix   Complexity   

Complexity

Complex classes like multivalued_dict 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
from collections import defaultdict
2
from collections import UserDict
3
from collections.abc import Iterable
4
from collections.abc import Hashable
5
from itertools import filterfalse
6
7
START_POS = 'S'
8
END_POS = 'E'
9
10
class MultivaluedDictError(Exception):
11
    pass
12
    
13
class KeyValuePairsError(MultivaluedDictError):
14
    def __init__(self, list_of_not_kv_pair):
15
        self.list_of_not_kv_pair = list_of_not_kv_pair
16
        
17
    def __repr__(self):
18
        return f'{list_of_not_kv_pair} does not form a key-value pair. '
19
    
20
class multivalued_dict(UserDict):
21
    '''
22
        >>> mv_d = multivalued_dict()
23
        >>> mv_d
24
        multivalued_dict({})
25
        
26
        >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
27
        >>> mv_d
28
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
29
        
30
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['b', 'test-2'], ['a', 'test-3']])
31
        >>> mv_d
32
        multivalued_dict({'a': ['test-1', 'test-3'], 'b': ['test-2']})
33
        
34
        >>> mv_d = multivalued_dict(a = 'test-1', b = 'test-2', c = 'test-3')
35
        >>> mv_d
36
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
37
        
38
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['c', 'test-3']], b = 'test-2')
39
        >>> mv_d
40
        multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': ['test-2']})
41
        
42
        >>> mv_d0 = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
43
        >>> mv_d0
44
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
45
        >>> mv_d = multivalued_dict(mv_d0)
46
        >>> mv_d
47
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
48
    '''
49
50
    @classmethod
51
    def __is_multivalued_dict__(cls, x):
52
        '''
53
        >>> mv_d = multivalued_dict()
54
        >>> multivalued_dict.__is_multivalued_dict__(mv_d)
55
        True
56
        '''
57
        return (isinstance(x, cls) or ((True if x.default_factory == type([]) else False) if isinstance(x, defaultdict) else False))
58
    
59
    def __init__(self, *args, **kwargs):
60
        '''
61
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
62
            >>> mv_d
63
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
64
            
65
            >>> mv_d.__init__({'d': 'test-4'})
66
            >>> mv_d
67
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4']})
68
            
69
            >>> multivalued_dict.__init__(mv_d, {'e': 'test-5'})
70
            >>> mv_d
71
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4'], 'e': ['test-5']})
72
        '''
73
        if not multivalued_dict.__is_multivalued_dict__(self):
74
            raise TypeError(f"descriptor '__init__' requires a 'multivalued_dict' object but received a {type(self)}")
75
        len_of_args = len(args)
76
        if len_of_args > 1:
77
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
78
        if 'data' not in self.__dict__:
79
            self.data = defaultdict(list)
80
        if len_of_args == 1:
81
            initial_items = args[0]
82
            if multivalued_dict.__is_multivalued_dict__(initial_items):
83
                self.__mvdict_init__(initial_items)
84
            else:
85
                self.update(initial_items)
86
        if kwargs != dict():
87
            self.update(kwargs)
88
89
    def __mvdict_init__(self, multivalued_init_items):
90
        '''
91
            >>> mv_d = multivalued_dict()
92
            >>> mv_d.__mvdict_init__(multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'}))
93
            >>> mv_d
94
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
95
            
96
            >>> multivalued_dict.__mvdict_init__(mv_d, multivalued_dict({'d': 'test-4'}))
97
            >>> mv_d
98
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4']})
99
        '''
100
        if multivalued_dict.__is_multivalued_dict__(multivalued_init_items):
101
            if 'data' not in self.__dict__:
102
                self.data = multivalued_init_items
103
            else:
104
                for _key, _value in multivalued_init_items.items():
105
                    self.data[_key].extend(_value)
106
        else:
107
            raise TypeError(f"{type(multivalued_init_items)}  objects are not multivalued_dict or defaultdict(<class 'list'>) objects. ")
108
    
109
    def __repr__(self):
110
        return f'multivalued_dict({dict(self.data)})'
111
    
112
    def __lenvalue__(self, key = None):
113
        '''
114
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
115
            >>> mv_d.__lenvalue__()
116
            6
117
            >>> mv_d.__lenvalue__('a')
118
            3
119
        '''
120
        if key == None:
121
            return sum(map(len, self.data.values()))
122
        else:
123
            return len(self.data[key])
124
    
125
    def __matchkv__(self, key, value):
126
        '''
127
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
128
            >>> mv_d.__matchkv__('b', 3)
129
            False
130
            >>> mv_d.__matchkv__('a', 2)
131
            True
132
            >>> mv_d.__matchkv__('d', 1)
133
            False
134
        '''
135
        return value in self.data[key]
136
        
137
    def __delkv__(self, key, value, allkv = True, direction = START_POS):
138
        '''
139
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'z'], ['a', 'y'], ['a', 'z'], ['a', 'y']])
140
            >>> mv_d
141
            multivalued_dict({'a': ['x', 'y', 'z', 'y', 'z', 'y']})
142
            
143
            >>> mv_d.__delkv__('a', 'y', False)
144
            >>> mv_d
145
            multivalued_dict({'a': ['x', 'z', 'y', 'z', 'y']})
146
            
147
            >>> mv_d.__delkv__('a', 'y', False, END_POS)
148
            >>> mv_d
149
            multivalued_dict({'a': ['x', 'z', 'y', 'z']})
150
            
151
            >>> mv_d.__delkv__('a', 'z')
152
            >>> mv_d
153
            multivalued_dict({'a': ['x', 'y']})
154
        '''
155
        assert allkv in (True, False), '"allkv" can only be True or False'
156
        assert direction in (START_POS, END_POS), '"direction" can only be START_POS or END_POS'
157
        
158
        if allkv:
159
            while value in self.data[key]:
160
                self.data[key].remove(value)
161
        else:
162
            if direction == START_POS:
163
                self.data[key].remove(value)
164
            elif direction == END_POS:
165
                value_len = len(self.data[key])
166
                for i in range(value_len):
167
                    if self.data[key][-1 - i] == value:
168
                        self.data[key].__delitem__(-1 - i)
169
                        break
170
171
    def count(self, key, value):
172
        '''
173
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'y'], ['a', 'z'], ['a', 'z'], ['a', 'z']])
174
            >>> mv_d.count('a', 'y')
175
            2
176
        '''
177
        return self.data[key].count(value)
178
    
179
    def update(self, *args, **kwargs):
180
        '''
181
            
182
        '''
183
        if not multivalued_dict.__is_multivalued_dict__(self):
184
            raise TypeError(f"descriptor 'update' requires a 'multivalued_dict' object but received a {type(self)}")
185
        len_of_args = len(args)
186
        if len_of_args > 1:
187
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
188
        if len_of_args == 1:
189
            update_items = args[0]
190
            if not isinstance(update_items, Iterable):
191
                raise TypeError(f'{type(update_items)} object is not iterable ')
192
            if isinstance(update_items, dict):
193
                for _key, _value in update_items.items():
194
                    self.data[_key].append(_value)
195
            else:
196
                for item in update_items:
197
                    if len(item) != 2:
198
                        raise KeyValuePairsError(item)
199
                    _key, _value = item
200
                    self.data[_key].append(_value)
201
        if kwargs != dict():
202
            self.update(kwargs)
203
    
204
    def reverse(self, key):
205
        '''
206
            
207
        '''
208
        self.data[key].reverse()
209
    
210
    def copy(self):
211
        '''
212
            
213
        '''
214
        return multivalued_dict(self.data)
215
    
216
    def items(self):
217
        '''
218
            
219
        '''
220
        return self.data.items()
221
    
222
    def keys(self):
223
        '''
224
            
225
        '''
226
        return self.data.keys()
227
    
228
    def values(self):
229
        '''
230
            
231
        '''
232
        return self.data.values()
233
    
234
    @classmethod
235
    def fromkeys(cls, iterable, value = None):
236
        '''
237
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'])
238
            multivalued_dict({'a': [None], 'b': [None], 'c': [None]})
239
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'], 'test')
240
            multivalued_dict({'a': ['test'], 'b': ['test'], 'c': ['test']})
241
        '''
242
        dict_var = dict.fromkeys(iterable, value)
243
        return cls(dict_var)
244