From 2f9aa73ea7230821fb36a38a90976c2cecb45418 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 8 Jul 2015 16:10:26 +0200 Subject: [PATCH 1/9] export to dot format --- treelib/tree.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/treelib/tree.py b/treelib/tree.py index 31c0f10..ac49727 100644 --- a/treelib/tree.py +++ b/treelib/tree.py @@ -671,5 +671,29 @@ def to_json(self, with_data=False, sort=True, reverse=False): """Return the json string corresponding to self""" return json.dumps(self.to_dict(with_data=with_data, sort=sort, reverse=reverse)) + def to_dot(self, filename, shape='circle', graph='digraph'): + + nodes, connections = [], [] + for n in self.expand_tree(mode=self.WIDTH): + nid = self[n].identifier + state = nid + ' [label="' + self[n].tag + '", shape=' + shape + ']' + nodes.append(state) + + for c in self.children(nid): + cid = c.identifier + + connections.append(str(nid) + ' -> ' + str(cid)) + + # write nodes and connections to dot format + with open(filename, 'w') as f: + f.write(graph +' tree {\n') + for n in nodes: + f.write('\t' + n + '\n') + + for c in connections: + f.write('\t' + c + '\n') + + f.write('}') + if __name__ == '__main__': pass From 08dc67b57383841b4273c12b4b47d45b29310893 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 15 Jul 2015 00:15:08 +0200 Subject: [PATCH 2/9] unicode encoding unit tests added --- tests/test_treelib.py | 57 ++++++++++++++++++++++++++++++++++++++++++- treelib/tree.py | 31 +++++++++++++---------- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/tests/test_treelib.py b/tests/test_treelib.py index 028fc94..8a6b472 100644 --- a/tests/test_treelib.py +++ b/tests/test_treelib.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals import sys +import os try: from StringIO import StringIO as BytesIO except ImportError: @@ -275,7 +276,61 @@ def test_show(self): def tearDown(self): self.tree = None self.copytree = None - + + def read_generated_output(self, filename): + output = open(filename) + generated = output.read().decode('utf-8') + output.close() + + return generated + + def test_to_dot(self): + self.tree.to_dot('tree.dot') + expected = """\ +digraph tree { +\thárry [label="Hárry", shape=circle] +\tbill [label="Bill", shape=circle] +\tjane [label="Jane", shape=circle] +\tgeorge [label="George", shape=circle] +\tdiane [label="Diane", shape=circle] + +\thárry -> jane +\thárry -> bill +\tbill -> george +\tjane -> diane +}""" + + generated = self.read_generated_output('tree.dot') + + self.assertEqual(generated, expected, "Generated dot tree is not the expected one") + os.remove('tree.dot') + + def test_to_dot_empty_tree(self): + empty_tree = Tree() + empty_tree.to_dot("tree.dot") + + expected = """\ +digraph tree { + +}""" + generated = self.read_generated_output('tree.dot') + + self.assertEqual(expected, generated, 'The generated output for an empty tree is not empty') + os.remove('tree.dot') + + def test_unicode_filename(self): + tree = Tree() + tree.create_node('Node 1', 'node_1') + tree.to_dot('ŕʩϢ.dot') + + expected = """\ +digraph tree { +\tnode_1 [label="Node 1", shape=circle] + +}""" + self.assertTrue(os.path.isfile('ŕʩϢ.dot'), "The file ŕʩϢ.dot could not be found.") + generated = self.read_generated_output('ŕʩϢ.dot') + self.assertEqual(expected, generated, "The generated file content is not the expected one") def suite(): suites = [NodeCase, TreeCase] diff --git a/treelib/tree.py b/treelib/tree.py index ac49727..ecd4585 100644 --- a/treelib/tree.py +++ b/treelib/tree.py @@ -672,26 +672,31 @@ def to_json(self, with_data=False, sort=True, reverse=False): return json.dumps(self.to_dict(with_data=with_data, sort=sort, reverse=reverse)) def to_dot(self, filename, shape='circle', graph='digraph'): - + nodes, connections = [], [] - for n in self.expand_tree(mode=self.WIDTH): - nid = self[n].identifier - state = nid + ' [label="' + self[n].tag + '", shape=' + shape + ']' - nodes.append(state) - - for c in self.children(nid): - cid = c.identifier - - connections.append(str(nid) + ' -> ' + str(cid)) + if self.nodes: + + for n in self.expand_tree(mode=self.WIDTH): + nid = self[n].identifier + state = nid + ' [label="' + self[n].tag + '", shape=' + shape + ']' + nodes.append(state) + + for c in self.children(nid): + cid = c.identifier + + connections.append(nid + ' -> ' + cid) # write nodes and connections to dot format with open(filename, 'w') as f: f.write(graph +' tree {\n') for n in nodes: - f.write('\t' + n + '\n') - + out_line = '\t' + n + '\n' + f.write(out_line.encode('utf-8')) + + f.write('\n') for c in connections: - f.write('\t' + c + '\n') + out_line = '\t' + c + '\n' + f.write(out_line.encode('utf-8')) f.write('}') From e7047ba2ebcc0ff2b3c2be93e0f899938ef45a4a Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 15 Jul 2015 00:17:01 +0200 Subject: [PATCH 3/9] remove unicode file after unittest if test succeeds --- tests/test_treelib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_treelib.py b/tests/test_treelib.py index 8a6b472..a9383d1 100644 --- a/tests/test_treelib.py +++ b/tests/test_treelib.py @@ -331,6 +331,7 @@ def test_unicode_filename(self): self.assertTrue(os.path.isfile('ŕʩϢ.dot'), "The file ŕʩϢ.dot could not be found.") generated = self.read_generated_output('ŕʩϢ.dot') self.assertEqual(expected, generated, "The generated file content is not the expected one") + os.remove('ŕʩϢ.dot') def suite(): suites = [NodeCase, TreeCase] From 22cf20607610fcb480c26dac9bdc189eef6e72c8 Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 15 Jul 2015 00:21:25 +0200 Subject: [PATCH 4/9] added docstring for the to_dot method --- treelib/tree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/treelib/tree.py b/treelib/tree.py index ecd4585..d0b198c 100644 --- a/treelib/tree.py +++ b/treelib/tree.py @@ -672,6 +672,7 @@ def to_json(self, with_data=False, sort=True, reverse=False): return json.dumps(self.to_dict(with_data=with_data, sort=sort, reverse=reverse)) def to_dot(self, filename, shape='circle', graph='digraph'): + """Exports the tree in the dot format of the graphivz software""" nodes, connections = [], [] if self.nodes: From a0dbcf468adea104b98af2cca9e3edeed608e68d Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 15 Jul 2015 13:18:17 +0200 Subject: [PATCH 5/9] fixed the wrong encoding/decoding problem between python 2 and 3 by using the codecs library --- tests/test_treelib.py | 5 +++-- treelib/tree.py | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_treelib.py b/tests/test_treelib.py index a9383d1..874a314 100644 --- a/tests/test_treelib.py +++ b/tests/test_treelib.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import sys import os +import codecs try: from StringIO import StringIO as BytesIO except ImportError: @@ -278,8 +279,8 @@ def tearDown(self): self.copytree = None def read_generated_output(self, filename): - output = open(filename) - generated = output.read().decode('utf-8') + output = codecs.open(filename, 'r', 'utf-8') + generated = output.read() output.close() return generated diff --git a/treelib/tree.py b/treelib/tree.py index d0b198c..d78d992 100644 --- a/treelib/tree.py +++ b/treelib/tree.py @@ -8,6 +8,7 @@ from __future__ import unicode_literals import sys import json +import codecs from copy import deepcopy try: from .node import Node @@ -673,7 +674,7 @@ def to_json(self, with_data=False, sort=True, reverse=False): def to_dot(self, filename, shape='circle', graph='digraph'): """Exports the tree in the dot format of the graphivz software""" - + nodes, connections = [], [] if self.nodes: @@ -688,16 +689,14 @@ def to_dot(self, filename, shape='circle', graph='digraph'): connections.append(nid + ' -> ' + cid) # write nodes and connections to dot format - with open(filename, 'w') as f: + with codecs.open(filename, 'w', 'utf-8') as f: f.write(graph +' tree {\n') for n in nodes: - out_line = '\t' + n + '\n' - f.write(out_line.encode('utf-8')) + f.write('\t' + n + '\n') f.write('\n') for c in connections: - out_line = '\t' + c + '\n' - f.write(out_line.encode('utf-8')) + f.write('\t' + c + '\n') f.write('}') From fab4a9a1dfa3d76b5f126f75953850a8bf04c52a Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 17 Jul 2015 18:56:06 +0200 Subject: [PATCH 6/9] export_to_dot as a separate function in the plugin module --- tests/test_plugins.py | 83 +++++++++++++++++++++++++++++++++++++++++++ treelib/plugins.py | 32 ++++++++++++++++- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 tests/test_plugins.py diff --git a/tests/test_plugins.py b/tests/test_plugins.py new file mode 100644 index 0000000..e641011 --- /dev/null +++ b/tests/test_plugins.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from treelib import Tree +from treelib.plugins import * +import codecs +import os +import unittest + +class PluginCase(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.create_node("Diane", "diane", parent="jane") + tree.create_node("George", "george", parent="bill") + self.tree = tree + + def read_generated_output(self, filename): + output = codecs.open(filename, 'r', 'utf-8') + generated = output.read() + output.close() + + return generated + + def test_to_dot(self): + export_to_dot(self.tree, 'tree.dot') + expected = """\ +digraph tree { +\thárry [label="Hárry", shape=circle] +\tbill [label="Bill", shape=circle] +\tjane [label="Jane", shape=circle] +\tgeorge [label="George", shape=circle] +\tdiane [label="Diane", shape=circle] + +\thárry -> jane +\thárry -> bill +\tbill -> george +\tjane -> diane +}""" + + self.assertTrue(os.path.isfile('tree.dot'), "The file tree.dot could not be found.") + generated = self.read_generated_output('tree.dot') + + self.assertEqual(generated, expected, "Generated dot tree is not the expected one") + os.remove('tree.dot') + + def test_to_dot_empty_tree(self): + empty_tree = Tree() + export_to_dot(empty_tree, 'tree.dot') + + expected = """\ +digraph tree { + +}""" + self.assertTrue(os.path.isfile('tree.dot'), "The file tree.dot could not be found.") + generated = self.read_generated_output('tree.dot') + + self.assertEqual(expected, generated, 'The generated output for an empty tree is not empty') + os.remove('tree.dot') + + def test_unicode_filename(self): + tree = Tree() + tree.create_node('Node 1', 'node_1') + export_to_dot(tree, 'ŕʩϢ.dot') + + expected = """\ +digraph tree { +\tnode_1 [label="Node 1", shape=circle] + +}""" + self.assertTrue(os.path.isfile('ŕʩϢ.dot'), "The file ŕʩϢ.dot could not be found.") + generated = self.read_generated_output('ŕʩϢ.dot') + self.assertEqual(expected, generated, "The generated file content is not the expected one") + os.remove('ŕʩϢ.dot') + + def tearDown(self): + self.tree = None + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/treelib/plugins.py b/treelib/plugins.py index a0db613..346b926 100644 --- a/treelib/plugins.py +++ b/treelib/plugins.py @@ -1,5 +1,35 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """This is a public location to maintain contributed - utlities to extend the basic Tree class. + utilities to extend the basic Tree class. """ +from __future__ import unicode_literals +import codecs + +def export_to_dot(tree, filename, shape='circle', graph='digraph'): + """Exports the tree in the dot format of the graphviz software""" + + nodes, connections = [], [] + if tree.nodes: + + for n in tree.expand_tree(mode=tree.WIDTH): + nid = tree[n].identifier + state = nid + ' [label="' + tree[n].tag + '", shape=' + shape + ']' + nodes.append(state) + + for c in tree.children(nid): + cid = c.identifier + + connections.append(nid + ' -> ' + cid) + + # write nodes and connections to dot format + with codecs.open(filename, 'w', 'utf-8') as f: + f.write(graph +' tree {\n') + for n in nodes: + f.write('\t' + n + '\n') + + f.write('\n') + for c in connections: + f.write('\t' + c + '\n') + + f.write('}') \ No newline at end of file From d06a252f8d947bad2d13a8f0d4db874d1f1c9c91 Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 17 Jul 2015 18:58:57 +0200 Subject: [PATCH 7/9] cleanup --- tests/test_treelib.py | 57 ------------------------------------------- treelib/tree.py | 29 ---------------------- 2 files changed, 86 deletions(-) diff --git a/tests/test_treelib.py b/tests/test_treelib.py index 874a314..a7aa722 100644 --- a/tests/test_treelib.py +++ b/tests/test_treelib.py @@ -278,62 +278,6 @@ def tearDown(self): self.tree = None self.copytree = None - def read_generated_output(self, filename): - output = codecs.open(filename, 'r', 'utf-8') - generated = output.read() - output.close() - - return generated - - def test_to_dot(self): - self.tree.to_dot('tree.dot') - expected = """\ -digraph tree { -\thárry [label="Hárry", shape=circle] -\tbill [label="Bill", shape=circle] -\tjane [label="Jane", shape=circle] -\tgeorge [label="George", shape=circle] -\tdiane [label="Diane", shape=circle] - -\thárry -> jane -\thárry -> bill -\tbill -> george -\tjane -> diane -}""" - - generated = self.read_generated_output('tree.dot') - - self.assertEqual(generated, expected, "Generated dot tree is not the expected one") - os.remove('tree.dot') - - def test_to_dot_empty_tree(self): - empty_tree = Tree() - empty_tree.to_dot("tree.dot") - - expected = """\ -digraph tree { - -}""" - generated = self.read_generated_output('tree.dot') - - self.assertEqual(expected, generated, 'The generated output for an empty tree is not empty') - os.remove('tree.dot') - - def test_unicode_filename(self): - tree = Tree() - tree.create_node('Node 1', 'node_1') - tree.to_dot('ŕʩϢ.dot') - - expected = """\ -digraph tree { -\tnode_1 [label="Node 1", shape=circle] - -}""" - self.assertTrue(os.path.isfile('ŕʩϢ.dot'), "The file ŕʩϢ.dot could not be found.") - generated = self.read_generated_output('ŕʩϢ.dot') - self.assertEqual(expected, generated, "The generated file content is not the expected one") - os.remove('ŕʩϢ.dot') - def suite(): suites = [NodeCase, TreeCase] suite = unittest.TestSuite() @@ -342,6 +286,5 @@ def suite(): return suite - if __name__ == '__main__': unittest.main(defaultTest='suite') diff --git a/treelib/tree.py b/treelib/tree.py index d78d992..31c0f10 100644 --- a/treelib/tree.py +++ b/treelib/tree.py @@ -8,7 +8,6 @@ from __future__ import unicode_literals import sys import json -import codecs from copy import deepcopy try: from .node import Node @@ -672,33 +671,5 @@ def to_json(self, with_data=False, sort=True, reverse=False): """Return the json string corresponding to self""" return json.dumps(self.to_dict(with_data=with_data, sort=sort, reverse=reverse)) - def to_dot(self, filename, shape='circle', graph='digraph'): - """Exports the tree in the dot format of the graphivz software""" - - nodes, connections = [], [] - if self.nodes: - - for n in self.expand_tree(mode=self.WIDTH): - nid = self[n].identifier - state = nid + ' [label="' + self[n].tag + '", shape=' + shape + ']' - nodes.append(state) - - for c in self.children(nid): - cid = c.identifier - - connections.append(nid + ' -> ' + cid) - - # write nodes and connections to dot format - with codecs.open(filename, 'w', 'utf-8') as f: - f.write(graph +' tree {\n') - for n in nodes: - f.write('\t' + n + '\n') - - f.write('\n') - for c in connections: - f.write('\t' + c + '\n') - - f.write('}') - if __name__ == '__main__': pass From a7b62a7290cecb19bcf533e56fc0af19876b7cae Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 17 Jul 2015 19:02:12 +0200 Subject: [PATCH 8/9] do nothing when run as main module --- treelib/plugins.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/treelib/plugins.py b/treelib/plugins.py index 346b926..91c0daf 100644 --- a/treelib/plugins.py +++ b/treelib/plugins.py @@ -32,4 +32,7 @@ def export_to_dot(tree, filename, shape='circle', graph='digraph'): for c in connections: f.write('\t' + c + '\n') - f.write('}') \ No newline at end of file + f.write('}') + +if __name__ == '__main__': + pass \ No newline at end of file From a7deb98fd6cc09bbb5d60b9865da272f36e4ea83 Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 17 Jul 2015 19:12:32 +0200 Subject: [PATCH 9/9] moved testcases to own test class --- tests/test_plugins.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index e641011..6eeeaa9 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -3,11 +3,11 @@ from __future__ import unicode_literals from treelib import Tree from treelib.plugins import * -import codecs import os import unittest -class PluginCase(unittest.TestCase): +class DotExportCase(unittest.TestCase): + """Test class for the export to dot format function""" def setUp(self): tree = Tree() @@ -25,7 +25,7 @@ def read_generated_output(self, filename): return generated - def test_to_dot(self): + def test_export_to_dot(self): export_to_dot(self.tree, 'tree.dot') expected = """\ digraph tree { @@ -47,7 +47,7 @@ def test_to_dot(self): self.assertEqual(generated, expected, "Generated dot tree is not the expected one") os.remove('tree.dot') - def test_to_dot_empty_tree(self): + def test_export_to_dot_empty_tree(self): empty_tree = Tree() export_to_dot(empty_tree, 'tree.dot')