Passed
Push — master ( 842787...588242 )
by sosei
01:06
created

multivalued_dict.KeyValuePairsError.__repr__()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
from collections import defaultdict
2
from collections import UserDict
3
from collections.abc import Iterable
4
import inspect
5
6
START_POS = 'S'
7
END_POS = 'E'
8
9
class multivalued_dict(UserDict):
10
    '''
11
        >>> mv_d = multivalued_dict()
12
        >>> mv_d
13
        multivalued_dict({})
14
        
15
        >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
16
        >>> mv_d
17
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
18
        
19
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['b', 'test-2'], ['a', 'test-3']])
20
        >>> mv_d
21
        multivalued_dict({'a': ['test-1', 'test-3'], 'b': ['test-2']})
22
        
23
        >>> mv_d = multivalued_dict(a = 'test-1', b = 'test-2', c = 'test-3')
24
        >>> mv_d
25
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
26
        
27
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['c', 'test-3']], b = 'test-2')
28
        >>> mv_d
29
        multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': ['test-2']})
30
        
31
        >>> mv_d0 = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
32
        >>> mv_d0
33
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
34
        >>> mv_d = multivalued_dict(mv_d0)
35
        >>> mv_d
36
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
37
    '''
38
    
39
    __marker = object()
40
    
41
    @staticmethod
42
    def __is_self(v_self):
43
        if not multivalued_dict.__is_multivalued_dict__(v_self):
44
            raise TypeError(f"descriptor '{inspect.currentframe().f_back.f_code.co_name}' requires a 'multivalued_dict' object but received a '{v_self.__class__.__name__}'")
45
    
46
    @classmethod
47
    def __is_multivalued_dict__(cls, x):
48
        '''
49
        >>> mv_d = multivalued_dict()
50
        >>> multivalued_dict.__is_multivalued_dict__(mv_d)
51
        True
52
        '''
53
        return (isinstance(x, cls) or ((True if x.default_factory == type([]) else False) if isinstance(x, defaultdict) else False))
54
    
55
    @classmethod
56
    def fromkeys(cls, iterable, value = None):
57
        '''
58
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'])
59
            multivalued_dict({'a': [None], 'b': [None], 'c': [None]})
60
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'], 'test')
61
            multivalued_dict({'a': ['test'], 'b': ['test'], 'c': ['test']})
62
        '''
63
        dict_var = dict.fromkeys(iterable, value)
64
        return cls(dict_var)
65
    
66
    def __init__(self, *args, **kwargs):
67
        '''
68
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
69
            >>> mv_d
70
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
71
            
72
            >>> mv_d.__init__({'d': 'test-4'})
73
            >>> mv_d
74
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4']})
75
            
76
            >>> multivalued_dict.__init__(mv_d, {'e': 'test-5'})
77
            >>> mv_d
78
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4'], 'e': ['test-5']})
79
        '''
80
        multivalued_dict.__is_self(self)
81
        len_of_args = len(args)
82
        if len_of_args > 1:
83
            raise TypeError(f'multivalued_dict expected at most 1 arguments, got {len_of_args}')
84
        else:
85
            if 'data' not in self.__dict__:
86
                self.data = defaultdict(list)
87
            if len_of_args == 1:
88
                initial_items = args[0]
89
                self.update(initial_items)
90
        if kwargs != dict():
91
            self.update(kwargs)
92
    
93
    def __repr__(self):
94
        multivalued_dict.__is_self(self)
95
        return f'multivalued_dict({dict(self.data)})'
96
    
97
    def __iter__(self):
98
        '''
99
            
100
        '''
101
        multivalued_dict.__is_self(self)
102
        return self.data.__iter__()
103
    
104
    def __len__(self):
105
        '''
106
            >>> mv_d = multivalued_dict([['a', 'test-1'], ['a', 'test-2'], ['a', 'test-3'], ['b', 'test-4']])
107
            >>> mv_d.__len__()
108
            2
109
        '''
110
        multivalued_dict.__is_self(self)
111
        return self.data.__len__()
112
    
113
    def __lenvalue__(self, key = None):
114
        '''
115
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
116
            >>> mv_d.__lenvalue__()
117
            6
118
            >>> mv_d.__lenvalue__('a')
119
            3
120
        '''
121
        multivalued_dict.__is_self(self)
122
        if key == None:
123
            return sum(map(len, self.data.values()))
124
        else:
125
            return len(self.data[key])
126
    
127
    def __getitem__(self, key):
128
        '''
129
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
130
            >>> mv_d['a']
131
            ['test-1']
132
        '''
133
        multivalued_dict.__is_self(self)
134
        if key in self.data:
135
            return self.data[key]
136
        else:
137
            raise KeyError(key)
138
    
139
    def __matchkv__(self, key, value):
140
        '''
141
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
142
            >>> mv_d.__matchkv__('b', 3)
143
            False
144
            >>> mv_d.__matchkv__('a', 2)
145
            True
146
            >>> mv_d.__matchkv__('d', 1)
147
            False
148
        '''
149
        multivalued_dict.__is_self(self)
