blob: 3ca9a7e030eb8ba4fe3e0901d2d837961936c585 [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)
Brad Bishop8ffe1e42016-02-11 16:15:40 -050056 depth_exceeded = len(self.path) + 1 > self.depth
57 if self.depth and depth_exceeded:
58 continue
59 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 = {}
97
98 def _try_delete_parent(self, elements):
99 if len(elements) == 1:
100 return False
101
102 kids = 'children'
103 elements.pop()
104 d = self.root
105 for k in elements[:-1]:
106 d = d[k][kids]
107
108 if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
109 del d[elements[-1]]
110 self._try_delete_parent(elements)
111
112 def _get_node(self, key):
113 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600114 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500115 d = self.root
116 for k in elements[:-1]:
117 try:
118 d = d[k][kids]
119 except KeyError:
120 raise KeyError(key)
121
122 return d[elements[-1]]
123
124 def __iter__(self):
125 return self
126
127 def __missing__(self, key):
Brad Bishopd641c082018-01-31 15:55:58 -0500128 for x in self.iterkeys():
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500129 if key == x:
130 return False
131 return True
132
133 def __delitem__(self, key):
134 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600135 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500136 d = self.root
137 for k in elements[:-1]:
138 try:
139 d = d[k][kids]
140 except KeyError:
141 raise KeyError(key)
142
143 del d[elements[-1]]
144 self._try_delete_parent(elements)
145
146 def __setitem__(self, key, value):
147 kids = 'children'
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600148 elements = ['/'] + list(filter(bool, key.split('/')))
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500149 d = self.root
150 for k in elements[:-1]:
151 d = d.setdefault(k, {kids: {}})[kids]
152
153 children = d.setdefault(elements[-1], {kids: {}})[kids]
154 d[elements[-1]].update({kids: children, 'data': value})
155
156 def __getitem__(self, key):
157 return self._get_node(key).get('data')
158
159 def setdefault(self, key, default):
160 if not self.get(key):
161 self.__setitem__(key, default)
162
163 return self.__getitem__(key)
164
165 def get(self, key, default=None):
166 try:
167 x = self.__getitem__(key)
168 except KeyError:
169 x = default
170
171 return x
172
173 def get_children(self, key):
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600174 return [x for x in self._get_node(key)['children'].keys()]
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500175
176 def demote(self, key):
177 n = self._get_node(key)
178 if 'data' in n:
179 del n['data']
180
181 def keys(self, subtree='/', depth=None):
182 return [x for x in self.iterkeys(subtree, depth)]
183
184 def values(self, subtree='/', depth=None):
185 return [x[1] for x in self.iteritems(subtree, depth)]
186
187 def items(self, subtree='/', depth=None):
188 return [x for x in self.iteritems(subtree, depth)]
189
190 def dataitems(self, subtree='/', depth=None):
191 return [x for x in self.iteritems(subtree, depth)
192 if x[1] is not None]
193
194 def iterkeys(self, subtree='/', depth=None):
195 if not self.root:
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600196 # TODO: openbmc/openbmc#2994 remove python 2 support
197 try: # python 2
198 return {}.iterkeys()
199 except AttributeError: # python 3
200 return iter({}.keys())
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500201 return PathTreeKeyIterator(self, subtree, depth)
202
203 def iteritems(self, subtree='/', depth=None):
204 if not self.root:
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600205 # TODO: openbmc/openbmc#2994 remove python 2 support
206 try: # python 2
207 return {}.iteritems()
208 except AttributeError: # python 3
209 return iter({}.items())
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500210 return PathTreeItemIterator(self, subtree, depth)
Brad Bishop4e601a02016-04-01 14:29:46 -0400211
212 def dumpd(self, subtree='/'):
213 result = {}
214 d = result
215
216 for k, v in self.iteritems(subtree):
CamVan Nguyendc63ed42018-03-07 18:25:21 -0600217 elements = ['/'] + list(filter(bool, k.split('/')))
Brad Bishop4e601a02016-04-01 14:29:46 -0400218 d = result
219 for k in elements:
220 d = d.setdefault(k, {})
221 if v is not None:
222 d.update(v)
223
224 return result