From ca7e960f617537f93d5bf0b2523833f0810a729e Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Mon, 1 May 2017 17:31:03 +0100 Subject: [PATCH 1/3] add numeric support for unit_scale - fixes #273 - fixes #295 --- README.rst | 7 ++++--- tqdm/_tqdm.py | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index de5fc5357..5c41ffe8a 100644 --- a/README.rst +++ b/README.rst @@ -309,11 +309,12 @@ Parameters * unit : str, optional String that will be used to define the unit of each iteration [default: it]. -* unit_scale : bool, optional - If set, the number of iterations will be reduced/scaled +* unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled automatically and a metric prefix following the International System of Units standard will be added - (kilo, mega, etc.) [default: False]. + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale `total` and `n`. * dynamic_ncols : bool, optional If set, constantly alters ``ncols`` to the environment (allowing for window resizes) [default: False]. diff --git a/tqdm/_tqdm.py b/tqdm/_tqdm.py index 8bef10c46..6d239c541 100644 --- a/tqdm/_tqdm.py +++ b/tqdm/_tqdm.py @@ -229,10 +229,11 @@ def format_meter(n, total, elapsed, ncols=None, prefix='', (1-9 #). unit : str, optional The iteration unit [default: 'it']. - unit_scale : bool, optional - If set, the number of iterations will printed with an + unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be printed with an appropriate SI metric prefix (K = 10^3, M = 10^6, etc.) - [default: False]. + [default: False]. If any other non-zero number, will scale + `total` and `n`. rate : float, optional Manual override for iteration rate. If [default: None], uses n/elapsed. @@ -257,6 +258,12 @@ def format_meter(n, total, elapsed, ncols=None, prefix='', if total and n > total: total = None + # apply custom scale if necessary + if unit_scale and unit_scale not in (True, 1): + total *= unit_scale + n *= unit_scale + unit_scale = False + format_interval = tqdm.format_interval elapsed_str = format_interval(elapsed) @@ -613,11 +620,12 @@ def __init__(self, iterable=None, desc=None, total=None, leave=True, unit : str, optional String that will be used to define the unit of each iteration [default: it]. - unit_scale : bool, optional - If set, the number of iterations will be reduced/scaled + unit_scale : bool or int or float, optional + If 1 or True, the number of iterations will be reduced/scaled automatically and a metric prefix following the International System of Units standard will be added - (kilo, mega, etc.) [default: False]. + (kilo, mega, etc.) [default: False]. If any other non-zero + number, will scale `total` and `n`. dynamic_ncols : bool, optional If set, constantly alters `ncols` to the environment (allowing for window resizes) [default: False]. From 8dc997b2a92f18985050ddca7562cf31853e016d Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sat, 22 Jul 2017 11:46:27 +0100 Subject: [PATCH 2/3] clean comments --- tqdm/tests/tests_main.py | 2 +- tqdm/tests/tests_pandas.py | 10 +++--- tqdm/tests/tests_perf.py | 20 +++++------ tqdm/tests/tests_tqdm.py | 72 ++++++++++++++++++------------------- tqdm/tests/tests_version.py | 2 +- 5 files changed, 53 insertions(+), 53 deletions(-) diff --git a/tqdm/tests/tests_main.py b/tqdm/tests/tests_main.py index 80d87ff7e..b9871663d 100644 --- a/tqdm/tests/tests_main.py +++ b/tqdm/tests/tests_main.py @@ -13,7 +13,7 @@ def _sh(*cmd, **kwargs): # WARNING: this should be the last test as it messes with sys.stdin, argv @with_setup(pretest, posttest) def test_main(): - """ Test command line pipes """ + """Test command line pipes""" ls_out = _sh('ls').replace('\r\n', '\n') ls = subprocess.Popen(('ls'), stdout=subprocess.PIPE, diff --git a/tqdm/tests/tests_pandas.py b/tqdm/tests/tests_pandas.py index 5123c1dac..bfa110e21 100644 --- a/tqdm/tests/tests_pandas.py +++ b/tqdm/tests/tests_pandas.py @@ -6,7 +6,7 @@ @with_setup(pretest, posttest) def test_pandas_groupby_apply(): - """ Test pandas.DataFrame.groupby(...).progress_apply """ + """Test pandas.DataFrame.groupby(...).progress_apply""" try: from numpy.random import randint import pandas as pd @@ -36,7 +36,7 @@ def test_pandas_groupby_apply(): @with_setup(pretest, posttest) def test_pandas_apply(): - """ Test pandas.DataFrame[.series].progress_apply """ + """Test pandas.DataFrame[.series].progress_apply""" try: from numpy.random import randint import pandas as pd @@ -62,7 +62,7 @@ def test_pandas_apply(): @with_setup(pretest, posttest) def test_pandas_map(): - """ Test pandas.Series.progress_map """ + """Test pandas.Series.progress_map""" try: from numpy.random import randint import pandas as pd @@ -82,7 +82,7 @@ def test_pandas_map(): @with_setup(pretest, posttest) def test_pandas_leave(): - """ Test pandas with `leave=True` """ + """Test pandas with `leave=True`""" try: from numpy.random import randint import pandas as pd @@ -105,7 +105,7 @@ def test_pandas_leave(): @with_setup(pretest, posttest) def test_pandas_deprecation(): - """ Test bar object instance as argument deprecation """ + """Test bar object instance as argument deprecation""" try: from numpy.random import randint from tqdm import tqdm_pandas diff --git a/tqdm/tests/tests_perf.py b/tqdm/tests/tests_perf.py index ce48dff89..3529b7ddb 100644 --- a/tqdm/tests/tests_perf.py +++ b/tqdm/tests/tests_perf.py @@ -25,14 +25,14 @@ def get_relative_time(prevtime=0): def cpu_sleep(t): - '''Sleep the given amount of cpu time''' + """Sleep the given amount of cpu time""" start = process_time() while((process_time() - start) < t): pass def checkCpuTime(sleeptime=0.2): - '''Check if cpu time works correctly''' + """Check if cpu time works correctly""" if checkCpuTime.passed: return True # First test that sleeping does not consume cputime @@ -85,14 +85,14 @@ def test_inner(): class MockIO(StringIO): - """ Wraps StringIO to mock a file with no I/O """ + """Wraps StringIO to mock a file with no I/O""" def write(self, data): return def simple_progress(iterable=None, total=None, file=sys.stdout, desc='', leave=False, miniters=1, mininterval=0.1, width=60): - """ Simple progress bar reproducing tqdm's major features """ + """Simple progress bar reproducing tqdm's major features""" n = [0] # use a closure start_t = [time()] last_n = [0] @@ -160,7 +160,7 @@ def update_and_yield(): @with_setup(pretest, posttest) @retry_on_except() def test_iter_overhead(): - """ Test overhead of iteration based tqdm """ + """Test overhead of iteration based tqdm""" total = int(1e6) @@ -186,7 +186,7 @@ def test_iter_overhead(): @with_setup(pretest, posttest) @retry_on_except() def test_manual_overhead(): - """ Test overhead of manual tqdm """ + """Test overhead of manual tqdm""" total = int(1e6) @@ -213,7 +213,7 @@ def test_manual_overhead(): @with_setup(pretest, posttest) @retry_on_except() def test_iter_overhead_hard(): - """ Test overhead of iteration based tqdm (hard) """ + """Test overhead of iteration based tqdm (hard)""" total = int(1e5) @@ -242,7 +242,7 @@ def test_iter_overhead_hard(): @with_setup(pretest, posttest) @retry_on_except() def test_manual_overhead_hard(): - """ Test overhead of manual tqdm (hard) """ + """Test overhead of manual tqdm (hard)""" total = int(1e5) @@ -272,7 +272,7 @@ def test_manual_overhead_hard(): @with_setup(pretest, posttest) @retry_on_except() def test_iter_overhead_simplebar_hard(): - """ Test overhead of iteration based tqdm vs simple progress bar (hard) """ + """Test overhead of iteration based tqdm vs simple progress bar (hard)""" total = int(1e4) @@ -302,7 +302,7 @@ def test_iter_overhead_simplebar_hard(): @with_setup(pretest, posttest) @retry_on_except() def test_manual_overhead_simplebar_hard(): - """ Test overhead of manual tqdm vs simple progress bar (hard) """ + """Test overhead of manual tqdm vs simple progress bar (hard)""" total = int(1e4) diff --git a/tqdm/tests/tests_tqdm.py b/tqdm/tests/tests_tqdm.py index f5380063f..1a32388ed 100644 --- a/tqdm/tests/tests_tqdm.py +++ b/tqdm/tests/tests_tqdm.py @@ -157,7 +157,7 @@ def getvalue(self): def get_bar(all_bars, i): - """ Get a specific update from a whole bar traceback """ + """Get a specific update from a whole bar traceback""" # Split according to any used control characters bars_split = RE_ctrlchr_excl.split(all_bars) bars_split = list(filter(None, bars_split)) # filter out empty splits @@ -169,7 +169,7 @@ def progressbar_rate(bar_str): def squash_ctrlchars(s): - """ Apply control characters in a string just like a terminal display """ + """Apply control characters in a string just like a terminal display""" # List of supported control codes ctrlcodes = [r'\r', r'\n', r'\x1b\[A'] @@ -214,7 +214,7 @@ def squash_ctrlchars(s): def test_format_interval(): - """ Test time interval format """ + """Test time interval format""" format_interval = tqdm.format_interval assert format_interval(60) == '01:00' @@ -223,7 +223,7 @@ def test_format_interval(): def test_format_meter(): - """ Test statistics and progress bar formatting """ + """Test statistics and progress bar formatting""" try: unich = unichr except NameError: @@ -267,7 +267,7 @@ def test_format_meter(): def test_si_format(): - """ Test SI unit prefixes """ + """Test SI unit prefixes""" format_meter = tqdm.format_meter assert '9.00 ' in format_meter(1, 9, 1, unit_scale=True, unit='B') @@ -296,7 +296,7 @@ def test_si_format(): @with_setup(pretest, posttest) def test_all_defaults(): - """ Test default kwargs """ + """Test default kwargs""" with closing(UnicodeIO()) as our_file: with tqdm(range(10), file=our_file) as progressbar: assert len(progressbar) == 10 @@ -312,7 +312,7 @@ def test_all_defaults(): @with_setup(pretest, posttest) def test_iterate_over_csv_rows(): - """ Test csv iterator """ + """Test csv iterator""" # Create a test csv pseudo file with closing(StringIO()) as test_csv_file: writer = csv.writer(test_csv_file) @@ -330,7 +330,7 @@ def test_iterate_over_csv_rows(): @with_setup(pretest, posttest) def test_file_output(): - """ Test output to arbitrary file-like objects """ + """Test output to arbitrary file-like objects""" with closing(StringIO()) as our_file: for i in tqdm(_range(3), file=our_file): if i == 1: @@ -340,7 +340,7 @@ def test_file_output(): @with_setup(pretest, posttest) def test_leave_option(): - """ Test `leave=True` always prints info about the last iteration """ + """Test `leave=True` always prints info about the last iteration""" with closing(StringIO()) as our_file: for _ in tqdm(_range(3), file=our_file, leave=True): pass @@ -358,7 +358,7 @@ def test_leave_option(): @with_setup(pretest, posttest) def test_trange(): - """ Test trange """ + """Test trange""" with closing(StringIO()) as our_file: for _ in trange(3, file=our_file, leave=True): pass @@ -374,7 +374,7 @@ def test_trange(): @with_setup(pretest, posttest) def test_min_interval(): - """ Test mininterval """ + """Test mininterval""" with closing(StringIO()) as our_file: for _ in tqdm(_range(3), file=our_file, mininterval=1e-10): pass @@ -384,7 +384,7 @@ def test_min_interval(): @with_setup(pretest, posttest) def test_max_interval(): - """ Test maxinterval """ + """Test maxinterval""" total = 100 bigstep = 10 smallstep = 5 @@ -520,7 +520,7 @@ def test_max_interval(): @with_setup(pretest, posttest) def test_min_iters(): - """ Test miniters """ + """Test miniters""" with closing(StringIO()) as our_file: for _ in tqdm(_range(3), file=our_file, leave=True, miniters=4): our_file.write('blank\n') @@ -537,7 +537,7 @@ def test_min_iters(): @with_setup(pretest, posttest) def test_dynamic_min_iters(): - """ Test purely dynamic miniters (and manual updates and __del__) """ + """Test purely dynamic miniters (and manual updates and __del__)""" with closing(StringIO()) as our_file: total = 10 t = tqdm(total=total, file=our_file, miniters=None, mininterval=0, @@ -606,7 +606,7 @@ def test_dynamic_min_iters(): @with_setup(pretest, posttest) def test_big_min_interval(): - """ Test large mininterval """ + """Test large mininterval""" with closing(StringIO()) as our_file: for _ in tqdm(_range(2), file=our_file, mininterval=1E10): pass @@ -623,7 +623,7 @@ def test_big_min_interval(): @with_setup(pretest, posttest) def test_smoothed_dynamic_min_iters(): - """ Test smoothed dynamic miniters """ + """Test smoothed dynamic miniters""" timer = DiscreteTimer() with closing(StringIO()) as our_file: @@ -654,7 +654,7 @@ def test_smoothed_dynamic_min_iters(): @with_setup(pretest, posttest) def test_smoothed_dynamic_min_iters_with_min_interval(): - """ Test smoothed dynamic miniters with mininterval """ + """Test smoothed dynamic miniters with mininterval""" timer = DiscreteTimer() # In this test, `miniters` should gradually decline @@ -699,7 +699,7 @@ def test_smoothed_dynamic_min_iters_with_min_interval(): @with_setup(pretest, posttest) def test_disable(): - """ Test disable """ + """Test disable""" with closing(StringIO()) as our_file: for _ in tqdm(_range(3), file=our_file, disable=True): pass @@ -716,7 +716,7 @@ def test_disable(): @with_setup(pretest, posttest) def test_unit(): - """ Test SI unit prefix """ + """Test SI unit prefix""" with closing(StringIO()) as our_file: for _ in tqdm(_range(3), file=our_file, miniters=1, unit="bytes"): pass @@ -726,7 +726,7 @@ def test_unit(): @with_setup(pretest, posttest) def test_ascii(): - """ Test ascii/unicode bar """ + """Test ascii/unicode bar""" # Test ascii autodetection with closing(StringIO()) as our_file: with tqdm(total=10, file=our_file, ascii=None) as t: @@ -757,7 +757,7 @@ def test_ascii(): @with_setup(pretest, posttest) def test_update(): - """ Test manual creation and updates """ + """Test manual creation and updates""" with closing(StringIO()) as our_file: with tqdm(total=2, file=our_file, miniters=1, mininterval=0) \ as progressbar: @@ -783,7 +783,7 @@ def test_update(): @with_setup(pretest, posttest) def test_close(): - """ Test manual creation and closure and n_instances """ + """Test manual creation and closure and n_instances""" # With `leave` option with closing(StringIO()) as our_file: @@ -831,7 +831,7 @@ def test_close(): @with_setup(pretest, posttest) def test_smoothing(): - """ Test exponential weighted average smoothing """ + """Test exponential weighted average smoothing""" timer = DiscreteTimer() # -- Test disabling smoothing @@ -930,7 +930,7 @@ def test_smoothing(): @with_setup(pretest, posttest) def test_deprecated_nested(): - """ Test nested progress bars """ + """Test nested progress bars""" if nt_and_no_colorama: raise SkipTest # TODO: test degradation on windows without colorama? @@ -950,7 +950,7 @@ def test_deprecated_nested(): @with_setup(pretest, posttest) def test_bar_format(): - """ Test custom bar formatting """ + """Test custom bar formatting""" with closing(StringIO()) as our_file: bar_format = r'{l_bar}{bar}|{n_fmt}/{total_fmt}-{n}/{total}{percentage}{rate}{rate_fmt}{elapsed}{remaining}' # NOQA for _ in trange(2, file=our_file, leave=True, bar_format=bar_format): @@ -967,7 +967,7 @@ def test_bar_format(): @with_setup(pretest, posttest) def test_unpause(): - """ Test unpause """ + """Test unpause""" timer = DiscreteTimer() with closing(StringIO()) as our_file: t = trange(10, file=our_file, leave=True, mininterval=0) @@ -990,7 +990,7 @@ def test_unpause(): @with_setup(pretest, posttest) def test_position(): - """ Test positioned progress bars """ + """Test positioned progress bars""" if nt_and_no_colorama: raise SkipTest @@ -1123,7 +1123,7 @@ def test_position(): @with_setup(pretest, posttest) def test_set_description(): - """ Test set description """ + """Test set description""" with closing(StringIO()) as our_file: with tqdm(desc='Hello', file=our_file) as t: assert t.desc == 'Hello: ' @@ -1135,7 +1135,7 @@ def test_set_description(): @with_setup(pretest, posttest) def test_deprecated_gui(): - """ Test internal GUI properties """ + """Test internal GUI properties""" # Check: StatusPrinter iff gui is disabled with closing(StringIO()) as our_file: t = tqdm(total=2, gui=True, file=our_file, miniters=1, mininterval=0) @@ -1177,7 +1177,7 @@ def test_deprecated_gui(): @with_setup(pretest, posttest) def test_cmp(): - """ Test comparison functions """ + """Test comparison functions""" with closing(StringIO()) as our_file: t0 = tqdm(total=10, file=our_file) t1 = tqdm(total=10, file=our_file) @@ -1206,7 +1206,7 @@ def test_cmp(): @with_setup(pretest, posttest) def test_repr(): - """ Test representation """ + """Test representation""" with closing(StringIO()) as our_file: with tqdm(total=10, ascii=True, file=our_file) as t: assert str(t) == ' 0%| | 0/10 [00:00 Date: Sat, 22 Jul 2017 11:54:32 +0100 Subject: [PATCH 3/3] unit test, coverage --- tqdm/tests/tests_tqdm.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tqdm/tests/tests_tqdm.py b/tqdm/tests/tests_tqdm.py index 1a32388ed..997a2e09d 100644 --- a/tqdm/tests/tests_tqdm.py +++ b/tqdm/tests/tests_tqdm.py @@ -1599,3 +1599,13 @@ def test_file_redirection(): assert res.count("Such fun\n") == 3 assert "0/3" in res assert "3/3" in res + + +@with_setup(pretest, posttest) +def test_unit_scale(): + """Test numeric `unit_scale`""" + with closing(StringIO()) as our_file: + for _ in tqdm(_range(100), unit_scale=9, file=our_file): + pass + out = our_file.getvalue() + assert '900/900' in out