150
        return value in self.data[key]
151
    
152
    def __eq__(self, other):
153
        '''
154
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
155
            >>> mv_d
156
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
157
            >>> mv_d == {'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']}
158
            True
159
            >>> mv_d == {'a': ['test-1'], 'b': ['test-2'], 'c': ['test-0']}
160
            False
161
        '''
162
        multivalued_dict.__is_self(self)
163
        return self.data.__eq__(other)
164
    
165
    def __contains__(self, key):
166
        '''
167
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
168
            >>> mv_d
169
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
170
            >>> 'a' in mv_d
171
            True
172
            >>> 'd' in mv_d
173
            False
174
        '''
175
        multivalued_dict.__is_self(self)
176
        return self.data.__contains__(key)
177
    
178
    def __delkv__(self, key, value, allkv = True, direction = START_POS):
179
        '''
180
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'z'], ['a', 'y'], ['a', 'z'], ['a', 'y']])
181
            >>> mv_d
182
            multivalued_dict({'a': ['x', 'y', 'z', 'y', 'z', 'y']})
183
            
184
            >>> mv_d.__delkv__('a', 'y', False)
185
            >>> mv_d
186
            multivalued_dict({'a': ['x', 'z', 'y', 'z', 'y']})
187
            
188
            >>> mv_d.__delkv__('a', 'y', False, END_POS)
189
            >>> mv_d
190
            multivalued_dict({'a': ['x', 'z', 'y', 'z']})
191
            
192
            >>> mv_d.__delkv__('a', 'z')
193
            >>> mv_d
194
            multivalued_dict({'a': ['x', 'y']})
195
        '''
196
        multivalued_dict.__is_self(self)
197
        assert allkv in (True, False), '"allkv" can only be True or False'
198
        assert direction in (START_POS, END_POS), '"direction" can only be START_POS or END_POS'
199
        
200
        if allkv:
201
            while value in self.data[key]:
202
                self.data[key].remove(value)
203
        else:
204
            if direction == START_POS:
205
                self.data[key].remove(value)
206
            elif direction == END_POS:
207
                value_len = len(self.data[key])
208
                for i in range(value_len):
209
                    if self.data[key][-1 - i] == value:
210
                        self.data[key].__delitem__(-1 - i)
211
                        break
212
    
213
    def __delitem__(self, key):
214
        '''
215
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
216
            >>> mv_d.__delitem__('b')
217
            >>> mv_d
218
            multivalued_dict({'a': ['test-1'], 'c': ['test-3']})
219
        '''
220
        multivalued_dict.__is_self(self)
221
        self.data.__delitem__(key)
222
        
223
    def __setitem__(self, key, item):
224
        '''
225
            >>> mv_d = multivalued_dict([['a', 'test-1'], ['a', 'test-2'], ['a', 'test-3'], ['b', 'test-4']])
226
            >>> mv_d
227
            multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': ['test-4']})
228
            
229
            >>> mv_d.__setitem__('c', 'test-5')
230
            >>> mv_d
231
            multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': ['test-4'], 'c': ['test-5']})
232
            
233
            >>> mv_d.__setitem__('a', 'test-0')
234
            >>> mv_d
235
            multivalued_dict({'a': ['test-0'], 'b': ['test-4'], 'c': ['test-5']})
236
        '''
237
        multivalued_dict.__is_self(self)
238
        self.data.__setitem__(key, [item])
239
    
240
    def get(self, key, default = None):
241
        '''
242
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
243
            >>> mv_d.get('a')
244
            ['test-1']
245
            >>> mv_d.get('d')
246
            [None]
247
        '''
248
        multivalued_dict.__is_self(self)
249
        return self.data.get(key, [default])
250
    
251
    def count(self, key, value):
252
        '''
253
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'y'], ['a', 'z'], ['a', 'z'], ['a', 'z']])
254
            >>> mv_d.count('a', 'y')
255
            2
256
        '''
257
        multivalued_dict.__is_self(self)
258
        return self.data[key].count(value)
259
    
260
    def update(self, *args, **kwargs):
261
        '''
262
            >>> mv_d = multivalued_dict()
263
            >>> mv_d
264
            multivalued_dict({})
265
            
266
            >>> mv_d.update({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
267
            >>> mv_d
268
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
269
            
270
            >>> mv_d.update([['a', 'test-4'], ['a', 'test-5']])
271
            >>> mv_d
272
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5'], 'b': ['test-2'], 'c': ['test-3']})
273
            
274
            >>> mv_d.update(c = 'test-3')
275
            >>> mv_d
276
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5'], 'b': ['test-2'], 'c': ['test-3', 'test-3']})
277
            
278
            >>> mv_d.update([['b', 'test-6'], ['c', 'test-7']], a = 'test-8')
279
            >>> mv_d
280
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5', 'test-8'], 'b': ['test-2', 'test-6'], 'c': ['test-3', 'test-3', 'test-7']})
281
            
282
            >>> mv_d.update(multivalued_dict({'d': 'test-9', 'e': 'test-10'}))
283
            >>> mv_d
284
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5', 'test-8'], 'b': ['test-2', 'test-6'], 'c': ['test-3', 'test-3', 'test-7'], 'd': ['test-9'], 'e': ['test-10']})
285
        '''
