Passed
Push — master ( f9f6ee...eaec94 )
by Fabio
01:14
created

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

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
# -*- coding: utf-8 -*-
2
3
from benedict.utils import keypath_util
4
5
6
class KeypathDict(dict):
7
8
    def __init__(self, *args, **kwargs):
9
        self._separator = kwargs.pop('separator', None) \
10
            if 'separator' in kwargs else '.'
11
        super(KeypathDict, self).__init__(*args, **kwargs)
12
        self._check_keys(self)
13
14
    def _check_keys(self, d):
15
        keys = keypath_util.all_keys(d)
16
        keypath_util.check_keys(keys, self._separator)
17
18
    def _join_keys(self, keys):
19
        return keypath_util.join_keys(keys, self._separator)
20
21
    def _split_keys(self, key):
22
        return keypath_util.split_keys(key, self._separator)
23
24
    def _get_value_by_keys(self, keys):
25
        i = 0
26
        j = len(keys)
27
        val = self
28
        while i < j:
29
            key = keys[i]
30
            try:
31
                val = val[key]
32
            except KeyError:
33
                val = None
34
                break
35
            i += 1
36
        return val
37
38
    def _get_value_context_by_keys(self, keys):
39
        item_keys = keys[:-1]
40
        item_key = keys[-1]
41
        item_parent = self._get_value_by_keys(item_keys)
42
        return (item_parent, item_key, )
43
44
    def _has_value_by_keys(self, keys):
45
        item_parent, item_key = self._get_value_context_by_keys(keys)
46
        if isinstance(item_parent, dict):
47
            if item_key in item_parent:
48
                return True
49
            else:
50
                return False
51
        else:
52
            return False
53
54
    def _set_value_by_keys(self, keys, value):
55
        i = 0
56
        j = len(keys)
57
        item = self
58
        while i < j:
59
            key = keys[i]
60
            if i < (j - 1):
61
                if item == self:
62
                    subitem = super(KeypathDict, self).get(key, None)
63
                else:
64
                    subitem = item.get(key, None)
65
                if not isinstance(subitem, dict):
66
                    subitem = item[key] = {}
67
                item = subitem
68
            else:
69
                item[key] = value
70
            i += 1
71
72
    def __contains__(self, key):
73
        keys = self._split_keys(key)
74
        if len(keys) > 1:
75
            return self._has_value_by_keys(keys)
76
        else:
77
            return super(KeypathDict, self).__contains__(key)
78
79
    def __delitem__(self, key):
80
        keys = self._split_keys(key)
81
        if len(keys) > 1:
82
            item_parent, item_key = self._get_value_context_by_keys(keys)
83
            if isinstance(item_parent, dict):
84
                del item_parent[item_key]
85
            else:
86
                raise KeyError
87
        else:
88
            super(KeypathDict, self).__delitem__(key)
89
90
    def __getitem__(self, key):
91
        keys = self._split_keys(key)
92
        value = None
93
        if len(keys) > 1:
94
            item_parent, item_key = self._get_value_context_by_keys(keys)
95
            if isinstance(item_parent, dict):
96
                return item_parent[item_key]
97
            else:
98
                raise KeyError
99
        else:
100
            value = super(KeypathDict, self).__getitem__(key)
101
        return value
102
103
    def __setitem__(self, key, value):
104
        if isinstance(value, dict):
105
            self._check_keys(value)
106
        keys = self._split_keys(key)
107
        if len(keys) > 1:
108
            self._set_value_by_keys(keys, value)
109
        else:
110
            super(KeypathDict, self).__setitem__(key, value)
111
112
    @classmethod
113
    def fromkeys(cls, sequence, value=None):
114
        d = KeypathDict()
115
        for key in sequence:
116
            d[key] = value
117
        return d
118
119
    def get(self, key, default=None):
120
        keys = self._split_keys(key)
121
        if len(keys) > 1:
122
            item_parent, item_key = self._get_value_context_by_keys(keys)
123
            if isinstance(item_parent, dict):
124
                return item_parent.get(item_key, default)
125
            else:
126
                return default
127
        else:
128
            return super(KeypathDict, self).get(key, default)
129
130
    def keypaths(self):
131
        if self._separator:
132
            def walk_keypaths(root, path):
133
                keypaths = []
134
                for key, val in root.items():
135
                    keys = path + [key]
136
                    keypaths += [self._join_keys(keys)]
137
                    if isinstance(val, dict):
138
                        keypaths += walk_keypaths(val, keys)
139
                return keypaths
140
            keypaths = walk_keypaths(self, [])
141
            keypaths.sort()
142
            return keypaths
143
        else:
144
            return []
145
146
    def pop(self, key, default=None):
147
        keys = self._split_keys(key)
148
        if len(keys) > 1:
149
            item_parent, item_key = self._get_value_context_by_keys(keys)
150
            if isinstance(item_parent, dict):
151
                if default is None:
152
                    return item_parent.pop(item_key)
153
                else:
154
                    return item_parent.pop(item_key, default)
155
            else:
156
                if default is None:
157
                    raise KeyError
158
                else:
159
                    return default
160
        else:
161
            if default is None:
162
                return super(KeypathDict, self).pop(key)
163
            else:
164
                return super(KeypathDict, self).pop(key, default)
165
166
    def set(self, key, value):
167
        self[key] = value
168
169
    def setdefault(self, key, default=None):
170
        if key not in self:
171
            self[key] = default
172
            return default
173
        else:
174
            return self[key]
175
176
    def update(self, other):
177
        d = dict(other)
178
        self._check_keys(d)
179
        return super(KeypathDict, self).update(d)
180