Repository: caesar0301/treelib

Issue:
AttributeError: 'str' object has no attribute 'decode'
python3.4, OSX 10.10

```python
>>> from treelib import Tree, Node
>>> tree = Tree()
>>> tree.create_node("Harry", "harry")
>>> tree.create_node("Jane", "jane", parent="harry")
>>> tree.show()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/site-packages/treelib/tree.py", line 517, in show
    func=print)
  File "/usr/local/lib/python3.4/site-packages/treelib/tree.py", line 560, in _print_backend
    if idhidden \
AttributeError: 'str' object has no attribute 'decode'
```

Discussion:

Patch:
diff --git a/treelib/tree.py b/treelib/tree.py
index 9bcf610..634566c 100644
--- a/treelib/tree.py
+++ b/treelib/tree.py
@@ -556,16 +556,16 @@ class Tree(object):
         if not self.contains(nid):
             raise NodeIDAbsentError("Node '%s' is not in the tree" % nid)
 
-        label = ('{0}'.format(self[nid].tag.decode('utf-8')))\
+        label = ('{0}'.format(self[nid].tag))\
                  if idhidden \
                     else ('{0}[{1}]'.format(
-                            self[nid].tag.decode('utf-8'),
-                            self[nid].identifier.decode('utf-8')))
+                            self[nid].tag,
+                            self[nid].identifier))
 
         filter = (self._real_true) if (filter is None) else filter
 
         if level == self.ROOT:
-            func(label)
+            func(label.encode('utf8'))
         else:
             leading = ''.join(map(lambda x: DT_VLINE + ' ' * 3
                                   if not x else ' ' * 4, iflast[0:-1]))


Test patch:
diff --git a/tests/test_treelib.py b/tests/test_treelib.py
index 952f851..a061c8a 100644
--- a/tests/test_treelib.py
+++ b/tests/test_treelib.py
@@ -1,4 +1,10 @@
 #!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+try:
+    from StringIO import StringIO as BytesIO
+except ImportError:
+    from io import BytesIO
 import unittest
 from treelib import Tree, Node
 from treelib.tree import NodeIDAbsentError
@@ -58,9 +64,9 @@ class NodeCase(unittest.TestCase):
 class TreeCase(unittest.TestCase):
     def setUp(self):
         tree = Tree()
-        tree.create_node("Harry", "harry")
-        tree.create_node("Jane", "jane", parent="harry")
-        tree.create_node("Bill", "bill", parent="harry")
+        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
@@ -71,14 +77,14 @@ class TreeCase(unittest.TestCase):
         self.assertEqual(isinstance(self.copytree, Tree), True)
 
     def test_is_root(self):
-        self.assertTrue(self.tree._nodes['harry'].is_root())
+        self.assertTrue(self.tree._nodes['hárry'].is_root())
         self.assertFalse(self.tree._nodes['jane'].is_root())
 
     def test_paths_to_leaves(self):
         paths = self.tree.paths_to_leaves()
         self.assertEqual( len(paths), 2 )
-        self.assertTrue( ['harry', 'jane', 'diane'] in paths )
-        self.assertTrue( ['harry', 'bill', 'george'] in paths )
+        self.assertTrue( ['hárry', 'jane', 'diane'] in paths )
+        self.assertTrue( ['hárry', 'bill', 'george'] in paths )
 
     def test_nodes(self):
         self.assertEqual(len(self.tree.nodes), 5)
@@ -148,7 +154,7 @@ class TreeCase(unittest.TestCase):
         # Try getting the level of the node
         """
         self.tree.show()
-        Harry
+        Hárry
         |___ Bill
         |    |___ George
         |         |___ Jill
@@ -161,7 +167,7 @@ class TreeCase(unittest.TestCase):
         self.assertEqual(self.tree.depth(self.tree.get_node("george")), 2)
         self.assertEqual(self.tree.depth("jane"), 1)
         self.assertEqual(self.tree.depth("bill"), 1)
-        self.assertEqual(self.tree.depth("harry"), 0)
+        self.assertEqual(self.tree.depth("hárry"), 0)
 
         # Try getting Exception
         node = Node("Test One", "identifier 1")
@@ -177,11 +183,11 @@ class TreeCase(unittest.TestCase):
                                                             in leaves), True)
 
     def test_link_past_node(self):
-        self.tree.create_node("Jill", "jill", parent="harry")
+        self.tree.create_node("Jill", "jill", parent="hárry")
         self.tree.create_node("Mark", "mark", parent="jill")
-        self.assertEqual("mark" not in self.tree.is_branch("harry"), True)
+        self.assertEqual("mark" not in self.tree.is_branch("hárry"), True)
         self.tree.link_past_node("jill")
-        self.assertEqual("mark" in self.tree.is_branch("harry"), True)
+        self.assertEqual("mark" in self.tree.is_branch("hárry"), True)
 
     def test_expand_tree(self):
         nodes = [self.tree[nid] for nid in self.tree.expand_tree()]
@@ -202,7 +208,7 @@ class TreeCase(unittest.TestCase):
         self.tree.remove_node("jill")
 
     def test_rsearch(self):
-        for nid in ["harry", "jane", "diane"]:
+        for nid in ["hárry", "jane", "diane"]:
             self.assertEqual(nid in self.tree.rsearch("diane"), True)
 
     def test_subtree(self):
@@ -216,8 +222,8 @@ class TreeCase(unittest.TestCase):
 
     def test_remove_subtree(self):
         subtree_shallow = self.tree.remove_subtree("jane")
-        self.assertEqual("jane" not in self.tree.is_branch("harry"), True)
-        self.tree.paste("harry", subtree_shallow)
+        self.assertEqual("jane" not in self.tree.is_branch("hárry"), True)
+        self.tree.paste("hárry", subtree_shallow)
 
     def test_to_json(self):
         self.assertEqual.__self__.maxDiff = None
@@ -225,7 +231,7 @@ class TreeCase(unittest.TestCase):
         self.tree.to_json(True)
 
     def test_siblings(self):
-        self.assertEqual(len(self.tree.siblings("harry")) == 0, True)
+        self.assertEqual(len(self.tree.siblings("hárry")) == 0, True)
         self.assertEqual(self.tree.siblings("jane")[0].identifier == "bill",
                          True)
 
@@ -239,13 +245,29 @@ class TreeCase(unittest.TestCase):
         self.tree.remove_node("jill")
 
     def test_level(self):
-        self.assertEqual(self.tree.level('harry'),  0)
+        self.assertEqual(self.tree.level('hárry'),  0)
         depth = self.tree.depth()
         self.assertEqual(self.tree.level('diane'),  depth)
         self.assertEqual(self.tree.level('diane',
                                          lambda x: x.identifier!='jane'),
                          depth-1)
 
+    def test_print_backend(self):
+        reader = BytesIO()
+
+        def write(line):
+            reader.write(line + b'\n')
+
+        self.tree._print_backend(func=write)
+
+        assert reader.getvalue() == """\
+Hárry
+├── Bill
+│   └── George
+└── Jane
+    └── Diane
+""".encode('utf8')
+
     def tearDown(self):
         self.tree = None
         self.copytree = None