Repository: caesar0301/treelib

Issue:
When i create a Tree , its not maintaining the order which in i created the children when i invoke show method
>>> from treelib import Node, Tree
>>> tree = Tree()
>>> tree.create_node('Students', 'Students', parent=None)
Node(tag=Students, identifier=Students, data=None)
>>> tree.create_node('Ben', 'Ben',parent='Students')
Node(tag=Ben, identifier=Ben, data=None)
>>> tree.create_node('Annie', 'Annie',parent='Students')
Node(tag=Annie, identifier=Annie, data=None)
>>> tree.show()
Students
├── Annie
└── Ben


As you see eventhough i created Ben Node first as child it is displaying Annie first, is there an argument in show() or in create_method() which i can pass to maintain the order when i call show method

Discussion:
caesar0301: More UTs are required
HollowMan6: It's such a surprise to finally see a response for my PR that is opened 2 years ago! I can't remember the details of this PR at all, but still will check to see if I can help contribute to this later anyway.

Patch:
diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index fc7a708..5ffa075 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -56,4 +56,4 @@ jobs:
         ./scripts/flake8.sh
     - name: Test with pytest
       run: |
-        pytest
\ No newline at end of file
+        pytest
diff --git a/AUTHORS b/AUTHORS
index f358261..ad58a53 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,10 @@
-Brett Alistair Kromkamp - brettkromkamp@gmail.com
-Xiaming Chen - chenxm35@gmail.com
-Holger Bast - holgerbast@gmx.de
+# Contributors
 
-and other committers etc.
\ No newline at end of file
+* Brett Alistair Kromkamp (brettkromkamp@gmail.com): Post basic idea online.
+* Xiaming Chen (chenxm35@gmail.com): Finished primary parts and made the library freely public.
+* Holger Bast (holgerbast@gmx.de): Replaced list with `dict` for fast node index and optimized the performance.
+* Ilya Kuprik (ilya-spy@ynadex.ru): Added ZIGZAG tree-walk algorithm to tree traversal.
+
+and other committers etc.
+
+Thank you all.
diff --git a/HISTORY b/HISTORY
index b28a57a..2e1491d 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,9 +1,17 @@
-Mar 27 2023 V1.6.3
+# History
+
+Mar 27, 2023 V1.6.3
 Apply to black code style. Migrate future to six
 
-Oct 13 2019 V1.5.6
+
+Oct 13, 2019 V1.5.6
 Add ability to independently mutate multiple shallow copies of same initial tree.
 
+
+Aug 10, 2017 V1.4.0
+Abandon supporting Python 3.2 since v1.4.0.
+
+
 Mar 27, 2014 V1.2.6
 Node identifier supports multiple data types beyond int and str.
 Finish depth() and leaves() routines.
diff --git a/README.md b/README.md
index dc6011d..c1ee089 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-treelib
--------
+# treelib
 
 Tree implementation in python: simple for you to use.
 
