The library offers two helper classes, GraphNode and TreeNode. TreeNode can be considered to be a special case of a GraphNode. A TreeNode may have only one parent at a time. In addition it provides functionality to find the root of the tree (find_root) and to walk through each node of the tree based on the node used as a pivot.
Both GraphNode and TreeNode have been derived from Node that provides following functionality:
Assignment of children/parents resets previous content and creates needed links to nodes. Otherwise setting attributes works as expected.
Simple assignment
>>> node1, node2 = Node(), Node()
>>> node1.children = node2
>>>
>>> assert node1.children[0] == node2
>>> assert node2.parents[0] == node1
Tuple assignment
>>> node1, node2, node3 = Node(), Node(), Node()
>>> node1.children = (node2, node3)
>>>
>>> assert node1.children[0] == node2
>>> assert node2.parents[0] == node1
>>> assert node1.children[1] == node3
>>> assert node3.parents[0] == node1
Assign value to an attribute
>>> node = Node()
>>>
>>> node.value = 13
>>> assert node.value == 13
Empties container content.
>>> node1, node2 = Node(), Node()
>>>
>>> node1.children = node2
>>> node1.children.empty()
>>>
>>> assert len(node1.children) == 0
>>> assert len(node2.parents) == 0
Appends given items to container.
Regular case
>>> node1, node2 = Node(), Node()
>>>
>>> node1.children = node2
>>>
>>> assert node1.children[0] == node2
>>> assert node2.parents[0] == node1
Cycles are allowed by default
>>> node1.parents.append(node2)
>>>
>>> assert node2.children[0] == node1
>>> assert node1.parents[0] == node2
Append multiple times
>>> node1, node2 = Node(), Node()
>>> node1.children.append(node2)
>>> node1.children.append(node2)
>>>
>>> assert node1.children[0] == node2
>>> assert node2.parents[0] == node1
>>> assert len(node1.children) == 1
>>> assert len(node2.parents) == 1
Append multiple at once
>>> node1, node2, node3 = Node(), Node(), Node()
>>>
>>> node1.children = (node2, node3)
>>>
>>> assert len(node1.children) == 2
>>> assert node2 in node1.children
>>> assert node3 in node1.children
Removes given items from container.
Regular case
>>> node1, node2 = Node(), Node()
>>>
>>> node1.children = node2
>>> node1.children.remove(node2)
>>>
>>> assert len(node1.children) == 0
>>> assert len(node2.parents) == 0
Remove multiple times
>>> node1, node2 = Node(), Node()
>>>
>>> node1.parents = node2
>>> node1.parents.remove(node2)
>>> node1.parents.remove(node2)
>>> node1.parents.remove(node2)
>>>
>>> assert len(node1.parents) == 0
>>> assert len(node2.children) == 0
Remove multiple at once
>>> node1, node2, node3 = Node(), Node(), Node()
>>>
>>> node1.children = (node2, node3)
>>> node1.children.remove(node2, node3)
>>>
>>> assert len(node1.children) == 0
Finds nodes matching to given rules. The idea is that the method seeks based on the type of the container. For example in case “node.parents.find” is invoked, it goes through all parents beginning from the parents of the given node.
Default case
>>> node1, node2, node3, node4 = Node(), Node(), Node(), Node()
>>>
>>> node1.children = (node2, node3)
>>> node3.parents.append(node4)
>>>
>>> node1.name = 'joe'
>>> node1.value = 13
>>> node2.color = 'blue'
>>> node3.color = 'black'
>>> node4.value = 13
Single argument, single result
>>> assert node2.parents.find(name='joe') == node1
>>> assert node1.children.find(color='blue') == node2
Single argument, multiple results
>>> assert node3.parents.find(value=13) == [node1, node4]
Multiple arguments, single result
>>> assert node2.parents.find(name='joe', value=13) == node1
Regex argument (match anything except newline)
>>> assert node2.parents.find(name='.') == node1
Regex argument (match from beginning)
>>> assert node1.children.find(color='^bl') == [node2, node3]
No result
>>> assert node2.parents.find(color='red') == None
Cyclic case
>>> node1, node2 = Node(), Node()
>>>
>>> node1.children = node2
>>> node2.children = node1
>>>
>>> node1.name = 'joe'
>>> node2.name = 'jack'
Single argument, single result
>>> assert node1.children.find(name='joe') == node1
>>> assert node1.children.find(name='jack') == node2
Currently GraphNode does not provide any extra functionality.
TreeNode provides following utility methods:
Finds the root node.
Regular case
>>> node1, node1a = TreeNode(), TreeNode()
>>> node1b, node1a1 = TreeNode(), TreeNode()
>>> node1.children.append(node1a, node1b)
>>> node1a.children.append(node1a1)
>>>
>>> assert node1.find_root() == node1
>>> assert node1a.find_root() == node1
>>> assert node1b.find_root() == node1
>>> assert node1a1.find_root() == node1
Walks through the nodes beginning from the current one in preorder.
>>> node1, node2, node3 = TreeNode(), TreeNode(), TreeNode()
>>> node4, node5 = TreeNode(), TreeNode()
>>>
>>> node1.children = (node2, node5)
>>> node2.children = (node3, node4)
>>> result = (node1, node3, node4, node2, node5 )
>>>
>>> for i, node in enumerate(node1.walk()):
... assert node == result[i], '%s %s %s' % (i, node, result[i])