Completed
Branch master (ea8775)
by Fabio
02:39 queued 01:33
created

benedict.dicts.keypath.KeypathDict.__pop_by_keys()   A

Complexity

Conditions 3

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nop 3
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
3
from benedict.utils import dict_util, keypath_util
4
5
6
class KeypathDict(dict):
7
8
    _keypath_separator = None
9
10
    def __init__(self, *args, **kwargs):
11
        """
12
        Constructs a new instance.
13
        """
14
        self._keypath_separator = kwargs.pop('keypath_separator', '.')
15
        super(KeypathDict, self).__init__(*args, **kwargs)
16
        keypath_util.check_keys(self, self._keypath_separator)
17
18
    @property
19
    def keypath_separator(self):
20
        return self._keypath_separator
21
22
    @keypath_separator.setter
23
    def keypath_separator(self, value):
24
        keypath_util.check_keys(self, value)
25
        self._keypath_separator = value
26
27
    def __contains__(self, key):
28
        keys = keypath_util.list_keys(key, self._keypath_separator)
29
        if len(keys) > 1:
30
            return self.__contains_by_keys(keys)
31
        key = keys[0]
32
        return super(KeypathDict, self).__contains__(key)
33
34
    def __contains_by_keys(self, keys):
35
        parent, key, _ = dict_util.resolve(self, keys)[-1]
36
        if isinstance(parent, dict) and key in parent:
37
            return True
38
        return False
39
40
    def __delitem__(self, key):
41
        keys = keypath_util.list_keys(key, self._keypath_separator)
42
        if len(keys) > 1:
43
            self.__delitem_by_keys(keys)
44
            return
45
        key = keys[0]
46
        super(KeypathDict, self).__delitem__(key)
47
48
    def __delitem_by_keys(self, keys):
49
        parent, key, _ = dict_util.resolve(self, keys)[-1]
50
        if isinstance(parent, dict):
51
            del parent[key]
52
            return
53
        raise KeyError
54
55
    def __getitem__(self, key):
56
        keys = keypath_util.list_keys(key, self._keypath_separator)
57
        value = None
58
        if len(keys) > 1:
59
            return self.__getitem_by_keys(keys)
60
        key = keys[0]
61
        return super(KeypathDict, self).__getitem__(key)
62
63
    def __getitem_by_keys(self, keys):
64
        parent, key, _ = dict_util.resolve(self, keys)[-1]
65
        if isinstance(parent, dict):
66
            return parent[key]
67
        raise KeyError
68
69
    def __setitem__(self, key, value):
70
        keypath_util.check_keys(value, self._keypath_separator)
71
        keys = keypath_util.list_keys(key, self._keypath_separator)
72
        if len(keys) > 1:
73
            self.__setitem_by_keys(keys, value)
74
            return
75
        key = keys[0]
76
        super(KeypathDict, self).__setitem__(key, value)
77
78
    def __setitem_by_keys(self, keys, value):
79
        i = 0
80
        j = len(keys)
81
        item = self
82
        while i < j:
83
            key = keys[i]
84
            if i < (j - 1):
85
                subitem = item.get(key, None)
86
                if not isinstance(subitem, dict):
87
                    subitem = item[key] = {}
88
                item = subitem
89
                i += 1
90
                continue
91
            item[key] = value
92
            break
93
94
    @classmethod
95
    def fromkeys(cls, sequence, value=None):
96
        d = cls()
97
        for key in sequence:
98
            d[key] = value
99
        return d
100
101
    def get(self, key, default=None):
102
        keys = keypath_util.list_keys(key, self._keypath_separator)
103
        if len(keys) > 1:
104
            return self.__get_by_keys(keys, default)
105
        key = keys[0]
106
        return super(KeypathDict, self).get(key, default)
107
108
    def __get_by_keys(self, keys, default=None):
109
        parent, key, _ = dict_util.resolve(self, keys)[-1]
110
        if isinstance(parent, dict):
111
            return parent.get(key, default)
112
        return default
113
114
    def pop(self, key, *args):
115
        keys = keypath_util.list_keys(key, self._keypath_separator)
116
        if len(keys) > 1:
117
            return self.__pop_by_keys(keys, *args)
118
        return super(KeypathDict, self).pop(key, *args)
119
120
    def __pop_by_keys(self, keys, *args):
121
        parent, key, _ = dict_util.resolve(self, keys)[-1]
122
        if isinstance(parent, dict):
123
            return parent.pop(key, *args)
124
        if args:
125
            # default
126
            return args[0]
127
        raise KeyError
128
129
    def set(self, key, value):
130
        self.__setitem__(key, value)
131
132
    def setdefault(self, key, default=None):
133
        if key not in self:
134
            self.__setitem__(key, default)
135
            return default
136
        return self.__getitem__(key)
137
138
    def update(self, other):
139
        keypath_util.check_keys(other, self._keypath_separator)
140
        super(KeypathDict, self).update(other)
141