@@ -16,38 +15,24 @@ Tree implementation in python: simple for you to use.
 [![GitHub pull-requests](https://img.shields.io/github/issues-pr/caesar0301/treelib.svg)](https://GitHub.com/caesar0301/treelib/pulls)
 [![GitHub pull-requests closed](https://img.shields.io/github/issues-pr-closed/caesar0301/treelib.svg)](https://GitHub.com/caesar0301/treelib/pulls?q=is%3Apr+is%3Aclosed)
 
+## Quick Start
 
+    pip install -U treelib
 
-
-Quick Start
------------
-
-    sudo easy_install -U treelib
-    
-install with pip by pip install treelib
-
-Documentation
--------------
+## Documentation
 
 For installation, APIs and examples, see http://treelib.readthedocs.io/en/latest/
 
-Update
--------
+## Code Style
 
-* 2017-08-10: Abandon supporting Python 3.2 since v1.4.0.
-* 2012-07-07: First published.
+`treelib` complies with [black](https://github.com/psf/black) formatter and
+specific [flake8 validations](https://github.com/caesar0301/treelib/blob/master/scripts/flake8.sh).
+Before creating a pull request, please make sure you pass the local validation
+with `scripts/flake8.sh`.
 
+## Contributors
 
-Contributors
-------------
-
-> Brett Alistair Kromkamp (brettkromkamp@gmail.com): Post basic idea online.
->
-> Xiaming Chen (chenxm35@gmail.com): Finished primary parts and made the library freely public.
->
-> Holger Bast (holgerbast@gmx.de): Replaced list with `dict` for fast node index and optimized the performance.
->
-> Ilya Kuprik (ilya-spy@ynadex.ru): Added ZIGZAG tree-walk algorithm to tree traversal.
+Thank you all,
+[committers](https://github.com/caesar0301/treelib/graphs/contributors).
 
 [![ForTheBadge built-with-love](http://ForTheBadge.com/images/badges/built-with-love.svg)](https://GitHub.com/Naereen/)
-
diff --git a/examples/family_tree.py b/examples/family_tree.py
index d8b956d..fa2293d 100644
--- a/examples/family_tree.py
+++ b/examples/family_tree.py
@@ -5,7 +5,7 @@
 #
 __author__ = "chenxm"
 
-from treelib import Tree, Node
+from treelib import Tree
 
 
 def create_family_tree():
diff --git a/examples/folder_tree.py b/examples/folder_tree.py
index 0e942e8..695d6ef 100644
--- a/examples/folder_tree.py
+++ b/examples/folder_tree.py
@@ -105,7 +105,7 @@ def get_parentid(current_depth, root, dir):
     search_string = os.path.join(root, dir)
     pos2 = search_string.rfind("/")
     pos1 = search_string.rfind("/", 0, pos2)
-    parent_dir = search_string[pos1 + 1 : pos2]
+    parent_dir = search_string[pos1 + 1 : pos2]  # noqa: E203
     parentid = (
         str(current_depth - 1)
         + "_"
@@ -130,10 +130,8 @@ def crawler():
     global FILECOUNT
 
     for root, dirs, files in os.walk(rootPath):
-
         # +++ DIRECTORIES +++
         for dir in dirs:
-
             # calculating current depth
             current_depth = os.path.join(root, dir).count("/") - start_depth
 
@@ -155,7 +153,6 @@ def crawler():
 
         # +++ FILES +++
         for filename in fnmatch.filter(files, pattern):
-
             if dir in folder_blacklist:
                 continue
 
diff --git a/examples/recursive_dirtree_generator.py b/examples/recursive_dirtree_generator.py
index 776c193..986cd1a 100644
--- a/examples/recursive_dirtree_generator.py
+++ b/examples/recursive_dirtree_generator.py
@@ -11,12 +11,21 @@ import treelib
 import random
 import hashlib
 from string import digits, letters
+import sys
+
 
 MAX_FILES_PER_DIR = 10
 
 
+def range2(stop):
+    if sys.version_info[0] < 3:
+        return xrange(stop)  # noqa: F821
+    else:
+        return range(stop)
+
+
 def get_random_string(length):
-    return "".join(random.choice(digits + letters) for _ in range(length))
+    return "".join(random.choice(digits + letters) for _ in range2(length))
 
 
 def build_recursive_tree(tree, base, depth, width):
@@ -32,7 +41,7 @@ def build_recursive_tree(tree, base, depth, width):
     """
     if depth >= 0:
         depth -= 1
-        for i in xrange(width):
+        for i in range2(width):
             directory = Directory()
             tree.create_node(
                 "{0}".format(directory.name),
@@ -52,7 +61,7 @@ class Directory(object):
     def __init__(self):
         self._name = get_random_string(64)
         self._files = [
-            File() for _ in xrange(MAX_FILES_PER_DIR)
+            File() for _ in range2(MAX_FILES_PER_DIR)
         ]  # Each directory contains 1000 files
 
     @property
diff --git a/scripts/flake8.sh b/scripts/flake8.sh
index 9e05f3e..b93226d 100755
--- a/scripts/flake8.sh
+++ b/scripts/flake8.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
 flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
-flake8 . --count --exit-zero --max-line-length=127 --statistics
+flake8 . --count --max-line-length=127 --exclude docs/source/conf.py --statistics
diff --git a/setup.py b/setup.py
index 559a1a7..910e467 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 from setuptools import setup
 
-__version__ = "1.6.3"
+__version__ = "1.6.4"
 
 
 setup(
diff --git a/treelib/__init__.py b/treelib/__init__.py
index d7e6ec7..4902412 100644
--- a/treelib/__init__.py
+++ b/treelib/__init__.py
@@ -35,5 +35,5 @@ you have to trigger the compatibility by declaring `unicode_literals` in the cod
    >>> from __future__ import unicode_literals
 """
 
-from .tree import Tree
-from .node import Node
+from .tree import Tree  # noqa: F401
+from .node import Node  # noqa: F401
diff --git a/treelib/tree.py b/treelib/tree.py
index 285b8c0..908db4e 100644
--- a/treelib/tree.py
+++ b/treelib/tree.py
@@ -162,6 +162,7 @@ class Tree(object):
         reverse=False,
         line_type="ascii-ex",
         data_property=None,
+        sorting=True,
         func=print,
     ):
         """
@@ -215,17 +216,17 @@ class Tree(object):
                     return "%s[%s]" % (node.tag, node.identifier)
 
         # legacy ordering
-        if key is None:
+        if sorting and key is None:
 
             def key(node):
                 return node
 
         # iter with func
-        for pre, node in self.__get(nid, level, filter, key, reverse, line_type):
+        for pre, node in self.__get(nid, level, filter, key, reverse, line_type, sorting):
             label = get_label(node)
             func("{0}{1}".format(pre, label).encode("utf-8"))
 
-    def __get(self, nid, level, filter_, key, reverse, line_type):
+    def __get(self, nid, level, filter_, key, reverse, line_type, sorting):
         # default filter
         if filter_ is None:
 
@@ -242,9 +243,9 @@ class Tree(object):
             "ascii-emh": ("\u2502", "\u255e\u2550\u2550 ", "\u2558\u2550\u2550 "),
         }[line_type]
 
-        return self.__get_iter(nid, level, filter_, key, reverse, dt, [])
+        return self.__get_iter(nid, level, filter_, key, reverse, dt, [], sorting)
 
-    def __get_iter(self, nid, level, filter_, key, reverse, dt, is_last):
+    def __get_iter(self, nid, level, filter_, key, reverse, dt, is_last, sorting):
         dt_vertical_line, dt_line_box, dt_line_corner = dt
 
         nid = self.root if nid is None else nid
@@ -267,15 +268,16 @@ class Tree(object):
                 self[i] for i in node.successors(self._identifier) if filter_(self[i])
             ]
             idxlast = len(children) - 1
-            if key:
-                children.sort(key=key, reverse=reverse)
-            elif reverse:
-                children = reversed(children)
+            if sorting:
+                if key:
+                    children.sort(key=key, reverse=reverse)
+                elif reverse:
+                    children = reversed(children)
             level += 1
             for idx, child in enumerate(children):
                 is_last.append(idx == idxlast)
                 for item in self.__get_iter(
-                    child.identifier, level, filter_, key, reverse, dt, is_last
+                    child.identifier, level, filter_, key, reverse, dt, is_last, sorting
                 ):
                     yield item
                 is_last.pop()
@@ -838,6 +840,7 @@ class Tree(object):
         reverse=False,
         line_type="ascii-ex",
         data_property=None,
+        sorting=True,
     ):
         """
         Save the tree into file for offline analysis.
@@ -858,6 +861,7 @@ class Tree(object):
             reverse,
             line_type,
             data_property,
+            sorting,
             func=handler,
         )
 
@@ -872,6 +876,7 @@ class Tree(object):
         line_type="ascii-ex",
         data_property=None,
         stdout=True,
+        sorting=True,
     ):
         """
         Print the tree structure in hierarchy style.
@@ -881,7 +886,9 @@ class Tree(object):
         former two use the same backend to generate a string of tree structure in a
         text graph.
 
-        * Version >= 1.2.7a*: you can also specify the ``line_type`` parameter, such as 'ascii' (default), 'ascii-ex', 'ascii-exr', 'ascii-em', 'ascii-emv', 'ascii-emh') to the change graphical form.
+        * Version >= 1.2.7a*: you can also specify the ``line_type`` parameter,
+          such as 'ascii' (default), 'ascii-ex', 'ascii-exr', 'ascii-em', 'ascii-emv',
+          'ascii-emh') to the change graphical form.
 
         :param nid: the reference node to start expanding.
         :param level: the node level in the tree (root as level 0).
@@ -893,6 +900,9 @@ class Tree(object):
         :param reverse: the ``reverse`` param for sorting :class:`Node` objects in the same level.
         :param line_type:
         :param data_property: the property on the node data object to be printed.
+        :param sorting: if True perform node sorting, if False return
+            nodes in original insertion order. In latter case @key and
+            @reverse parameters are ignored.
         :return: None
         """
         self._reader = ""
@@ -910,6 +920,7 @@ class Tree(object):
                 reverse,
                 line_type,
                 data_property,
+                sorting,
                 func=write,
             )
         except NodeIDAbsentError:
@@ -959,7 +970,7 @@ class Tree(object):
                         if self.level(node.identifier) == level
                     ]
                 )
-            except:
+            except Exception:
                 raise TypeError(
                     "level should be an integer instead of '%s'" % type(level)
                 )
@@ -1063,21 +1074,33 @@ class Tree(object):
         """To format the tree in JSON format."""
         return json.dumps(self.to_dict(with_data=with_data, sort=sort, reverse=reverse))
 
-    def to_graphviz(self, filename=None, shape="circle", graph="digraph",
-                    filter=None, key=None, reverse=False, sorting=True):
+    def to_graphviz(
+        self,
+        filename=None,
+        shape="circle",
+        graph="digraph",
+        filter=None,
+        key=None,
+        reverse=False,
+        sorting=True,
+    ):
         """Exports the tree in the dot format of the graphviz software"""
         nodes, connections = [], []
         if self.nodes:
-
-            for n in self.expand_tree(mode=self.WIDTH, filter=filter, key=key,
-                                      reverse=reverse, sorting=sorting):
+            for n in self.expand_tree(
+                mode=self.WIDTH,
+                filter=filter,
+                key=key,
+                reverse=reverse,
+                sorting=sorting,
+            ):
                 nid = self[n].identifier
                 state = '"{0}" [label="{1}", shape={2}]'.format(nid, self[n].tag, shape)
                 nodes.append(state)
 
                 for c in self.children(nid):
                     cid = c.identifier
-                    edge = '->' if graph == 'digraph' else '--'
+                    edge = "->" if graph == "digraph" else "--"
                     connections.append(('"{0}" ' + edge + ' "{1}"').format(nid, cid))
 
         # write nodes and connections to dot format


Test patch:
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index 05fde9d..0f04fbf 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -7,7 +7,7 @@ import os
 import unittest
 
 from treelib import Tree
-from treelib.plugins import *
+from treelib.plugins import export_to_dot
 
 
 class DotExportCase(unittest.TestCase):
diff --git a/tests/test_tree.py b/tests/test_tree.py
index 308b6c5..719fe78 100644
--- a/tests/test_tree.py
+++ b/tests/test_tree.py
@@ -6,10 +6,6 @@ import sys
 
 import os
 
-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, LoopError
@@ -304,7 +300,6 @@ class TreeCase(unittest.TestCase):
         )
 
     def test_merge(self):
-
         # merge on empty initial tree
         t1 = Tree(identifier="t1")
         t2 = self.get_t2()
@@ -381,7 +376,6 @@ class TreeCase(unittest.TestCase):
         )
 
     def test_paste(self):
-
         # paste under root
         t1 = self.get_t1()
         t2 = self.get_t2()
@@ -545,7 +539,7 @@ Hárry
 
     def test_show(self):
         if sys.version_info[0] < 3:
-            reload(sys)
+            reload(sys)  # noqa: F821
             sys.setdefaultencoding("utf-8")
         sys.stdout = open(os.devnull, "w")  # stops from printing to console
 
@@ -559,6 +553,23 @@ Hárry
         self.tree = None
         self.copytree = None
 
+    def test_show_without_sorting(self):
+        t = Tree()
+        t.create_node('Students', 'Students', parent=None)
+        Node(tag='Students', identifier='Students', data=None)
+        t.create_node('Ben', 'Ben',parent='Students')
+        Node(tag='Ben', identifier='Ben', data=None)
+        t.create_node('Annie', 'Annie',parent='Students')
+        Node(tag='Annie', identifier='Annie', data=None)
+        t.show()
+        self.assertEqual(
+            t.show(sorting=False, stdout=False),
+            """Students
+├── Ben
+└── Annie
+""",
+        )
+
     def test_all_nodes_itr(self):
         """
         tests: Tree.all_nodes_iter