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

Fix #139: replace stdout with end in show. #140

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 38 additions & 30 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import contextlib

import sys

import os

try:
from StringIO import StringIO as BytesIO
from StringIO import StringIO
except ImportError:
from io import BytesIO
from io import StringIO
import unittest
from treelib import Tree, Node
from treelib.tree import NodeIDAbsentError, LoopError
Expand Down Expand Up @@ -242,31 +244,38 @@ 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()
f = StringIO()
with contextlib.redirect_stdout(f):
self.tree.show()
self.assertEqual(
self.tree._reader,
u'''Hárry
f.getvalue(),
u'''Hárry
├── Bill
│ └── George
└── Jane
├── Diane
└── Jill
└── Mark

'''
)
f.close()
self.tree.remove_node("jill")
self.assertNotIn('jill', self.tree.nodes.keys())
self.assertNotIn('mark', self.tree.nodes.keys())
self.tree.show()
f = StringIO()
with contextlib.redirect_stdout(f):
self.tree.show(end='\n')
self.assertEqual(
self.tree._reader,
u'''Hárry
f.getvalue(),
u'''Hárry
├── Bill
│ └── George
└── Jane
└── Diane
'''
)
f.close()

def test_paste_at_root_level(self):
t1 = Tree()
Expand All @@ -285,15 +294,22 @@ def test_paste_at_root_level(self):
self.assertEqual(t1.root, 'r')
self.assertNotIn('r2', t1._nodes.keys())
self.assertEqual(set(t1._nodes.keys()), {'r', 'a', 'a1', 'b', 'c', 'd', 'd1'})
t1.show()
self.assertEqual(t1._reader, '''root
f = StringIO()
with contextlib.redirect_stdout(f):
t1.show()
self.assertEqual(
f.getvalue(),
'''root
├── A
│ └── A1
├── B
├── C
└── D
└── D1
''')

'''
)
f.close()

def test_rsearch(self):
for nid in ["hárry", "jane", "diane"]:
Expand Down Expand Up @@ -340,19 +356,15 @@ def __init__(self, color):
def test_show_data_property(self):
new_tree = Tree()

sys.stdout = open(os.devnull, "w") # stops from printing to console

try:
new_tree.show()
with open(os.devnull, 'w') as f:
with contextlib.redirect_stdout(f):
new_tree.show()

class Flower(object):
def __init__(self, color):
self.color = color
new_tree.create_node("Jill", "jill", data=Flower("white"))
new_tree.show(data_property="color")
finally:
sys.stdout.close()
sys.stdout = sys.__stdout__ # stops from printing to console
class Flower(object):
def __init__(self, color):
self.color = color
new_tree.create_node("Jill", "jill", data=Flower("white"))
new_tree.show(data_property="color")

def test_level(self):
self.assertEqual(self.tree.level('hárry'), 0)
Expand Down Expand Up @@ -382,13 +394,9 @@ def test_show(self):
if sys.version_info[0] < 3:
reload(sys)
sys.setdefaultencoding('utf-8')
sys.stdout = open(os.devnull, "w") # stops from printing to console

try:
self.tree.show()
finally:
sys.stdout.close()
sys.stdout = sys.__stdout__ # stops from printing to console
with open(os.devnull, "w") as f:
with contextlib.redirect_stdout(f):
self.tree.show()

def tearDown(self):
self.tree = None
Expand Down
15 changes: 8 additions & 7 deletions treelib/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,8 @@ def _write_line(line, f):
key, reverse, line_type, data_property, func=handler)

def show(self, nid=None, level=ROOT, idhidden=True, filter=None,
key=None, reverse=False, line_type='ascii-ex', data_property=None, stdout=True):
key=None, reverse=False, line_type='ascii-ex', data_property=None,
end='\n\n'):
"""
Print the tree structure in hierarchy style.

Expand All @@ -801,23 +802,23 @@ def show(self, nid=None, level=ROOT, idhidden=True, filter=None,
: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 end: the string to append to the final line of the output. By
default, an extra newline is printed; pass '\n' if this is not
desired.
:return: None
"""
self._reader = ""
self._reader = []
Copy link
Collaborator

@leonardbinet leonardbinet Feb 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO there is no reason to have a member variable as @GalacticStarfish explains it in #91. What do you think of replacing self._reader by a reader variable?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, that's just a wast of memory cause it is cached but not reused AFAICS.


def write(line):
self._reader += line.decode('utf-8') + "\n"
self._reader.append(line.decode('utf-8'))

try:
self.__print_backend(nid, level, idhidden, filter,
key, reverse, line_type, data_property, func=write)
except NodeIDAbsentError:
print('Tree is empty')

if stdout:
print(self._reader)
else:
return self._reader
print('\n'.join(self._reader), end=end)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly disagree with using print, see #141

Using print to stdout in a library is a bad practice, see for instance this article

It's also bad practice to ship a package that prints directly to stdout because it removes the user's ability to control the messages.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This article talk about logging. I don't think It is related to this use case here. Here it is just an helper function.

You could create 2 functions, it would make the API simpler. I also think a stream as input is better, anyway the default value is stdout.


def siblings(self, nid):
"""
Expand Down