Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nodes tree-wise bpointer/fpointers #126

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*.py[cod]
.idea

env/*
# C extensions
*.so

Expand Down
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
Expand Down
3 changes: 3 additions & 0 deletions HISTORY
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Oct 13 2019 V1.5.6
Add ability to independently mutate multiple shallow copies of same initial tree.

Mar 27, 2014 V1.2.6
Node identifier supports multiple data types beyond int and str.
Finish depth() and leaves() routines.
Expand Down
31 changes: 30 additions & 1 deletion tests/test_node.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest

from collections import defaultdict
from treelib import Node


Expand All @@ -11,9 +12,13 @@ def setUp(self):
def test_initialization(self):
self.assertEqual(self.node1.tag, "Test One")
self.assertEqual(self.node1.identifier, "identifier 1")
self.assertEqual(self.node1.expanded, True)
# retro-compatibility
self.assertEqual(self.node1.bpointer, None)
self.assertEqual(self.node1.fpointer, [])

self.assertEqual(self.node1.expanded, True)
self.assertEqual(self.node1._bpointer, {})
self.assertEqual(self.node1._fpointer, defaultdict(list))
self.assertEqual(self.node1.data, None)

def test_set_tag(self):
Expand All @@ -32,21 +37,45 @@ def test_set_identifier(self):
self.node1.identifier = "identifier 1"

def test_set_fpointer(self):
# retro-compatibility
self.node1.update_fpointer("identifier 2")
self.assertEqual(self.node1.fpointer, ['identifier 2'])
self.node1.fpointer = []
self.assertEqual(self.node1.fpointer, [])

def test_set_fpointer_in_tree(self):
self.node1.update_fpointer_in_tree("tree 1", "identifier 2")
self.assertEqual(self.node1.fpointer_in_tree("tree 1"), ['identifier 2'])
self.assertEqual(self.node1._fpointer["tree 1"], ['identifier 2'])
self.node1.set_fpointer_in_tree("tree 1", [])
self.assertEqual(self.node1._fpointer["tree 1"], [])

def test_set_bpointer(self):
# retro-compatibility
self.node2.update_bpointer("identifier 1")
self.assertEqual(self.node2.bpointer, 'identifier 1')
self.node2.bpointer = None
self.assertEqual(self.node2.bpointer, None)

def test_set_bpointer_in_tree(self):
self.node2.update_bpointer_in_tree("tree 1", "identifier 1")
self.assertEqual(self.node2.bpointer_in_tree("tree 1"), 'identifier 1')
self.assertEqual(self.node2._bpointer["tree 1"], 'identifier 1')
self.node2.update_bpointer_in_tree("tree 1", None)
self.assertEqual(self.node2.bpointer_in_tree("tree 1"), None)

def test_set_is_leaf(self):
self.node1.update_fpointer("identifier 2")
self.node2.update_bpointer("identifier 1")
self.assertEqual(self.node1.is_leaf(), False)
self.assertEqual(self.node2.is_leaf(), True)

def test_set_is_leaf_in_tree(self):
self.node1.update_fpointer_in_tree("tree 1", "identifier 2")
self.node2.update_bpointer_in_tree("tree 1", "identifier 1")
self.assertEqual(self.node1.is_leaf_in_tree("tree 1"), False)
self.assertEqual(self.node2.is_leaf_in_tree("tree 1"), True)

def test_data(self):

class Flower(object):
Expand Down
97 changes: 83 additions & 14 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ def encode(value):

class TreeCase(unittest.TestCase):
def setUp(self):
tree = Tree()
tree.create_node("Hárry", "hárry")
tree.create_node("Jane", "jane", parent="hárry")
tree.create_node("Bill", "bill", parent="hárry")
tree = Tree(identifier="tree 1")
tree.create_node(u"Hárry", u"hárry")
tree.create_node("Jane", "jane", parent=u"hárry")
tree.create_node("Bill", "bill", parent=u"hárry")
tree.create_node("Diane", "diane", parent="jane")
tree.create_node("George", "george", parent="bill")
# Hárry
Expand All @@ -38,16 +38,26 @@ def setUp(self):
# |-- Bill
# |-- George
self.tree = tree
self.copytree = Tree(self.tree, True)
self.copytree = Tree(self.tree, deep=True)

def test_tree(self):
self.assertEqual(isinstance(self.tree, Tree), True)
self.assertEqual(isinstance(self.copytree, Tree), True)

def test_is_root(self):
# retro-compatibility
self.assertTrue(self.tree._nodes['hárry'].is_root())
self.assertFalse(self.tree._nodes['jane'].is_root())

def test_is_root_in_tree(self):
subtree = self.tree.subtree("jane", identifier="subtree 2")
# harry is root of tree 1 but not present in subtree 2
self.assertTrue(self.tree._nodes['hárry'].is_root_in_tree("tree 1"))
self.assertNotIn('hárry', subtree._nodes)
# jane is not root of tree 1 but is root of subtree 2
self.assertFalse(self.tree._nodes['jane'].is_root_in_tree("tree 1"))
self.assertTrue(subtree._nodes['jane'].is_root_in_tree("subtree 2"))

def test_paths_to_leaves(self):
paths = self.tree.paths_to_leaves()
self.assertEqual( len(paths), 2 )
Expand All @@ -62,7 +72,7 @@ def test_nodes(self):
self.assertEqual(self.tree.contains("jane"), True)
self.assertEqual("jane" in self.tree, True)
self.assertEqual(self.tree.contains("alien"), False)
self.tree.create_node("Alien","alien", parent="jane");
self.tree.create_node("Alien", "alien", parent="jane")
self.assertEqual(self.tree.contains("alien"), True)
self.tree.remove_node("alien")

Expand Down Expand Up @@ -111,7 +121,7 @@ def test_remove_node(self):
self.assertEqual(self.tree.get_node("jill") is None, True)
self.assertEqual(self.tree.get_node("mark") is None, True)

def test_depth(self):
def test_depth_in_tree(self):
# Try getting the level of this tree
self.assertEqual(self.tree.depth(), 2)
self.tree.create_node("Jill", "jill", parent = "george")
Expand Down Expand Up @@ -145,14 +155,25 @@ def test_depth(self):
self.tree.remove_node("jill")

def test_leaves(self):
# retro-compatibility
leaves = self.tree.leaves()
for nid in self.tree.expand_tree():
self.assertEqual((self.tree[nid].is_leaf()) == (self.tree[nid] \
in leaves), True)
self.assertEqual((self.tree[nid].is_leaf()) == (self.tree[nid] in leaves), True)
leaves = self.tree.leaves(nid='jane')
for nid in self.tree.expand_tree(nid='jane'):
self.assertEqual(self.tree[nid].is_leaf() == (self.tree[nid] in leaves), True)

def test_leaves_in_tree(self):
leaves = self.tree.leaves()
for nid in self.tree.expand_tree():
self.assertEqual(
(self.tree[nid].is_leaf_in_tree("tree 1")) == (self.tree[nid] in leaves),
True
)
leaves = self.tree.leaves(nid='jane')
for nid in self.tree.expand_tree(nid='jane'):
self.assertEqual(self.tree[nid].is_leaf_in_tree("tree 1") == (self.tree[nid] in leaves), True)

def test_link_past_node(self):
self.tree.create_node("Jill", "jill", parent="hárry")
self.tree.create_node("Mark", "mark", parent="jill")
Expand Down Expand Up @@ -212,7 +233,31 @@ def test_paste_tree(self):
new_tree.create_node("Mark", "mark", parent="jill")
self.tree.paste("jane", new_tree)
self.assertEqual("jill" in self.tree.is_branch("jane"), True)
self.tree.show()
self.assertEqual(
self.tree._reader,
u'''Hárry
├── Bill
│ └── George
└── Jane
├── Diane
└── Jill
└── Mark
'''
)
self.tree.remove_node("jill")
self.assertNotIn('jill', self.tree.nodes.keys())
self.assertNotIn('mark', self.tree.nodes.keys())
self.tree.show()
self.assertEqual(
self.tree._reader,
u'''Hárry
├── Bill
│ └── George
└── Jane
└── Diane
'''
)

def test_rsearch(self):
for nid in ["hárry", "jane", "diane"]:
Expand All @@ -229,7 +274,7 @@ def test_subtree(self):

def test_remove_subtree(self):
subtree_shallow = self.tree.remove_subtree("jane")
self.assertEqual("jane" not in self.tree.is_branch("hárry"), True)
self.assertEqual("jane" not in self.tree.is_branch(u"hárry"), True)
self.tree.paste("hárry", subtree_shallow)

def test_to_json(self):
Expand Down Expand Up @@ -326,7 +371,7 @@ def test_filter_nodes(self):
tests: Tree.filter_nodes
Added by: William Rusnack
"""
new_tree = Tree()
new_tree = Tree(identifier="tree 1")

self.assertEqual(tuple(new_tree.filter_nodes(lambda n: True)), ())

Expand All @@ -335,8 +380,8 @@ def test_filter_nodes(self):
nodes.append(new_tree.create_node('second', parent=new_tree.root))

self.assertEqual(tuple(new_tree.filter_nodes(lambda n: False)), ())
self.assertEqual(tuple(new_tree.filter_nodes(lambda n: n.is_root())), (nodes[0],))
self.assertEqual(tuple(new_tree.filter_nodes(lambda n: not n.is_root())), (nodes[1],))
self.assertEqual(tuple(new_tree.filter_nodes(lambda n: n.is_root_in_tree("tree 1"))), (nodes[0],))
self.assertEqual(tuple(new_tree.filter_nodes(lambda n: not n.is_root_in_tree("tree 1"))), (nodes[1],))
self.assertTrue(set(new_tree.filter_nodes(lambda n: True)), set(nodes))

def test_loop(self):
Expand Down Expand Up @@ -375,7 +420,7 @@ def test_modify_node_identifier_recursively(self):
self.assertTrue(tree.get_node("xyz").identifier == 'xyz')

def test_modify_node_identifier_root(self):
tree = Tree()
tree = Tree(identifier="tree 3")
tree.create_node("Harry", "harry")
tree.create_node("Jane", "jane", parent="harry")
tree.update_node(tree['harry'].identifier, identifier='xyz', tag='XYZ')
Expand All @@ -397,3 +442,27 @@ class SubTree(Tree):
tree = Tree(node_class=SubNode)
node = tree.create_node()
self.assertTrue(isinstance(node, SubNode))

def test_shallow_copy_hermetic_pointers(self):
# tree 1
# Hárry
# └── Jane
# └── Diane
# └── Bill
# └── George
tree2 = self.tree.subtree(nid='jane', identifier='tree 2')
# tree 2
# Jane
# └── Diane

# check that in shallow copy, instances are the same
self.assertIs(self.tree['jane'], tree2['jane'])
self.assertEqual(self.tree['jane']._bpointer, {'tree 1': u"hárry", 'tree 2': None})
self.assertEqual(dict(self.tree['jane']._fpointer), {'tree 1': ['diane'], 'tree 2': ['diane']})

# when creating new node on subtree, check that it has no impact on initial tree
tree2.create_node("Jill", "jill", parent="diane")
self.assertIn('jill', tree2)
self.assertIn('jill', tree2.is_branch("diane"))
self.assertNotIn('jill', self.tree)
self.assertNotIn('jill', self.tree.is_branch("diane"))
2 changes: 1 addition & 1 deletion treelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

>>> from __future__ import unicode_literals
"""
__version__ = '1.5.5'
__version__ = '1.5.6'

from .tree import Tree
from .node import Node
Loading