Add node removal/disassociate functions

When a node desires to be removed from its parent (or
a parent wants to remove a child) it is quite useful
to provide functions that do just this so that nodes
can remove themselves or there children when this kind
of usage is desired.

Change-Id: I0071e9a7e15219e0cb7b92779e4f5a08596e5d34
This commit is contained in:
Joshua Harlow
2015-03-04 11:49:10 -08:00
parent d3353a4cf3
commit db22de960c
2 changed files with 76 additions and 0 deletions

View File

@@ -113,6 +113,44 @@ class TreeTest(test.TestCase):
root = tree.Node("josh")
self.assertTrue(root.empty())
def test_removal(self):
root = self._make_species()
self.assertIsNotNone(root.remove('reptile'))
self.assertRaises(ValueError, root.remove, 'reptile')
self.assertIsNone(root.find('reptile'))
def test_removal_direct(self):
root = self._make_species()
self.assertRaises(ValueError, root.remove, 'human',
only_direct=True)
def test_removal_self(self):
root = self._make_species()
n = root.find('horse')
self.assertIsNotNone(n.parent)
n.remove('horse', include_self=True)
self.assertIsNone(n.parent)
self.assertIsNone(root.find('horse'))
def test_disassociate(self):
root = self._make_species()
n = root.find('horse')
self.assertIsNotNone(n.parent)
c = n.disassociate()
self.assertEqual(1, c)
self.assertIsNone(n.parent)
self.assertIsNone(root.find('horse'))
def test_disassociate_many(self):
root = self._make_species()
n = root.find('horse')
n.parent.add(n)
n.parent.add(n)
c = n.disassociate()
self.assertEqual(3, c)
self.assertIsNone(n.parent)
self.assertIsNone(root.find('horse'))
def test_not_empty(self):
root = self._make_species()
self.assertFalse(root.empty())

View File

@@ -149,6 +149,44 @@ class Node(object):
return n
return None
def disassociate(self):
"""Removes this node from its parent (if any).
:returns: occurences of this node that were removed from its parent.
"""
occurrences = 0
if self.parent is not None:
p = self.parent
self.parent = None
# Remove all instances of this node from its parent.
while True:
try:
p._children.remove(self)
except ValueError:
break
else:
occurrences += 1
return occurrences
def remove(self, item, only_direct=False, include_self=True):
"""Removes a item from this nodes children.
This will search not only this node but also any children nodes and
finally if nothing is found then a value error is raised instead of
the normally returned *removed* node object.
:param item: item to lookup.
:param only_direct: only look at current node and its direct children.
:param include_self: include the current node during searching.
"""
node = self.find(item, only_direct=only_direct,
include_self=include_self)
if node is None:
raise ValueError("Item '%s' not found to remove" % item)
else:
node.disassociate()
return node
def __contains__(self, item):
"""Returns whether item exists in this node or this nodes children.