From 91ecdb8b739f2a02823b4ac571d6bd44da6d4b09 Mon Sep 17 00:00:00 2001 From: Yan QiDong Date: Sun, 14 Jan 2018 10:49:43 +0800 Subject: [PATCH] Replace --pretty with --with-raw - `--pretty` is removed. The default effect changed to this. - humanfriendly is added to pretty the bytes. - `--with-raw` is added. There will be an extra column to display raw sizes. - Test cases are enhanced. --- csft/__main__.py | 32 ++++++--------------- requirements.txt | 1 + setup.py | 1 + tests/test_main.py | 72 ++++++++++++++++++++++++++++++++++------------ 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/csft/__main__.py b/csft/__main__.py index 2f76ca7..281ac05 100644 --- a/csft/__main__.py +++ b/csft/__main__.py @@ -5,11 +5,15 @@ The entry point of csft. """ +from __future__ import print_function + import sys from argparse import ArgumentParser from os.path import curdir from pathlib import Path +from humanfriendly import format_size + from . import __name__ as _name, __version__ as _version from ._csft import column, csft2data @@ -35,30 +39,12 @@ def _parse_args(argv): parser.add_argument('path', type=_dir, help='the directory to be analyzed') parser.add_argument('--top', type=_positive_int, metavar='N', help='only display top N results') - parser.add_argument('-p', '--pretty', action='store_true', - help='print size with units') + parser.add_argument('--with-raw', action='store_true', + help='print raw size without units') return parser.parse_args(args=argv) -def pretty_byte(byte): - """ - Convert raw bytes to a value with an appropriate unit. - - :param byte: The number of bytes to be convert. - :return: A value with an appropriate unit. - """ - if byte < 0: - raise ValueError('%s is not positive!', byte) - - units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'] - index = 0 - for index, _ in enumerate(units): - if byte >> (10 * (index + 1)) <= 0: - break - return '%d %s' % (byte / (1024 ** index), units[index]) - - def main(argv=None): """Execute the application from CLI.""" if argv is None: @@ -71,10 +57,10 @@ def main(argv=None): if args.top: data = data.head(args.top) + if args.with_raw: + data['raw'] = data[column.SIZE] - if args.pretty: - data[column.SIZE] = data[column.SIZE].map(pretty_byte) - + data[column.SIZE] = data[column.SIZE].map(format_size) print(data) return 0 diff --git a/requirements.txt b/requirements.txt index e001338..6274dd3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ pandas >= 0.20.3 scandir >= 1.6 pathlib >= 1.0.1 +humanfriendly >= 4.6 pytest pytest-cov pytest-pep8 diff --git a/setup.py b/setup.py index f3e32f5..bcb6d88 100755 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ REQUIRES = [ 'pandas >= 0.20.3', + 'humanfriendly >= 4.6', ] if VER < '3.4': diff --git a/tests/test_main.py b/tests/test_main.py index bf85dc5..7ab9504 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -8,6 +8,7 @@ from pytest import fixture, mark, raises from csft import __main__ as main +from csft._csft import column @fixture @@ -16,15 +17,22 @@ def null(): yield fobj +@fixture +def data(): + return DataFrame({ + column.TYPE: ('a', 'b', 'c'), + column.SIZE: (3, 2, 1), + }) + + def test_call(null): check_call(['python', '-m', 'csft', 'csft'], stdout=null, stderr=null) -@mark.parametrize('argv', [None, [], ['csft'], ]) -def test_main(argv, mocker, capsys): - expect = 'TEST_PRINT' +@mark.parametrize('argv', [None, [], [''], ['csft'], ]) +def test_main(argv, data, mocker, capsys): mocker.patch('sys.argv', ['csft']) - csft2data = mocker.patch('csft.__main__.csft2data', return_value=expect) + csft2data = mocker.patch('csft.__main__.csft2data', return_value=data) assert 0 == main.main(argv=argv) @@ -33,13 +41,18 @@ def test_main(argv, mocker, capsys): else: csft2data.assert_called_once_with(main._dir(curdir)) - assert expect == capsys.readouterr()[0].strip() + out, err = capsys.readouterr() + assert out + assert not err -def test_wrong_path(capsys): +@mark.parametrize('path', ['path/not/exist', 'path/not/valid', 'LICENSE']) +def test_wrong_path(path, capsys): with raises(SystemExit): - main.main(argv=['path/is/not/a/directory']) - assert capsys.readouterr() + main.main(argv=[path]) + out, err = capsys.readouterr() + assert not out + assert err def test_show_version(capsys): @@ -50,23 +63,44 @@ def test_show_version(capsys): from csft import __version__ if sys.version < '3.0': - out = capsys.readouterr()[1] + _, out = capsys.readouterr() else: - out = capsys.readouterr()[0] + out, _ = capsys.readouterr() assert __version__ == out.strip() -def test_arg_top(mocker, capsys): - expect = 'TEST_PRINT' - obj = DataFrame() - mocker.patch.object(obj, 'head', return_value=expect) - csft2data = mocker.patch('csft.__main__.csft2data', return_value=obj) +def test_arg_top(data, mocker, capsys): + csft2data = mocker.patch('csft.__main__.csft2data', return_value=data) + head = mocker.patch.object(data, 'head', return_value=data) assert 0 == main.main(argv=[curdir, '--top', '5']) + csft2data.assert_called_once_with(main._dir(curdir)) - assert expect == capsys.readouterr()[0].strip() + head.assert_called_once() + out, err = capsys.readouterr() + assert out + assert not err + + +@mark.parametrize('top', ['not-a-number', '-1']) +def test_arg_top_err(top, mocker, capsys): + csft2data = mocker.patch('csft.__main__.csft2data') with raises(SystemExit): - main.main(argv=[curdir, '--top', 'not-a-number']) - with raises(SystemExit): - main.main(argv=[curdir, '--top', '-1']) + main.main(argv=[curdir, '--top', top]) + + csft2data.assert_not_called() + out, err = capsys.readouterr() + assert not out + assert err + + +def test_arg_with_raw(data, mocker, capsys): + csft2data = mocker.patch('csft.__main__.csft2data', return_value=data) + + assert 0 == main.main(argv=[curdir, '--with-raw']) + + csft2data.assert_called_once_with(main._dir(curdir)) + out, err = capsys.readouterr() + assert out + assert not err