286
        multivalued_dict.__is_self(self)
287
        len_of_args = len(args)
288
        if len_of_args > 1:
289
            raise TypeError(f'multivalued_dict expected at most 1 arguments, got {len_of_args}')
290
        if len_of_args == 1:
291
            update_items = args[0]
292
            if not isinstance(update_items, Iterable):
293
                raise TypeError(f"'{update_items.__class__.__name__}' object is not iterable")
294
            if multivalued_dict.__is_multivalued_dict__(update_items):
295
                for _key, _value in update_items.items():
296
                    self.data[_key].extend(_value)
297
            elif isinstance(update_items, dict):
298
                for _key, _value in update_items.items():
299
                    self.data[_key].append(_value)
300
            else:
301
                i = 0
302
                for item in update_items:
303
                    if not isinstance(item, Iterable):
304
                        raise TypeError(f'cannot convert dictionary update sequence element #{i} to a sequence')
305
                    if len(item) != 2:
306
                        raise ValueError(f'dictionary update sequence element #{i} has length {len(item)}; 2 is required')
307
                    _key, _value = item
308
                    self.data[_key].append(_value)
309
                    i += 1
310
        if kwargs != dict():
311
            self.update(kwargs)
312
    
313
    def setdefault(self, key, default = None):
314
        '''
315
            >>> mv_d = multivalued_dict({'a': 'test-1', 'c': 'test-3'})
316
            >>> mv_d.setdefault('a')
317
            ['test-1']
318
            >>> mv_d.setdefault('b')
319
            [None]
320
            >>> mv_d
321
            multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': [None]})
322
            >>> mv_d.setdefault('d', 'test=4')
323
            ['test=4']
324
            >>> mv_d
325
            multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': [None], 'd': ['test=4']})
326
        '''
327
        multivalued_dict.__is_self(self)
328
        return self.data.setdefault(key, [default])
329
        
330
    def pop(self, key, default=__marker):
331
        '''
332
            
333
        '''
334
        multivalued_dict.__is_self(self)
335
        if default is self.__marker:
336
            return self.data.pop(key)
337
        else:
338
            return self.data.pop(key, [default])
339
    
340
    def popitem(self):
341
        '''
342
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
343
            >>> mv_d
344
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
345
            >>> mv_d.popitem()
346
            ('c', ['test-3'])
347
        '''
348
        multivalued_dict.__is_self(self)
349
        return self.data.popitem()
350
    
351
    def copy(self):
352
        '''
353
            >>> mv_d_a = multivalued_dict([['a', 1], ['a', 2], ['a', 3]])
354
            >>> mv_d_b = mv_d_a.copy()
355
            >>> mv_d_a
356
            multivalued_dict({'a': [1, 2, 3]})
357
            >>> mv_d_b
358
            multivalued_dict({'a': [1, 2, 3]})
359
            >>> mv_d_a['a'][1] = 99
360
            >>> mv_d_a
361
            multivalued_dict({'a': [1, 99, 3]})
362
            >>> mv_d_b
363
            multivalued_dict({'a': [1, 2, 3]})
364
        '''
365
        multivalued_dict.__is_self(self)
366
        return multivalued_dict(self.data)
367
    
368
    def items(self):
369
        '''
370
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
371
            >>> for k, v in mv_d.items():
372
            ...     print(f'key = {k}, value = {v}')
373
            ...
374
            key = a, value = ['test-1']
375
            key = b, value = ['test-2']
376
            key = c, value = ['test-3']
377
        '''
378
        multivalued_dict.__is_self(self)
379
        return self.data.items()
380
    
381
    def keys(self):
382
        '''
383
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
384
            >>> for k in mv_d.keys():
385
            ...     print(f'key = {k}')
386
            ...
387
            key = a
388
            key = b
389
            key = c
390
        '''
391
        multivalued_dict.__is_self(self)
392
        return self.data.keys()
393
    
394
    def values(self):
395
        '''
396
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
397
            >>> for v in mv_d.values():
398
            ...     print(f'value = {v}')
399
            ...
400
            value = ['test-1']
401
            value = ['test-2']
402
            value = ['test-3']
403
        '''
404
        multivalued_dict.__is_self(self)
405
        return self.data.values()
406
    
407
    def clear(self):
408
        '''
409
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
410
            >>> mv_d
411
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
412
            >>> mv_d.clear()
413
            >>> mv_d
414
            multivalued_dict({})
415
        '''
416
        multivalued_dict.__is_self(self)
417
        self.data.clear()
418
    
419
    def reverse(self, key):
420
        '''
421
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3]])
422
            >>> mv_d
423
            multivalued_dict({'a': [1, 2, 3]})
424
            >>> mv_d.reverse('a')
425
            >>> mv_d
426
            multivalued_dict({'a': [3, 2, 1]})
427
        '''
428
        multivalued_dict.__is_self(self)
429
        self.data[key].reverse()
430