blob: b24c162ac2ff0e4cc6333b9ca5e53e1df45f1210 [file] [log] [blame]
Brad Bishop8ffe1e42016-02-11 16:15:40 -05001# Contributors Listed Below - COPYRIGHT 2016
2# [+] International Business Machines Corp.
3#
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied. See the License for the specific language governing
15# permissions and limitations under the License.
16
17
18class PathTreeItemIterator(object):
19 def __init__(self, path_tree, subtree, depth):
20 self.path_tree = path_tree
21 self.path = []
22 self.itlist = []
CamVan Nguyendc63ed42018-03-07 18:25:21 -060023 self.subtree = ['/'] + list(filter(bool, subtree.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -050024 self.depth = depth
25 d = path_tree.root
26 for k in self.subtree:
27 try:
28 d = d[k]['children']
29 except KeyError:
30 raise KeyError(subtree)
CamVan Nguyendc63ed42018-03-07 18:25:21 -060031 # TODO: openbmc/openbmc#2994 remove python 2 support
32 try: # python 2
33 self.it = d.iteritems()
34 except AttributeError: # python 3
35 self.it = iter(d.items())
Brad Bishop8ffe1e42016-02-11 16:15:40 -050036
37 def __iter__(self):
38 return self
39
CamVan Nguyendc63ed42018-03-07 18:25:21 -060040 # TODO: openbmc/openbmc#2994 remove python 2 support
41 # python 2
Brad Bishopd641c082018-01-31 15:55:58 -050042 def next(self):
Brad Bishop8ffe1e42016-02-11 16:15:40 -050043 key, value = self._next()
44 path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
45 return path, value.get('data')
46
CamVan Nguyendc63ed42018-03-07 18:25:21 -060047 # python 3
48 import sys
49 if sys.version_info[0] > 2:
50 __next__ = next
51
Brad Bishop8ffe1e42016-02-11 16:15:40 -050052 def _next(self):
53 try:
54 while True:
CamVan Nguyendc63ed42018-03-07 18:25:21 -060055 x = next(self.it)
Andrew Jeffery786e6a62018-05-04 23:03:50 +093056 if self.depth:
57 if len(self.path) + 1 > self.depth:
58 continue
Brad Bishop8ffe1e42016-02-11 16:15:40 -050059 self.itlist.append(self.it)
60 self.path.append(x[0])
CamVan Nguyendc63ed42018-03-07 18:25:21 -060061 # TODO: openbmc/openbmc#2994 remove python 2 support
62 try: # python 2
63 self.it = x[1]['children'].iteritems()
64 except AttributeError: # python 3
65 self.it = iter(x[1]['children'].items())
Brad Bishop8ffe1e42016-02-11 16:15:40 -050066 break
67
68 except StopIteration:
69 if not self.itlist:
70 raise StopIteration
71
72 self.it = self.itlist.pop()
73 self.path.pop()
74 x = self._next()
75
76 return x
77
78
79class PathTreeKeyIterator(PathTreeItemIterator):
80 def __init__(self, path_tree, subtree, depth):
81 super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
82
CamVan Nguyendc63ed42018-03-07 18:25:21 -060083 # TODO: openbmc/openbmc#2994 remove python 2 support
84 # python 2
Brad Bishopd641c082018-01-31 15:55:58 -050085 def next(self):
Brad Bishop8ffe1e42016-02-11 16:15:40 -050086 return super(PathTreeKeyIterator, self).next()[0]
87
CamVan Nguyendc63ed42018-03-07 18:25:21 -060088 # python 3
89 import sys
90 if sys.version_info[0] > 2:
91 __next__ = next
92
Brad Bishop8ffe1e42016-02-11 16:15:40 -050093
94class PathTree:
95 def __init__(self):
96 self.root = {}
Andrew Jeffery52aeb312018-05-08 11:36:19 +093097 self.cache = {}
Brad Bishop8ffe1e42016-02-11 16:15:40 -050098
99 def _try_delete_parent(self, elements):
100 if len(elements) == 1:
101 return False
102
103 kids = 'children'
104 elements.pop()
105 d = self.root
106 for k in elements[:-1]:
107 d = d[k][kids]
108
109 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
110 del d[elements[-1]]
111 self._try_delete_parent(elements)
112
113 def _get_node(self, key):
114 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600115 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500116 d = self.root
117 for k in elements[:-1]:
118 try:
119 d = d[k][kids]
120 except KeyError:
121 raise KeyError(key)
122
123 return d[elements[-1]]
124
125 def __iter__(self):
Andrew Jeffery786e6a62018-05-04 23:03:50 +0930126 return PathTreeItemIterator(self, '/', None)
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500127
128 def __missing__(self, key):
Brad Bishopd641c082018-01-31 15:55:58 -0500129 for x in self.iterkeys():
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500130 if key == x:
131 return False
132 return True
133
134 def __delitem__(self, key):
Andrew Jeffery52aeb312018-05-08 11:36:19 +0930135 del self.cache[key]
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500136 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600137 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500138 d = self.root
139 for k in elements[:-1]:
140 try:
141 d = d[k][kids]
142 except KeyError:
143 raise KeyError(key)
144
145 del d[elements[-1]]
146 self._try_delete_parent(elements)
147
148 def __setitem__(self, key, value):
Andrew Jeffery52aeb312018-05-08 11:36:19 +0930149 self.cache[key] = value
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500150 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600151 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500152 d = self.root
153 for k in elements[:-1]:
154 d = d.setdefault(k, {kids: {}})[kids]
155
156 children = d.setdefault(elements[-1], {kids: {}})[kids]
157 d[elements[-1]].update({kids: children, 'data': value})
158
159 def __getitem__(self, key):
Andrew Jefferyce2a6f02018-05-16 13:53:30 +0930160 if key in self.cache:
161 return self.cache[key]
162
163 return self._get_node(key).get('data')
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500164
165 def setdefault(self, key, default):
166 if not self.get(key):
167 self.__setitem__(key, default)
168
169 return self.__getitem__(key)
170
171 def get(self, key, default=None):
172 try:
173 x = self.__getitem__(key)
174 except KeyError:
175 x = default
176
177 return x
178
179 def get_children(self, key):
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600180 return [x for x in self._get_node(key)['children'].keys()]
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500181
182 def demote(self, key):
183 n = self._get_node(key)
184 if 'data' in n:
185 del n['data']
186
187 def keys(self, subtree='/', depth=None):
188 return [x for x in self.iterkeys(subtree, depth)]
189
190 def values(self, subtree='/', depth=None):
191 return [x[1] for x in self.iteritems(subtree, depth)]
192
193 def items(self, subtree='/', depth=None):
194 return [x for x in self.iteritems(subtree, depth)]
195
196 def dataitems(self, subtree='/', depth=None):
Andrew Jefferyc31ae3b2018-05-09 12:52:23 +0930197 # dataitems() must return an iterable object containing all of the
198 # items explicitly inserted into the tree, rooted at subtree with
199 # depth number of path elements from the subtree root.
200 #
201 # Calling dataitems() to request all of the populated entries is
202 # unfortunately common, and it's (also) unfortunately expensive to
203 # generate (done by iterating over the entire tree). However, given we
204 # have a flat dict whose job is to cache the explicitly inserted values
205 # we can leverage that to provide a cheap-to-calculate answer to
206 # requests for the entire set of populated entries
207 if subtree == '/' and not depth:
208 return self.cache.items()
209
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500210 return [x for x in self.iteritems(subtree, depth)
211 if x[1] is not None]
212
213 def iterkeys(self, subtree='/', depth=None):
214 if not self.root:
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600215 # TODO: openbmc/openbmc#2994 remove python 2 support
216 try: # python 2
217 return {}.iterkeys()
218 except AttributeError: # python 3
219 return iter({}.keys())
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500220 return PathTreeKeyIterator(self, subtree, depth)
221
222 def iteritems(self, subtree='/', depth=None):
223 if not self.root:
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600224 # TODO: openbmc/openbmc#2994 remove python 2 support
225 try: # python 2
226 return {}.iteritems()
227 except AttributeError: # python 3
228 return iter({}.items())
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500229 return PathTreeItemIterator(self, subtree, depth)
Brad Bishop4e601a02016-04-01 14:29:46 -0400230
231 def dumpd(self, subtree='/'):
232 result = {}
233 d = result
234
235 for k, v in self.iteritems(subtree):
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600236 elements = ['/'] + list(filter(bool, k.split('/')))
Brad Bishop4e601a02016-04-01 14:29:46 -0400237 d = result
238 for k in elements:
239 d = d.setdefault(k, {})
240 if v is not None:
241 d.update(v)
242
243 return result