xref: /openbmc/pyphosphor/obmc/utils/pathtree.py (revision d641c086b5cc5f873c69aab454d1d3297e178192)
18ffe1e44SBrad Bishop# Contributors Listed Below - COPYRIGHT 2016
28ffe1e44SBrad Bishop# [+] International Business Machines Corp.
38ffe1e44SBrad Bishop#
48ffe1e44SBrad Bishop#
58ffe1e44SBrad Bishop# Licensed under the Apache License, Version 2.0 (the "License");
68ffe1e44SBrad Bishop# you may not use this file except in compliance with the License.
78ffe1e44SBrad Bishop# You may obtain a copy of the License at
88ffe1e44SBrad Bishop#
98ffe1e44SBrad Bishop#     http://www.apache.org/licenses/LICENSE-2.0
108ffe1e44SBrad Bishop#
118ffe1e44SBrad Bishop# Unless required by applicable law or agreed to in writing, software
128ffe1e44SBrad Bishop# distributed under the License is distributed on an "AS IS" BASIS,
138ffe1e44SBrad Bishop# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148ffe1e44SBrad Bishop# implied. See the License for the specific language governing
158ffe1e44SBrad Bishop# permissions and limitations under the License.
168ffe1e44SBrad Bishop
178ffe1e44SBrad Bishop
188ffe1e44SBrad Bishopclass PathTreeItemIterator(object):
198ffe1e44SBrad Bishop    def __init__(self, path_tree, subtree, depth):
208ffe1e44SBrad Bishop        self.path_tree = path_tree
218ffe1e44SBrad Bishop        self.path = []
228ffe1e44SBrad Bishop        self.itlist = []
23*d641c086SBrad Bishop        self.subtree = ['/'] + filter(bool, subtree.split('/'))
248ffe1e44SBrad Bishop        self.depth = depth
258ffe1e44SBrad Bishop        d = path_tree.root
268ffe1e44SBrad Bishop        for k in self.subtree:
278ffe1e44SBrad Bishop            try:
288ffe1e44SBrad Bishop                d = d[k]['children']
298ffe1e44SBrad Bishop            except KeyError:
308ffe1e44SBrad Bishop                raise KeyError(subtree)
31*d641c086SBrad Bishop        self.it = d.iteritems()
328ffe1e44SBrad Bishop
338ffe1e44SBrad Bishop    def __iter__(self):
348ffe1e44SBrad Bishop        return self
358ffe1e44SBrad Bishop
368ffe1e44SBrad Bishop    def __next__(self):
37*d641c086SBrad Bishop        return super(PathTreeItemIterator, self).next()
388ffe1e44SBrad Bishop
39*d641c086SBrad Bishop    def next(self):
408ffe1e44SBrad Bishop        key, value = self._next()
418ffe1e44SBrad Bishop        path = self.subtree[0] + '/'.join(self.subtree[1:] + self.path)
428ffe1e44SBrad Bishop        return path, value.get('data')
438ffe1e44SBrad Bishop
448ffe1e44SBrad Bishop    def _next(self):
458ffe1e44SBrad Bishop        try:
468ffe1e44SBrad Bishop            while True:
47*d641c086SBrad Bishop                x = self.it.next()
488ffe1e44SBrad Bishop                depth_exceeded = len(self.path) + 1 > self.depth
498ffe1e44SBrad Bishop                if self.depth and depth_exceeded:
508ffe1e44SBrad Bishop                    continue
518ffe1e44SBrad Bishop                self.itlist.append(self.it)
528ffe1e44SBrad Bishop                self.path.append(x[0])
53*d641c086SBrad Bishop                self.it = x[1]['children'].iteritems()
548ffe1e44SBrad Bishop                break
558ffe1e44SBrad Bishop
568ffe1e44SBrad Bishop        except StopIteration:
578ffe1e44SBrad Bishop            if not self.itlist:
588ffe1e44SBrad Bishop                raise StopIteration
598ffe1e44SBrad Bishop
608ffe1e44SBrad Bishop            self.it = self.itlist.pop()
618ffe1e44SBrad Bishop            self.path.pop()
628ffe1e44SBrad Bishop            x = self._next()
638ffe1e44SBrad Bishop
648ffe1e44SBrad Bishop        return x
658ffe1e44SBrad Bishop
668ffe1e44SBrad Bishop
678ffe1e44SBrad Bishopclass PathTreeKeyIterator(PathTreeItemIterator):
688ffe1e44SBrad Bishop    def __init__(self, path_tree, subtree, depth):
698ffe1e44SBrad Bishop        super(PathTreeKeyIterator, self).__init__(path_tree, subtree, depth)
708ffe1e44SBrad Bishop
71*d641c086SBrad Bishop    def next(self):
728ffe1e44SBrad Bishop        return super(PathTreeKeyIterator, self).next()[0]
738ffe1e44SBrad Bishop
748ffe1e44SBrad Bishop
758ffe1e44SBrad Bishopclass PathTree:
768ffe1e44SBrad Bishop    def __init__(self):
778ffe1e44SBrad Bishop        self.root = {}
788ffe1e44SBrad Bishop
798ffe1e44SBrad Bishop    def _try_delete_parent(self, elements):
808ffe1e44SBrad Bishop        if len(elements) == 1:
818ffe1e44SBrad Bishop            return False
828ffe1e44SBrad Bishop
838ffe1e44SBrad Bishop        kids = 'children'
848ffe1e44SBrad Bishop        elements.pop()
858ffe1e44SBrad Bishop        d = self.root
868ffe1e44SBrad Bishop        for k in elements[:-1]:
878ffe1e44SBrad Bishop            d = d[k][kids]
888ffe1e44SBrad Bishop
898ffe1e44SBrad Bishop        if 'data' not in d[elements[-1]] and not d[elements[-1]][kids]:
908ffe1e44SBrad Bishop            del d[elements[-1]]
918ffe1e44SBrad Bishop            self._try_delete_parent(elements)
928ffe1e44SBrad Bishop
938ffe1e44SBrad Bishop    def _get_node(self, key):
948ffe1e44SBrad Bishop        kids = 'children'
95*d641c086SBrad Bishop        elements = ['/'] + filter(bool, key.split('/'))
968ffe1e44SBrad Bishop        d = self.root
978ffe1e44SBrad Bishop        for k in elements[:-1]:
988ffe1e44SBrad Bishop            try:
998ffe1e44SBrad Bishop                d = d[k][kids]
1008ffe1e44SBrad Bishop            except KeyError:
1018ffe1e44SBrad Bishop                raise KeyError(key)
1028ffe1e44SBrad Bishop
1038ffe1e44SBrad Bishop        return d[elements[-1]]
1048ffe1e44SBrad Bishop
1058ffe1e44SBrad Bishop    def __iter__(self):
1068ffe1e44SBrad Bishop        return self
1078ffe1e44SBrad Bishop
1088ffe1e44SBrad Bishop    def __missing__(self, key):
109*d641c086SBrad Bishop        for x in self.iterkeys():
1108ffe1e44SBrad Bishop            if key == x:
1118ffe1e44SBrad Bishop                return False
1128ffe1e44SBrad Bishop        return True
1138ffe1e44SBrad Bishop
1148ffe1e44SBrad Bishop    def __delitem__(self, key):
1158ffe1e44SBrad Bishop        kids = 'children'
116*d641c086SBrad Bishop        elements = ['/'] + filter(bool, key.split('/'))
1178ffe1e44SBrad Bishop        d = self.root
1188ffe1e44SBrad Bishop        for k in elements[:-1]:
1198ffe1e44SBrad Bishop            try:
1208ffe1e44SBrad Bishop                d = d[k][kids]
1218ffe1e44SBrad Bishop            except KeyError:
1228ffe1e44SBrad Bishop                raise KeyError(key)
1238ffe1e44SBrad Bishop
1248ffe1e44SBrad Bishop        del d[elements[-1]]
1258ffe1e44SBrad Bishop        self._try_delete_parent(elements)
1268ffe1e44SBrad Bishop
1278ffe1e44SBrad Bishop    def __setitem__(self, key, value):
1288ffe1e44SBrad Bishop        kids = 'children'
129*d641c086SBrad Bishop        elements = ['/'] + filter(bool, key.split('/'))
1308ffe1e44SBrad Bishop        d = self.root
1318ffe1e44SBrad Bishop        for k in elements[:-1]:
1328ffe1e44SBrad Bishop            d = d.setdefault(k, {kids: {}})[kids]
1338ffe1e44SBrad Bishop
1348ffe1e44SBrad Bishop        children = d.setdefault(elements[-1], {kids: {}})[kids]
1358ffe1e44SBrad Bishop        d[elements[-1]].update({kids: children, 'data': value})
1368ffe1e44SBrad Bishop
1378ffe1e44SBrad Bishop    def __getitem__(self, key):
1388ffe1e44SBrad Bishop        return self._get_node(key).get('data')
1398ffe1e44SBrad Bishop
1408ffe1e44SBrad Bishop    def setdefault(self, key, default):
1418ffe1e44SBrad Bishop        if not self.get(key):
1428ffe1e44SBrad Bishop            self.__setitem__(key, default)
1438ffe1e44SBrad Bishop
1448ffe1e44SBrad Bishop        return self.__getitem__(key)
1458ffe1e44SBrad Bishop
1468ffe1e44SBrad Bishop    def get(self, key, default=None):
1478ffe1e44SBrad Bishop        try:
1488ffe1e44SBrad Bishop            x = self.__getitem__(key)
1498ffe1e44SBrad Bishop        except KeyError:
1508ffe1e44SBrad Bishop            x = default
1518ffe1e44SBrad Bishop
1528ffe1e44SBrad Bishop        return x
1538ffe1e44SBrad Bishop
1548ffe1e44SBrad Bishop    def get_children(self, key):
155*d641c086SBrad Bishop        return [x for x in self._get_node(key)['children'].iterkeys()]
1568ffe1e44SBrad Bishop
1578ffe1e44SBrad Bishop    def demote(self, key):
1588ffe1e44SBrad Bishop        n = self._get_node(key)
1598ffe1e44SBrad Bishop        if 'data' in n:
1608ffe1e44SBrad Bishop            del n['data']
1618ffe1e44SBrad Bishop
1628ffe1e44SBrad Bishop    def keys(self, subtree='/', depth=None):
1638ffe1e44SBrad Bishop        return [x for x in self.iterkeys(subtree, depth)]
1648ffe1e44SBrad Bishop
1658ffe1e44SBrad Bishop    def values(self, subtree='/', depth=None):
1668ffe1e44SBrad Bishop        return [x[1] for x in self.iteritems(subtree, depth)]
1678ffe1e44SBrad Bishop
1688ffe1e44SBrad Bishop    def items(self, subtree='/', depth=None):
1698ffe1e44SBrad Bishop        return [x for x in self.iteritems(subtree, depth)]
1708ffe1e44SBrad Bishop
1718ffe1e44SBrad Bishop    def dataitems(self, subtree='/', depth=None):
1728ffe1e44SBrad Bishop        return [x for x in self.iteritems(subtree, depth)
1738ffe1e44SBrad Bishop                if x[1] is not None]
1748ffe1e44SBrad Bishop
1758ffe1e44SBrad Bishop    def iterkeys(self, subtree='/', depth=None):
1768ffe1e44SBrad Bishop        if not self.root:
177*d641c086SBrad Bishop            return {}.iterkeys()
1788ffe1e44SBrad Bishop        return PathTreeKeyIterator(self, subtree, depth)
1798ffe1e44SBrad Bishop
1808ffe1e44SBrad Bishop    def iteritems(self, subtree='/', depth=None):
1818ffe1e44SBrad Bishop        if not self.root:
182*d641c086SBrad Bishop            return {}.iteritems()
1838ffe1e44SBrad Bishop        return PathTreeItemIterator(self, subtree, depth)
1844e601a0aSBrad Bishop
1854e601a0aSBrad Bishop    def dumpd(self, subtree='/'):
1864e601a0aSBrad Bishop        result = {}
1874e601a0aSBrad Bishop        d = result
1884e601a0aSBrad Bishop
1894e601a0aSBrad Bishop        for k, v in self.iteritems(subtree):
190*d641c086SBrad Bishop            elements = ['/'] + filter(bool, k.split('/'))
1914e601a0aSBrad Bishop            d = result
1924e601a0aSBrad Bishop            for k in elements:
1934e601a0aSBrad Bishop                d = d.setdefault(k, {})
1944e601a0aSBrad Bishop            if v is not None:
1954e601a0aSBrad Bishop                d.update(v)
1964e601a0aSBrad Bishop
1974e601a0aSBrad Bishop        return result
198