def ListDescendentPathInfos(self, client_id, path_type, components, max_depth=None): """Lists path info records that correspond to children of given path.""" result = [] for path_idx, path_record in iteritems(self.path_records): other_client_id, other_path_type, other_components = path_idx if client_id != other_client_id or path_type != other_path_type: continue if len(other_components) == len(components): continue if not collection.StartsWith(other_components, components): continue if (max_depth is not None and len(other_components) - len(components) > max_depth): continue result.append(path_record.GetPathInfo()) result.sort(key=lambda _: tuple(_.components)) return result
def testNonListIterable(self): self.assertTrue(collection.StartsWith((5, 4, 3), (5, 4)))
def testString(self): self.assertTrue(collection.StartsWith("foobar", "foo"))
def testStringList(self): self.assertTrue(collection.StartsWith(["a", "b", "c"], ["a", "b"]))
def testDifferentElement(self): self.assertFalse(collection.StartsWith([1, 2, 3], [1, 4, 5]))
def testProperPrefix(self): self.assertTrue(collection.StartsWith([1, 2, 3], [1, 2])) self.assertTrue(collection.StartsWith([1, 2, 3], [1]))
def testEqual(self): self.assertTrue(collection.StartsWith([1, 2, 3], [1, 2, 3]))
def testEmptyDoesNotStartWithNonEmpty(self): self.assertFalse(collection.StartsWith([], [1, 2, 3]))
def testNonEmptyStartsWithEmpty(self): self.assertTrue(collection.StartsWith([1, 2, 3], []))
def testEmptyStartsWithEmpty(self): self.assertTrue(collection.StartsWith([], []))
def ListDescendentPathInfos(self, client_id, path_type, components, timestamp=None, max_depth=None): """Lists path info records that correspond to children of given path.""" result = [] for path_idx, path_record in iteritems(self.path_records): other_client_id, other_path_type, other_components = path_idx if client_id != other_client_id or path_type != other_path_type: continue if len(other_components) == len(components): continue if not collection.StartsWith(other_components, components): continue if (max_depth is not None and len(other_components) - len(components) > max_depth): continue result.append(path_record.GetPathInfo(timestamp=timestamp)) if timestamp is None: return sorted(result, key=lambda _: tuple(_.components)) # We need to filter implicit path infos if specific timestamp is given. # TODO(hanuszczak): If we were to switch to use path trie instead of storing # records by path id, everything would be much easier. class TrieNode(object): """A trie of path components with path infos as values.""" def __init__(self): self.path_info = None self.children = {} self.explicit = False def Add(self, path_info, idx=0): """Adds given path info to the trie (or one of its subtrees).""" components = path_info.components if idx == len(components): self.path_info = path_info self.explicit |= (path_info.HasField("stat_entry") or path_info.HasField("hash_entry")) else: child = self.children.setdefault(components[idx], TrieNode()) child.Add(path_info, idx=idx + 1) self.explicit |= child.explicit def Collect(self, path_infos): if self.path_info is not None and self.explicit: path_infos.append(self.path_info) for component in sorted(iterkeys(self.children)): self.children[component].Collect(path_infos) trie = TrieNode() for path_info in result: trie.Add(path_info) explicit_path_infos = [] trie.Collect(explicit_path_infos) return explicit_path_infos