From b7c484c7fcea267146f46d1124d90c447b82712fed329fe09776305ec1f4cfc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20S=C3=BAkup?= Date: Fri, 23 Oct 2020 22:48:31 +0000 Subject: [PATCH] Accepting request 843615 from home:pgajdos:python - added patches https://github.com/tqdm/tqdm/pull/1052 + python-tqdm-remove-nose.patch OBS-URL: https://build.opensuse.org/request/show/843615 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-tqdm?expand=0&rev=84 --- python-tqdm-remove-nose.patch | 5840 +++++++++++++++++++++++++++++++++ python-tqdm.changes | 7 + python-tqdm.spec | 14 +- 3 files changed, 5857 insertions(+), 4 deletions(-) create mode 100644 python-tqdm-remove-nose.patch diff --git a/python-tqdm-remove-nose.patch b/python-tqdm-remove-nose.patch new file mode 100644 index 0000000..b507093 --- /dev/null +++ b/python-tqdm-remove-nose.patch @@ -0,0 +1,5840 @@ +diff --git a/pytest.ini b/pytest.ini +new file mode 100644 +index 00000000..e7f77388 +--- /dev/null ++++ b/pytest.ini +@@ -0,0 +1,4 @@ ++[pytest] ++python_files = tests_*.py ++testpaths = tqdm ++addopts = -v +diff --git a/setup.py b/setup.py +index 770e557b..868be3fd 100755 +--- a/setup.py ++++ b/setup.py +@@ -121,6 +121,6 @@ def find_packages(where='.'): + ], + keywords='progressbar progressmeter progress bar meter' + ' rate eta console terminal time', +- test_suite='nose.collector', +- tests_require=['nose', 'flake8', 'coverage'], ++ test_suite='pytest', ++ tests_require=['pytest', 'flake8', 'coverage'], + ) +diff --git a/tox.ini b/tox.ini +index 66565b46..fd3edb87 100644 +--- a/tox.ini ++++ b/tox.ini +@@ -9,11 +9,12 @@ envlist = py{26,27,33,34,35,36,37,38,py,py3}, tf-no-keras, perf, flake8, setup.p + + [coverage] + deps = +- nose ++ pytest ++ pytest-cov + coverage + coveralls + commands = +- nosetests -d -v --with-coverage --cover-package=tqdm --ignore-files="tests_perf\.py" tqdm/ ++ pytest --cov=tqdm -k "not tests_perf" -v tqdm/ + - coveralls + - coverage xml + - curl -OL https://coverage.codacy.com/get.sh +@@ -25,10 +26,9 @@ allowlist_externals = + [extra] + deps = + {[coverage]deps} +- nose-timer + codecov + commands = +- nosetests -d -v --with-coverage --with-timer --cover-package=tqdm --ignore-files="tests_perf\.py" tqdm/ ++ pytest --cov=tqdm -k "not tests_perf" -v tqdm/ + - coveralls + codecov + - coverage xml +@@ -51,9 +51,9 @@ allowlist_externals = {[extra]allowlist_externals} + # no cython/numpy/pandas for py{py,py3,26,33,34} + + [testenv:py26] +-# no codecov and timer for py26 + deps = +- nose ++ pytest ++ pytest-cov + coverage + coveralls==1.2.0 + codecov +@@ -81,14 +81,13 @@ deps = + {[extra]deps} + tensorflow + commands = +- nosetests -d -v --with-timer tqdm/tests/tests_keras.py ++ pytest tqdm/tests/tests_keras.py -v + + [testenv:perf] + deps = +- nose +- nose-timer ++ pytest + commands = +- nosetests -d -v --with-timer tqdm/tests/tests_perf.py ++ pytest tqdm/tests/tests_perf.py -v + + [testenv:flake8] + deps = flake8 +diff --git a/tqdm/tests/py37_asyncio.py b/tqdm/tests/py37_asyncio.py +index 5ac6291b..9e6a24c1 100644 +--- a/tqdm/tests/py37_asyncio.py ++++ b/tqdm/tests/py37_asyncio.py +@@ -2,7 +2,7 @@ + from functools import partial, wraps + from time import time + +-from tests_tqdm import with_setup, pretest, posttest, StringIO, closing ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing + from tqdm.asyncio import tqdm_asyncio, tarange + + tqdm = partial(tqdm_asyncio, miniters=0, mininterval=0) +@@ -10,11 +10,10 @@ + as_completed = partial(tqdm_asyncio.as_completed, miniters=0, mininterval=0) + + +-def with_setup_sync(func): +- @with_setup(pretest, posttest) ++def setup_sync(func): + @wraps(func) +- def inner(): +- return asyncio.run(func()) ++ def inner(self): ++ return asyncio.run(func(self)) + return inner + + +@@ -33,71 +32,68 @@ async def acount(*args, **kwargs): + yield i + + +-@with_setup_sync +-async def test_generators(): +- """Test asyncio generators""" +- with closing(StringIO()) as our_file: +- async for i in tqdm(count(), desc="counter", file=our_file): +- if i >= 8: +- break +- assert '9it' in our_file.getvalue() +- our_file.seek(0) +- our_file.truncate() +- +- async for i in tqdm(acount(), desc="async_counter", file=our_file): +- if i >= 8: +- break +- assert '9it' in our_file.getvalue() +- +- +-@with_setup_sync +-async def test_range(): +- """Test asyncio range""" +- with closing(StringIO()) as our_file: +- async for _ in tqdm(range(9), desc="range", file=our_file): +- pass +- assert '9/9' in our_file.getvalue() +- our_file.seek(0) +- our_file.truncate() +- +- async for _ in trange(9, desc="trange", file=our_file): +- pass +- assert '9/9' in our_file.getvalue() +- +- +-@with_setup_sync +-async def test_nested(): +- """Test asyncio nested""" +- with closing(StringIO()) as our_file: +- async for _ in tqdm(trange(9, desc="inner", file=our_file), +- desc="outer", file=our_file): +- pass +- assert 'inner: 100%' in our_file.getvalue() +- assert 'outer: 100%' in our_file.getvalue() +- +- +-@with_setup_sync +-async def test_coroutines(): +- """Test asyncio coroutine.send""" +- with closing(StringIO()) as our_file: +- with tqdm(count(), file=our_file) as pbar: +- async for i in pbar: +- if i == 9: +- pbar.send(-10) +- elif i < 0: +- assert i == -9 ++class TestTqdmAsyncio(TestWithInstancesCheck): ++ @setup_sync ++ async def test_generators(self): ++ """Test asyncio generators""" ++ with closing(StringIO()) as our_file: ++ async for i in tqdm(count(), desc="counter", file=our_file): ++ if i >= 8: + break +- assert '10it' in our_file.getvalue() +- +- +-@with_setup_sync +-async def test_as_completed(): +- """Test asyncio as_completed""" +- with closing(StringIO()) as our_file: +- t = time() +- skew = time() - t +- for i in as_completed([asyncio.sleep(0.01 * i) +- for i in range(30, 0, -1)], file=our_file): +- await i +- assert 0.29 < time() - t - 2 * skew < 0.31 +- assert '30/30' in our_file.getvalue() ++ assert '9it' in our_file.getvalue() ++ our_file.seek(0) ++ our_file.truncate() ++ ++ async for i in tqdm(acount(), desc="async_counter", file=our_file): ++ if i >= 8: ++ break ++ assert '9it' in our_file.getvalue() ++ ++ @setup_sync ++ async def test_range(self): ++ """Test asyncio range""" ++ with closing(StringIO()) as our_file: ++ async for _ in tqdm(range(9), desc="range", file=our_file): ++ pass ++ assert '9/9' in our_file.getvalue() ++ our_file.seek(0) ++ our_file.truncate() ++ ++ async for _ in trange(9, desc="trange", file=our_file): ++ pass ++ assert '9/9' in our_file.getvalue() ++ ++ @setup_sync ++ async def test_nested(self): ++ """Test asyncio nested""" ++ with closing(StringIO()) as our_file: ++ async for _ in tqdm(trange(9, desc="inner", file=our_file), ++ desc="outer", file=our_file): ++ pass ++ assert 'inner: 100%' in our_file.getvalue() ++ assert 'outer: 100%' in our_file.getvalue() ++ ++ @setup_sync ++ async def test_coroutines(self): ++ """Test asyncio coroutine.send""" ++ with closing(StringIO()) as our_file: ++ with tqdm(count(), file=our_file) as pbar: ++ async for i in pbar: ++ if i == 9: ++ pbar.send(-10) ++ elif i < 0: ++ assert i == -9 ++ break ++ assert '10it' in our_file.getvalue() ++ ++ @setup_sync ++ async def test_as_completed(self): ++ """Test asyncio as_completed""" ++ with closing(StringIO()) as our_file: ++ t = time() ++ skew = time() - t ++ for i in as_completed([asyncio.sleep(0.01 * i) ++ for i in range(30, 0, -1)], file=our_file): ++ await i ++ assert 0.29 < time() - t - 2 * skew < 0.31 ++ assert '30/30' in our_file.getvalue() +diff --git a/tqdm/tests/tests_asyncio.py b/tqdm/tests/tests_asyncio.py +index 11b58ac8..d1f00234 100644 +--- a/tqdm/tests/tests_asyncio.py ++++ b/tqdm/tests/tests_asyncio.py +@@ -3,5 +3,5 @@ + if sys.version_info[:2] > (3, 6): + from py37_asyncio import * # NOQA + else: +- from tests_tqdm import SkipTest ++ from unittest import SkipTest + raise SkipTest +diff --git a/tqdm/tests/tests_concurrent.py b/tqdm/tests/tests_concurrent.py +index e64cb789..ecfea117 100644 +--- a/tqdm/tests/tests_concurrent.py ++++ b/tqdm/tests/tests_concurrent.py +@@ -1,10 +1,10 @@ + """ + Tests for `tqdm.contrib.concurrent`. + """ ++import unittest + from warnings import catch_warnings + from tqdm.contrib.concurrent import thread_map, process_map +-from tests_tqdm import with_setup, pretest, posttest, SkipTest, StringIO, \ +- closing ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing + + + def incr(x): +@@ -12,47 +12,47 @@ def incr(x): + return x + 1 + + +-@with_setup(pretest, posttest) +-def test_thread_map(): +- """Test contrib.concurrent.thread_map""" +- with closing(StringIO()) as our_file: +- a = range(9) +- b = [i + 1 for i in a] ++class TestTqdmConcurrent(TestWithInstancesCheck): ++ ++ def test_thread_map(self): ++ """Test contrib.concurrent.thread_map""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ b = [i + 1 for i in a] ++ try: ++ assert thread_map(lambda x: x + 1, a, file=our_file) == b ++ except ImportError: ++ raise unittest.SkipTest ++ assert thread_map(incr, a, file=our_file) == b ++ ++ def test_process_map(self): ++ """Test contrib.concurrent.process_map""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ b = [i + 1 for i in a] ++ try: ++ assert process_map(incr, a, file=our_file) == b ++ except ImportError: ++ raise unittest.SkipTest ++ ++ ++class TestTqdmConcurrentWithoutInstancesCheck(unittest.TestCase): ++ def test_chunksize_warning(self): ++ """Test contrib.concurrent.process_map chunksize warnings""" + try: +- assert thread_map(lambda x: x + 1, a, file=our_file) == b ++ from unittest.mock import patch + except ImportError: +- raise SkipTest +- assert thread_map(incr, a, file=our_file) == b +- +- +-@with_setup(pretest, posttest) +-def test_process_map(): +- """Test contrib.concurrent.process_map""" +- with closing(StringIO()) as our_file: +- a = range(9) +- b = [i + 1 for i in a] +- try: +- assert process_map(incr, a, file=our_file) == b +- except ImportError: +- raise SkipTest +- +- +-def test_chunksize_warning(): +- """Test contrib.concurrent.process_map chunksize warnings""" +- try: +- from unittest.mock import patch +- except ImportError: +- raise SkipTest +- +- for iterables, should_warn in [ +- ([], False), +- (['x'], False), +- ([()], False), +- (['x', ()], False), +- (['x' * 1001], True), +- (['x' * 100, ('x',) * 1001], True), +- ]: +- with patch('tqdm.contrib.concurrent._executor_map'): +- with catch_warnings(record=True) as w: +- process_map(incr, *iterables) +- assert should_warn == bool(w) ++ raise unittest.SkipTest ++ ++ for iterables, should_warn in [ ++ ([], False), ++ (['x'], False), ++ ([()], False), ++ (['x', ()], False), ++ (['x' * 1001], True), ++ (['x' * 100, ('x',) * 1001], True), ++ ]: ++ with patch('tqdm.contrib.concurrent._executor_map'): ++ with catch_warnings(record=True) as w: ++ process_map(incr, *iterables) ++ assert should_warn == bool(w) +diff --git a/tqdm/tests/tests_contrib.py b/tqdm/tests/tests_contrib.py +index 5239d779..38e1779e 100644 +--- a/tqdm/tests/tests_contrib.py ++++ b/tqdm/tests/tests_contrib.py +@@ -2,66 +2,61 @@ + Tests for `tqdm.contrib`. + """ + import sys ++import unittest + from tqdm.contrib import tenumerate, tzip, tmap +-from tests_tqdm import with_setup, pretest, posttest, SkipTest, StringIO, \ +- closing +- +- +-def incr(x): +- """Dummy function""" +- return x + 1 +- +- +-@with_setup(pretest, posttest) +-def test_enumerate(): +- """Test contrib.tenumerate""" +- with closing(StringIO()) as our_file: +- a = range(9) +- assert list(tenumerate(a, file=our_file)) == list(enumerate(a)) +- assert list(tenumerate(a, 42, file=our_file)) == list(enumerate(a, 42)) +- with closing(StringIO()) as our_file: +- _ = list(tenumerate((i for i in a), file=our_file)) +- assert "100%" not in our_file.getvalue() +- with closing(StringIO()) as our_file: +- _ = list(tenumerate((i for i in a), file=our_file, total=len(a))) +- assert "100%" in our_file.getvalue() +- +- +-@with_setup(pretest, posttest) +-def test_enumerate_numpy(): +- """Test contrib.tenumerate(numpy.ndarray)""" +- try: +- import numpy as np +- except ImportError: +- raise SkipTest +- with closing(StringIO()) as our_file: +- a = np.random.random((42, 1337)) +- assert list(tenumerate(a, file=our_file)) == list(np.ndenumerate(a)) +- +- +-@with_setup(pretest, posttest) +-def test_zip(): +- """Test contrib.tzip""" +- with closing(StringIO()) as our_file: +- a = range(9) +- b = [i + 1 for i in a] +- if sys.version_info[:1] < (3,): +- assert tzip(a, b, file=our_file) == zip(a, b) +- else: +- gen = tzip(a, b, file=our_file) +- assert gen != list(zip(a, b)) +- assert list(gen) == list(zip(a, b)) +- +- +-@with_setup(pretest, posttest) +-def test_map(): +- """Test contrib.tmap""" +- with closing(StringIO()) as our_file: +- a = range(9) +- b = [i + 1 for i in a] +- if sys.version_info[:1] < (3,): +- assert tmap(lambda x: x + 1, a, file=our_file) == map(incr, a) +- else: +- gen = tmap(lambda x: x + 1, a, file=our_file) +- assert gen != b +- assert list(gen) == b ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing ++ ++ ++class TestTqdmContrib(TestWithInstancesCheck): ++ def incr(self, x): ++ """Dummy function""" ++ return x + 1 ++ ++ def test_enumerate(self): ++ """Test contrib.tenumerate""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ assert list(tenumerate(a, file=our_file)) == list(enumerate(a)) ++ assert list(tenumerate(a, 42, file=our_file)) == \ ++ list(enumerate(a, 42)) ++ with closing(StringIO()) as our_file: ++ _ = list(tenumerate((i for i in a), file=our_file)) ++ assert "100%" not in our_file.getvalue() ++ with closing(StringIO()) as our_file: ++ _ = list(tenumerate((i for i in a), file=our_file, total=len(a))) ++ assert "100%" in our_file.getvalue() ++ ++ def test_enumerate_numpy(self): ++ """Test contrib.tenumerate(numpy.ndarray)""" ++ try: ++ import numpy as np ++ except ImportError: ++ raise unittest.SkipTest ++ with closing(StringIO()) as our_file: ++ a = np.random.random((42, 1337)) ++ assert list(tenumerate(a, file=our_file)) == list(np.ndenumerate(a)) ++ ++ def test_zip(self): ++ """Test contrib.tzip""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ b = [i + 1 for i in a] ++ if sys.version_info[:1] < (3,): ++ assert tzip(a, b, file=our_file) == zip(a, b) ++ else: ++ gen = tzip(a, b, file=our_file) ++ assert gen != list(zip(a, b)) ++ assert list(gen) == list(zip(a, b)) ++ ++ def test_map(self): ++ """Test contrib.tmap""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ b = [i + 1 for i in a] ++ if sys.version_info[:1] < (3,): ++ assert tmap(lambda x: x + 1, a, file=our_file) == \ ++ map(self.incr, a) ++ else: ++ gen = tmap(lambda x: x + 1, a, file=our_file) ++ assert gen != b ++ assert list(gen) == b +diff --git a/tqdm/tests/tests_itertools.py b/tqdm/tests/tests_itertools.py +index c55e07db..6b78e204 100644 +--- a/tqdm/tests/tests_itertools.py ++++ b/tqdm/tests/tests_itertools.py +@@ -2,7 +2,7 @@ + Tests for `tqdm.contrib.itertools`. + """ + from tqdm.contrib.itertools import product +-from tests_tqdm import with_setup, pretest, posttest, StringIO, closing ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing + import itertools + + +@@ -15,13 +15,13 @@ def __iter__(self): + yield i + + +-@with_setup(pretest, posttest) +-def test_product(): +- """Test contrib.itertools.product""" +- with closing(StringIO()) as our_file: +- a = range(9) +- assert list(product(a, a[::-1], file=our_file)) == \ +- list(itertools.product(a, a[::-1])) ++class TestTqdmItertools(TestWithInstancesCheck): ++ def test_product(self): ++ """Test contrib.itertools.product""" ++ with closing(StringIO()) as our_file: ++ a = range(9) ++ assert list(product(a, a[::-1], file=our_file)) == \ ++ list(itertools.product(a, a[::-1])) + +- assert list(product(a, NoLenIter(a), file=our_file)) == \ +- list(itertools.product(a, NoLenIter(a))) ++ assert list(product(a, NoLenIter(a), file=our_file)) == \ ++ list(itertools.product(a, NoLenIter(a))) +diff --git a/tqdm/tests/tests_keras.py b/tqdm/tests/tests_keras.py +index 11684c49..39ee0fc3 100644 +--- a/tqdm/tests/tests_keras.py ++++ b/tqdm/tests/tests_keras.py +@@ -1,97 +1,97 @@ + from __future__ import division ++import unittest + from tqdm import tqdm +-from tests_tqdm import with_setup, pretest, posttest, SkipTest, StringIO, \ +- closing ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing + + +-@with_setup(pretest, posttest) +-def test_keras(): +- """Test tqdm.keras.TqdmCallback""" +- try: +- from tqdm.keras import TqdmCallback +- import numpy as np ++class TestTqdmKeras(TestWithInstancesCheck): ++ def test_keras(self): ++ """Test tqdm.keras.TqdmCallback""" + try: +- import keras as K ++ from tqdm.keras import TqdmCallback ++ import numpy as np ++ try: ++ import keras as K ++ except ImportError: ++ from tensorflow import keras as K + except ImportError: +- from tensorflow import keras as K +- except ImportError: +- raise SkipTest ++ raise unittest.SkipTest + +- # 1D autoencoder +- dtype = np.float32 +- model = K.models.Sequential( +- [K.layers.InputLayer((1, 1), dtype=dtype), K.layers.Conv1D(1, 1)] +- ) +- model.compile("adam", "mse") +- x = np.random.rand(100, 1, 1).astype(dtype) +- batch_size = 10 +- batches = len(x) / batch_size +- epochs = 5 ++ # 1D autoencoder ++ dtype = np.float32 ++ model = K.models.Sequential( ++ [K.layers.InputLayer((1, 1), dtype=dtype), K.layers.Conv1D(1, 1)] ++ ) ++ model.compile("adam", "mse") ++ x = np.random.rand(100, 1, 1).astype(dtype) ++ batch_size = 10 ++ batches = len(x) / batch_size ++ epochs = 5 + +- with closing(StringIO()) as our_file: ++ with closing(StringIO()) as our_file: + +- class Tqdm(tqdm): +- """redirected I/O class""" ++ class Tqdm(tqdm): ++ """redirected I/O class""" + +- def __init__(self, *a, **k): +- k.setdefault("file", our_file) +- super(Tqdm, self).__init__(*a, **k) ++ def __init__(self, *a, **k): ++ k.setdefault("file", our_file) ++ super(Tqdm, self).__init__(*a, **k) + +- # just epoch (no batch) progress +- model.fit( +- x, +- x, +- epochs=epochs, +- batch_size=batch_size, +- verbose=False, +- callbacks=[ +- TqdmCallback( +- epochs, +- data_size=len(x), +- batch_size=batch_size, +- verbose=0, +- tqdm_class=Tqdm, +- ) +- ], +- ) +- res = our_file.getvalue() +- assert "{epochs}/{epochs}".format(epochs=epochs) in res +- assert "{batches}/{batches}".format(batches=batches) not in res ++ # just epoch (no batch) progress ++ model.fit( ++ x, ++ x, ++ epochs=epochs, ++ batch_size=batch_size, ++ verbose=False, ++ callbacks=[ ++ TqdmCallback( ++ epochs, ++ data_size=len(x), ++ batch_size=batch_size, ++ verbose=0, ++ tqdm_class=Tqdm, ++ ) ++ ], ++ ) ++ res = our_file.getvalue() ++ assert "{epochs}/{epochs}".format(epochs=epochs) in res ++ assert "{batches}/{batches}".format(batches=batches) not in res + +- # full (epoch and batch) progress +- our_file.seek(0) +- our_file.truncate() +- model.fit( +- x, +- x, +- epochs=epochs, +- batch_size=batch_size, +- verbose=False, +- callbacks=[ +- TqdmCallback( +- epochs, +- data_size=len(x), +- batch_size=batch_size, +- verbose=2, +- tqdm_class=Tqdm, +- ) +- ], +- ) +- res = our_file.getvalue() +- assert "{epochs}/{epochs}".format(epochs=epochs) in res +- assert "{batches}/{batches}".format(batches=batches) in res ++ # full (epoch and batch) progress ++ our_file.seek(0) ++ our_file.truncate() ++ model.fit( ++ x, ++ x, ++ epochs=epochs, ++ batch_size=batch_size, ++ verbose=False, ++ callbacks=[ ++ TqdmCallback( ++ epochs, ++ data_size=len(x), ++ batch_size=batch_size, ++ verbose=2, ++ tqdm_class=Tqdm, ++ ) ++ ], ++ ) ++ res = our_file.getvalue() ++ assert "{epochs}/{epochs}".format(epochs=epochs) in res ++ assert "{batches}/{batches}".format(batches=batches) in res + +- # auto-detect epochs and batches +- our_file.seek(0) +- our_file.truncate() +- model.fit( +- x, +- x, +- epochs=epochs, +- batch_size=batch_size, +- verbose=False, +- callbacks=[TqdmCallback(verbose=2, tqdm_class=Tqdm)], +- ) +- res = our_file.getvalue() +- assert "{epochs}/{epochs}".format(epochs=epochs) in res +- assert "{batches}/{batches}".format(batches=batches) in res ++ # auto-detect epochs and batches ++ our_file.seek(0) ++ our_file.truncate() ++ model.fit( ++ x, ++ x, ++ epochs=epochs, ++ batch_size=batch_size, ++ verbose=False, ++ callbacks=[TqdmCallback(verbose=2, tqdm_class=Tqdm)], ++ ) ++ res = our_file.getvalue() ++ assert "{epochs}/{epochs}".format(epochs=epochs) in res ++ assert "{batches}/{batches}".format(batches=batches) in res +diff --git a/tqdm/tests/tests_main.py b/tqdm/tests/tests_main.py +index 08eccc05..1cce8d1f 100644 +--- a/tqdm/tests/tests_main.py ++++ b/tqdm/tests/tests_main.py +@@ -1,5 +1,6 @@ + import sys + import subprocess ++import unittest + from os import path + from shutil import rmtree + from tempfile import mkdtemp +@@ -7,8 +8,8 @@ + from tqdm.utils import IS_WIN + from io import open as io_open + +-from tests_tqdm import with_setup, pretest, posttest, _range, closing, \ +- UnicodeIO, StringIO, SkipTest ++from tests_tqdm import TestWithInstancesCheck, _range, closing,\ ++ UnicodeIO, StringIO + + + def _sh(*cmd, **kwargs): +@@ -27,256 +28,256 @@ def __getattr__(self, _): + NULL = Null() + + +-@with_setup(pretest, posttest) +-def test_pipes(): +- """Test command line pipes""" +- ls_out = _sh('ls').replace('\r\n', '\n') +- ls = subprocess.Popen('ls', stdout=subprocess.PIPE, +- stderr=subprocess.STDOUT) +- res = _sh(sys.executable, '-c', 'from tqdm.cli import main; main()', +- stdin=ls.stdout, stderr=subprocess.STDOUT) +- ls.wait() +- +- # actual test: +- +- assert ls_out in res.replace('\r\n', '\n') +- +- +-# WARNING: this should be the last test as it messes with sys.stdin, argv +-@with_setup(pretest, posttest) +-def test_main(): +- """Test misc CLI options""" +- _SYS = sys.stdin, sys.argv +- N = 123 +- +- # test direct import +- sys.stdin = map(str, _range(N)) +- sys.argv = ['', '--desc', 'Test CLI import', +- '--ascii', 'True', '--unit_scale', 'True'] +- import tqdm.__main__ # NOQA +- sys.stderr.write("Test misc CLI options ... ") +- +- # test --delim +- IN_DATA = '\0'.join(map(str, _range(N))) +- with closing(StringIO()) as sys.stdin: +- sys.argv = ['', '--desc', 'Test CLI delim', +- '--ascii', 'True', '--delim', r'\0', '--buf_size', '64'] +- sys.stdin.write(IN_DATA) +- # sys.stdin.write(b'\xff') # TODO +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(fp=fp) +- assert str(N) + "it" in fp.getvalue() +- +- # test --bytes +- IN_DATA = IN_DATA.replace('\0', '\n') +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- sys.stdin.seek(0) +- sys.argv = ['', '--ascii', '--bytes=True', '--unit_scale', 'False'] +- with closing(UnicodeIO()) as fp: +- main(fp=fp) +- assert str(len(IN_DATA)) in fp.getvalue() +- +- # test --log +- sys.stdin = map(str, _range(N)) +- # with closing(UnicodeIO()) as fp: +- main(argv=['--log', 'DEBUG'], fp=NULL) +- # assert "DEBUG:" in sys.stdout.getvalue() +- +- # test --tee +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--mininterval', '0', '--miniters', '1'], fp=fp) +- res = len(fp.getvalue()) +- # assert len(fp.getvalue()) < len(sys.stdout.getvalue()) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--tee', '--mininterval', '0', '--miniters', '1'], fp=fp) +- # spaces to clear intermediate lines could increase length +- assert len(fp.getvalue()) >= res + len(IN_DATA) +- +- # test --null +- _STDOUT = sys.stdout +- try: +- with closing(StringIO()) as sys.stdout: +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--null'], fp=fp) +- assert not sys.stdout.getvalue() +- +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=[], fp=fp) +- assert sys.stdout.getvalue() +- except: +- sys.stdout = _STDOUT +- raise +- else: +- sys.stdout = _STDOUT +- +- # test integer --update +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--update'], fp=fp) +- res = fp.getvalue() +- assert str(N // 2 * N) + "it" in res # arithmetic sum formula +- +- # test integer --update --delim +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA.replace('\n', 'D')) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--update', '--delim', 'D'], fp=fp) +- res = fp.getvalue() +- assert str(N // 2 * N) + "it" in res # arithmetic sum formula +- +- # test integer --update_to +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--update-to'], fp=fp) +- res = fp.getvalue() +- assert str(N - 1) + "it" in res +- assert str(N) + "it" not in res +- +- # test integer --update_to --delim +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA.replace('\n', 'D')) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--update-to', '--delim', 'D'], fp=fp) +- res = fp.getvalue() +- assert str(N - 1) + "it" in res +- assert str(N) + "it" not in res +- +- # test float --update_to +- IN_DATA = '\n'.join((str(i / 2.0) for i in _range(N))) +- with closing(StringIO()) as sys.stdin: +- sys.stdin.write(IN_DATA) +- +- sys.stdin.seek(0) +- with closing(UnicodeIO()) as fp: +- main(argv=['--update-to'], fp=fp) +- res = fp.getvalue() +- assert str((N - 1) / 2.0) + "it" in res +- assert str(N / 2.0) + "it" not in res +- +- # clean up +- sys.stdin, sys.argv = _SYS +- +- +-def test_manpath(): +- """Test CLI --manpath""" +- if IS_WIN: +- raise SkipTest +- tmp = mkdtemp() +- man = path.join(tmp, "tqdm.1") +- assert not path.exists(man) +- try: +- main(argv=['--manpath', tmp], fp=NULL) +- except SystemExit: +- pass +- else: +- raise SystemExit("Expected system exit") +- assert path.exists(man) +- rmtree(tmp, True) +- +- +-def test_comppath(): +- """Test CLI --comppath""" +- if IS_WIN: +- raise SkipTest +- tmp = mkdtemp() +- man = path.join(tmp, "tqdm_completion.sh") +- assert not path.exists(man) +- try: +- main(argv=['--comppath', tmp], fp=NULL) +- except SystemExit: +- pass +- else: +- raise SystemExit("Expected system exit") +- assert path.exists(man) +- +- # check most important options appear +- with io_open(man, mode='r', encoding='utf-8') as fd: +- script = fd.read() +- opts = set([ +- '--help', '--desc', '--total', '--leave', '--ncols', '--ascii', +- '--dynamic_ncols', '--position', '--bytes', '--nrows', '--delim', +- '--manpath', '--comppath' +- ]) +- assert all(args in script for args in opts) +- rmtree(tmp, True) +- +- +-def test_exceptions(): +- """Test CLI Exceptions""" +- _SYS = sys.stdin, sys.argv +- sys.stdin = map(str, _range(123)) +- +- sys.argv = ['', '-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo'] +- try: +- main(fp=NULL) +- except TqdmKeyError as e: +- if 'bad_arg_u_ment' not in str(e): +- raise +- else: +- raise TqdmKeyError('bad_arg_u_ment') +- +- sys.argv = ['', '-ascii', '-unit_scale', 'invalid_bool_value'] +- try: +- main(fp=NULL) +- except TqdmTypeError as e: +- if 'invalid_bool_value' not in str(e): +- raise +- else: +- raise TqdmTypeError('invalid_bool_value') +- +- sys.argv = ['', '-ascii', '--total', 'invalid_int_value'] +- try: +- main(fp=NULL) +- except TqdmTypeError as e: +- if 'invalid_int_value' not in str(e): +- raise +- else: +- raise TqdmTypeError('invalid_int_value') +- +- sys.argv = ['', '--update', '--update_to'] +- try: +- main(fp=NULL) +- except TqdmKeyError as e: +- if 'Can only have one of --' not in str(e): ++class TestTqdmMain(TestWithInstancesCheck): ++ ++ def test_pipes(self): ++ """Test command line pipes""" ++ ls_out = _sh('ls').replace('\r\n', '\n') ++ ls = subprocess.Popen('ls', stdout=subprocess.PIPE, ++ stderr=subprocess.STDOUT) ++ res = _sh(sys.executable, '-c', 'from tqdm.cli import main; main()', ++ stdin=ls.stdout, stderr=subprocess.STDOUT) ++ ls.wait() ++ ++ # actual test: ++ ++ assert ls_out in res.replace('\r\n', '\n') ++ ++ # WARNING: this should be the last test as it messes with sys.stdin, argv ++ def test_main(self): ++ # hack in posix_pipe() does not work under pytest ++ raise unittest.SkipTest ++ ++ """Test misc CLI options""" ++ _SYS = sys.stdin, sys.argv ++ N = 123 ++ ++ # test direct import ++ sys.stdin = [n.encode('utf-8') for n in map(str, _range(N))] ++ sys.argv = ['', '--desc', 'Test CLI import', ++ '--ascii', 'True', '--unit_scale', 'True'] ++ import tqdm.__main__ # NOQA ++ sys.stderr.write("Test misc CLI options ... ") ++ ++ # test --delim ++ IN_DATA = '\0'.join(map(str, _range(N))) ++ with closing(StringIO()) as sys.stdin: ++ sys.argv = ['', '--desc', 'Test CLI delim', ++ '--ascii', 'True', '--delim', r'\0', '--buf_size', '64'] ++ sys.stdin.write(IN_DATA) ++ # sys.stdin.write(b'\xff') # TODO ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(fp=fp) ++ assert str(N) + "it" in fp.getvalue() ++ ++ # test --bytes ++ IN_DATA = IN_DATA.replace('\0', '\n') ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ sys.stdin.seek(0) ++ sys.argv = ['', '--ascii', '--bytes=True', '--unit_scale', 'False'] ++ with closing(UnicodeIO()) as fp: ++ main(fp=fp) ++ assert str(len(IN_DATA)) in fp.getvalue() ++ ++ # test --log ++ sys.stdin = map(str, _range(N)) ++ # with closing(UnicodeIO()) as fp: ++ main(argv=['--log', 'DEBUG'], fp=NULL) ++ # assert "DEBUG:" in sys.stdout.getvalue() ++ ++ # test --tee ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--mininterval', '0', '--miniters', '1'], fp=fp) ++ res = len(fp.getvalue()) ++ # assert len(fp.getvalue()) < len(sys.stdout.getvalue()) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--tee', '--mininterval', '0', '--miniters', '1'], ++ fp=fp) ++ # spaces to clear intermediate lines could increase length ++ assert len(fp.getvalue()) >= res + len(IN_DATA) ++ ++ # test --null ++ _STDOUT = sys.stdout ++ try: ++ with closing(StringIO()) as sys.stdout: ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--null'], fp=fp) ++ assert not sys.stdout.getvalue() ++ ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=[], fp=fp) ++ assert sys.stdout.getvalue() ++ except: ++ sys.stdout = _STDOUT + raise +- else: +- raise TqdmKeyError('Cannot have both --update --update_to') +- +- # test SystemExits +- for i in ('-h', '--help', '-v', '--version'): +- sys.argv = ['', i] ++ else: ++ sys.stdout = _STDOUT ++ ++ # test integer --update ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--update'], fp=fp) ++ res = fp.getvalue() ++ assert str(N // 2 * N) + "it" in res # arithmetic sum formula ++ ++ # test integer --update --delim ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA.replace('\n', 'D')) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--update', '--delim', 'D'], fp=fp) ++ res = fp.getvalue() ++ assert str(N // 2 * N) + "it" in res # arithmetic sum formula ++ ++ # test integer --update_to ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--update-to'], fp=fp) ++ res = fp.getvalue() ++ assert str(N - 1) + "it" in res ++ assert str(N) + "it" not in res ++ ++ # test integer --update_to --delim ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA.replace('\n', 'D')) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--update-to', '--delim', 'D'], fp=fp) ++ res = fp.getvalue() ++ assert str(N - 1) + "it" in res ++ assert str(N) + "it" not in res ++ ++ # test float --update_to ++ IN_DATA = '\n'.join((str(i / 2.0) for i in _range(N))) ++ with closing(StringIO()) as sys.stdin: ++ sys.stdin.write(IN_DATA) ++ ++ sys.stdin.seek(0) ++ with closing(UnicodeIO()) as fp: ++ main(argv=['--update-to'], fp=fp) ++ res = fp.getvalue() ++ assert str((N - 1) / 2.0) + "it" in res ++ assert str(N / 2.0) + "it" not in res ++ ++ # clean up ++ sys.stdin, sys.argv = _SYS ++ ++ def test_manpath(self): ++ """Test CLI --manpath""" ++ if IS_WIN: ++ raise unittest.SkipTest ++ tmp = mkdtemp() ++ man = path.join(tmp, "tqdm.1") ++ assert not path.exists(man) + try: +- main(fp=NULL) ++ main(argv=['--manpath', tmp], fp=NULL) + except SystemExit: + pass + else: +- raise ValueError('expected SystemExit') ++ raise SystemExit("Expected system exit") ++ assert path.exists(man) ++ rmtree(tmp, True) ++ ++ def test_comppath(self): ++ """Test CLI --comppath""" ++ if IS_WIN: ++ raise unittest.SkipTest ++ tmp = mkdtemp() ++ man = path.join(tmp, "tqdm_completion.sh") ++ assert not path.exists(man) ++ try: ++ main(argv=['--comppath', tmp], fp=NULL) ++ except SystemExit: ++ pass ++ else: ++ raise SystemExit("Expected system exit") ++ assert path.exists(man) ++ ++ # check most important options appear ++ with io_open(man, mode='r', encoding='utf-8') as fd: ++ script = fd.read() ++ opts = set([ ++ '--help', '--desc', '--total', '--leave', '--ncols', '--ascii', ++ '--dynamic_ncols', '--position', '--bytes', '--nrows', '--delim', ++ '--manpath', '--comppath' ++ ]) ++ assert all(args in script for args in opts) ++ rmtree(tmp, True) ++ ++ def test_exceptions(self): ++ """Test CLI Exceptions""" ++ _SYS = sys.stdin, sys.argv ++ sys.stdin = map(str, _range(123)) ++ ++ sys.argv = ['', '-ascii', '-unit_scale', '--bad_arg_u_ment', 'foo'] ++ try: ++ main(fp=NULL) ++ except TqdmKeyError as e: ++ if 'bad_arg_u_ment' not in str(e): ++ raise ++ else: ++ raise TqdmKeyError('bad_arg_u_ment') ++ ++ sys.argv = ['', '-ascii', '-unit_scale', 'invalid_bool_value'] ++ try: ++ main(fp=NULL) ++ except TqdmTypeError as e: ++ if 'invalid_bool_value' not in str(e): ++ raise ++ else: ++ raise TqdmTypeError('invalid_bool_value') ++ ++ sys.argv = ['', '-ascii', '--total', 'invalid_int_value'] ++ try: ++ main(fp=NULL) ++ except TqdmTypeError as e: ++ if 'invalid_int_value' not in str(e): ++ raise ++ else: ++ raise TqdmTypeError('invalid_int_value') + +- # clean up +- sys.stdin, sys.argv = _SYS ++ sys.argv = ['', '--update', '--update_to'] ++ try: ++ main(fp=NULL) ++ except TqdmKeyError as e: ++ if 'Can only have one of --' not in str(e): ++ raise ++ else: ++ raise TqdmKeyError('Cannot have both --update --update_to') ++ ++ # test SystemExits ++ for i in ('-h', '--help', '-v', '--version'): ++ sys.argv = ['', i] ++ try: ++ main(fp=NULL) ++ except SystemExit: ++ pass ++ else: ++ raise ValueError('expected SystemExit') ++ ++ # clean up ++ sys.stdin, sys.argv = _SYS +diff --git a/tqdm/tests/tests_notebook.py b/tqdm/tests/tests_notebook.py +index 3af992f0..cb422391 100644 +--- a/tqdm/tests/tests_notebook.py ++++ b/tqdm/tests/tests_notebook.py +@@ -1,9 +1,9 @@ + from tqdm.notebook import tqdm as tqdm_notebook +-from tests_tqdm import with_setup, pretest, posttest ++from tests_tqdm import TestWithInstancesCheck + + +-@with_setup(pretest, posttest) +-def test_notebook_disabled_description(): +- """Test that set_description works for disabled tqdm_notebook""" +- with tqdm_notebook(1, disable=True) as t: +- t.set_description("description") ++class TestTqdmNotebook(TestWithInstancesCheck): ++ def test_notebook_disabled_description(self): ++ """Test that set_description works for disabled tqdm_notebook""" ++ with tqdm_notebook(1, disable=True) as t: ++ t.set_description("description") +diff --git a/tqdm/tests/tests_pandas.py b/tqdm/tests/tests_pandas.py +index 8719a7ca..5d9f5944 100644 +--- a/tqdm/tests/tests_pandas.py ++++ b/tqdm/tests/tests_pandas.py +@@ -1,264 +1,250 @@ ++import unittest + from tqdm import tqdm +-from tests_tqdm import with_setup, pretest, posttest, SkipTest, \ +- StringIO, closing +- +- +-@with_setup(pretest, posttest) +-def test_pandas_setup(): +- """Test tqdm.pandas()""" +- try: +- from numpy.random import randint +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=True, ascii=True, total=123) +- series = pd.Series(randint(0, 50, (100,))) +- series.progress_apply(lambda x: x + 10) +- res = our_file.getvalue() +- assert '100/123' in res +- +- +-@with_setup(pretest, posttest) +-def test_pandas_rolling_expanding(): +- """Test pandas.(Series|DataFrame).(rolling|expanding)""" +- try: +- from numpy.random import randint +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=True, ascii=True) +- +- series = pd.Series(randint(0, 50, (123,))) +- res1 = series.rolling(10).progress_apply(lambda x: 1, raw=True) +- res2 = series.rolling(10).apply(lambda x: 1, raw=True) +- assert res1.equals(res2) +- +- res3 = series.expanding(10).progress_apply(lambda x: 2, raw=True) +- res4 = series.expanding(10).apply(lambda x: 2, raw=True) +- assert res3.equals(res4) +- +- expects = ['114it'] # 123-10+1 +- for exres in expects: +- our_file.seek(0) +- if our_file.getvalue().count(exres) < 2: +- our_file.seek(0) +- raise AssertionError( +- "\nExpected:\n{0}\nIn:\n{1}\n".format( +- exres + " at least twice.", our_file.read())) +- +- +-@with_setup(pretest, posttest) +-def test_pandas_series(): +- """Test pandas.Series.progress_apply and .progress_map""" +- try: +- from numpy.random import randint +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=True, ascii=True) +- +- series = pd.Series(randint(0, 50, (123,))) +- res1 = series.progress_apply(lambda x: x + 10) +- res2 = series.apply(lambda x: x + 10) +- assert res1.equals(res2) +- +- res3 = series.progress_map(lambda x: x + 10) +- res4 = series.map(lambda x: x + 10) +- assert res3.equals(res4) ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing ++ ++ ++class TestTqdmPandas(TestWithInstancesCheck): ++ def test_pandas_setup(self): ++ """Test tqdm.pandas()""" ++ try: ++ from numpy.random import randint ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=True, ascii=True, total=123) ++ series = pd.Series(randint(0, 50, (100,))) ++ series.progress_apply(lambda x: x + 10) ++ res = our_file.getvalue() ++ assert '100/123' in res ++ ++ def test_pandas_rolling_expanding(self): ++ """Test pandas.(Series|DataFrame).(rolling|expanding)""" ++ try: ++ from numpy.random import randint ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=True, ascii=True) ++ ++ series = pd.Series(randint(0, 50, (123,))) ++ res1 = series.rolling(10).progress_apply(lambda x: 1, raw=True) ++ res2 = series.rolling(10).apply(lambda x: 1, raw=True) ++ assert res1.equals(res2) ++ ++ res3 = series.expanding(10).progress_apply(lambda x: 2, raw=True) ++ res4 = series.expanding(10).apply(lambda x: 2, raw=True) ++ assert res3.equals(res4) + +- expects = ['100%', '123/123'] +- for exres in expects: +- our_file.seek(0) +- if our_file.getvalue().count(exres) < 2: ++ expects = ['114it'] # 123-10+1 ++ for exres in expects: + our_file.seek(0) +- raise AssertionError( +- "\nExpected:\n{0}\nIn:\n{1}\n".format( +- exres + " at least twice.", our_file.read())) +- +- +-@with_setup(pretest, posttest) +-def test_pandas_data_frame(): +- """Test pandas.DataFrame.progress_apply and .progress_applymap""" +- try: +- from numpy.random import randint +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=True, ascii=True) +- df = pd.DataFrame(randint(0, 50, (100, 200))) +- +- def task_func(x): +- return x + 1 +- +- # applymap +- res1 = df.progress_applymap(task_func) +- res2 = df.applymap(task_func) +- assert res1.equals(res2) +- +- # apply unhashable +- res1 = [] +- df.progress_apply(res1.extend) +- assert len(res1) == df.size +- +- # apply +- for axis in [0, 1, 'index', 'columns']: +- res3 = df.progress_apply(task_func, axis=axis) +- res4 = df.apply(task_func, axis=axis) ++ if our_file.getvalue().count(exres) < 2: ++ our_file.seek(0) ++ raise AssertionError( ++ "\nExpected:\n{0}\nIn:\n{1}\n".format( ++ exres + " at least twice.", our_file.read())) ++ ++ def test_pandas_series(self): ++ """Test pandas.Series.progress_apply and .progress_map""" ++ try: ++ from numpy.random import randint ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=True, ascii=True) ++ ++ series = pd.Series(randint(0, 50, (123,))) ++ res1 = series.progress_apply(lambda x: x + 10) ++ res2 = series.apply(lambda x: x + 10) ++ assert res1.equals(res2) ++ ++ res3 = series.progress_map(lambda x: x + 10) ++ res4 = series.map(lambda x: x + 10) + assert res3.equals(res4) + +- our_file.seek(0) +- if our_file.read().count('100%') < 3: +- our_file.seek(0) +- raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format( +- '100% at least three times', our_file.read())) ++ expects = ['100%', '123/123'] ++ for exres in expects: ++ our_file.seek(0) ++ if our_file.getvalue().count(exres) < 2: ++ our_file.seek(0) ++ raise AssertionError( ++ "\nExpected:\n{0}\nIn:\n{1}\n".format( ++ exres + " at least twice.", our_file.read())) ++ ++ def test_pandas_data_frame(self): ++ """Test pandas.DataFrame.progress_apply and .progress_applymap""" ++ try: ++ from numpy.random import randint ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=True, ascii=True) ++ df = pd.DataFrame(randint(0, 50, (100, 200))) ++ ++ def task_func(x): ++ return x + 1 ++ ++ # applymap ++ res1 = df.progress_applymap(task_func) ++ res2 = df.applymap(task_func) ++ assert res1.equals(res2) ++ ++ # apply unhashable ++ res1 = [] ++ df.progress_apply(res1.extend) ++ assert len(res1) == df.size ++ ++ # apply ++ for axis in [0, 1, 'index', 'columns']: ++ res3 = df.progress_apply(task_func, axis=axis) ++ res4 = df.apply(task_func, axis=axis) ++ assert res3.equals(res4) + +- # apply_map, apply axis=0, apply axis=1 +- expects = ['20000/20000', '200/200', '100/100'] +- for exres in expects: + our_file.seek(0) +- if our_file.getvalue().count(exres) < 1: ++ if our_file.read().count('100%') < 3: + our_file.seek(0) +- raise AssertionError( +- "\nExpected:\n{0}\nIn:\n {1}\n".format( +- exres + " at least once.", our_file.read())) +- ++ raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format( ++ '100% at least three times', our_file.read())) + +-@with_setup(pretest, posttest) +-def test_pandas_groupby_apply(): +- """Test pandas.DataFrame.groupby(...).progress_apply""" +- try: +- from numpy.random import randint, rand +- import pandas as pd +- except ImportError: +- raise SkipTest ++ # apply_map, apply axis=0, apply axis=1 ++ expects = ['20000/20000', '200/200', '100/100'] ++ for exres in expects: ++ our_file.seek(0) ++ if our_file.getvalue().count(exres) < 1: ++ our_file.seek(0) ++ raise AssertionError( ++ "\nExpected:\n{0}\nIn:\n {1}\n".format( ++ exres + " at least once.", our_file.read())) ++ ++ def test_pandas_groupby_apply(self): ++ """Test pandas.DataFrame.groupby(...).progress_apply""" ++ try: ++ from numpy.random import randint, rand ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=False, ascii=True) ++ ++ df = pd.DataFrame(randint(0, 50, (500, 3))) ++ df.groupby(0).progress_apply(lambda x: None) ++ ++ dfs = pd.DataFrame(randint(0, 50, (500, 3)), columns=list('abc')) ++ dfs.groupby(['a']).progress_apply(lambda x: None) ++ ++ df2 = df = pd.DataFrame(dict(a=randint(1, 8, 10000), b=rand(10000))) ++ res1 = df2.groupby("a").apply(max) ++ res2 = df2.groupby("a").progress_apply(max) ++ assert res1.equals(res2) + +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=False, ascii=True) ++ our_file.seek(0) + +- df = pd.DataFrame(randint(0, 50, (500, 3))) +- df.groupby(0).progress_apply(lambda x: None) ++ # don't expect final output since no `leave` and ++ # high dynamic `miniters` ++ nexres = '100%|##########|' ++ if nexres in our_file.read(): ++ our_file.seek(0) ++ raise AssertionError("\nDid not expect:\n{0}\nIn:{1}\n".format( ++ nexres, our_file.read())) + +- dfs = pd.DataFrame(randint(0, 50, (500, 3)), columns=list('abc')) +- dfs.groupby(['a']).progress_apply(lambda x: None) ++ with closing(StringIO()) as our_file: ++ tqdm.pandas(file=our_file, leave=True, ascii=True) + +- df2 = df = pd.DataFrame(dict(a=randint(1, 8, 10000), b=rand(10000))) +- res1 = df2.groupby("a").apply(max) +- res2 = df2.groupby("a").progress_apply(max) +- assert res1.equals(res2) ++ dfs = pd.DataFrame(randint(0, 50, (500, 3)), columns=list('abc')) ++ dfs.loc[0] = [2, 1, 1] ++ dfs['d'] = 100 + +- our_file.seek(0) ++ expects = ['500/500', '1/1', '4/4', '2/2'] ++ dfs.groupby(dfs.index).progress_apply(lambda x: None) ++ dfs.groupby('d').progress_apply(lambda x: None) ++ dfs.groupby(dfs.columns, axis=1).progress_apply(lambda x: None) ++ dfs.groupby([2, 2, 1, 1], axis=1).progress_apply(lambda x: None) + +- # don't expect final output since no `leave` and +- # high dynamic `miniters` +- nexres = '100%|##########|' +- if nexres in our_file.read(): + our_file.seek(0) +- raise AssertionError("\nDid not expect:\n{0}\nIn:{1}\n".format( +- nexres, our_file.read())) +- +- with closing(StringIO()) as our_file: +- tqdm.pandas(file=our_file, leave=True, ascii=True) +- +- dfs = pd.DataFrame(randint(0, 50, (500, 3)), columns=list('abc')) +- dfs.loc[0] = [2, 1, 1] +- dfs['d'] = 100 ++ if our_file.read().count('100%') < 4: ++ our_file.seek(0) ++ raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format( ++ '100% at least four times', our_file.read())) + +- expects = ['500/500', '1/1', '4/4', '2/2'] +- dfs.groupby(dfs.index).progress_apply(lambda x: None) +- dfs.groupby('d').progress_apply(lambda x: None) +- dfs.groupby(dfs.columns, axis=1).progress_apply(lambda x: None) +- dfs.groupby([2, 2, 1, 1], axis=1).progress_apply(lambda x: None) ++ for exres in expects: ++ our_file.seek(0) ++ if our_file.getvalue().count(exres) < 1: ++ our_file.seek(0) ++ raise AssertionError( ++ "\nExpected:\n{0}\nIn:\n {1}\n".format( ++ exres + " at least once.", our_file.read())) ++ ++ def test_pandas_leave(self): ++ """Test pandas with `leave=True`""" ++ try: ++ from numpy.random import randint ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ df = pd.DataFrame(randint(0, 100, (1000, 6))) ++ tqdm.pandas(file=our_file, leave=True, ascii=True) ++ df.groupby(0).progress_apply(lambda x: None) + +- our_file.seek(0) +- if our_file.read().count('100%') < 4: + our_file.seek(0) +- raise AssertionError("\nExpected:\n{0}\nIn:\n{1}\n".format( +- '100% at least four times', our_file.read())) + +- for exres in expects: +- our_file.seek(0) +- if our_file.getvalue().count(exres) < 1: ++ exres = '100%|##########| 100/100' ++ if exres not in our_file.read(): + our_file.seek(0) + raise AssertionError( +- "\nExpected:\n{0}\nIn:\n {1}\n".format( +- exres + " at least once.", our_file.read())) +- +- +-@with_setup(pretest, posttest) +-def test_pandas_leave(): +- """Test pandas with `leave=True`""" +- try: +- from numpy.random import randint +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- df = pd.DataFrame(randint(0, 100, (1000, 6))) +- tqdm.pandas(file=our_file, leave=True, ascii=True) +- df.groupby(0).progress_apply(lambda x: None) +- +- our_file.seek(0) +- +- exres = '100%|##########| 100/100' +- if exres not in our_file.read(): +- our_file.seek(0) +- raise AssertionError( +- "\nExpected:\n{0}\nIn:{1}\n".format(exres, our_file.read())) +- +- +-@with_setup(pretest, posttest) +-def test_pandas_apply_args_deprecation(): +- """Test warning info in +- `pandas.Dataframe(Series).progress_apply(func, *args)`""" +- try: +- from numpy.random import randint +- from tqdm import tqdm_pandas +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True, ncols=20)) +- df = pd.DataFrame(randint(0, 50, (500, 3))) +- df.progress_apply(lambda x: None, 1) # 1 shall cause a warning +- # Check deprecation message +- res = our_file.getvalue() +- assert all([i in res for i in ( +- "TqdmDeprecationWarning", "not supported", +- "keyword arguments instead")]) +- +- +-@with_setup(pretest, posttest) +-def test_pandas_deprecation(): +- """Test bar object instance as argument deprecation""" +- try: +- from numpy.random import randint +- from tqdm import tqdm_pandas +- import pandas as pd +- except ImportError: +- raise SkipTest +- +- with closing(StringIO()) as our_file: +- tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True, ncols=20)) +- df = pd.DataFrame(randint(0, 50, (500, 3))) +- df.groupby(0).progress_apply(lambda x: None) +- # Check deprecation message +- assert "TqdmDeprecationWarning" in our_file.getvalue() +- assert "instead of `tqdm_pandas(tqdm(...))`" in our_file.getvalue() +- +- with closing(StringIO()) as our_file: +- tqdm_pandas(tqdm, file=our_file, leave=False, ascii=True, ncols=20) +- df = pd.DataFrame(randint(0, 50, (500, 3))) +- df.groupby(0).progress_apply(lambda x: None) +- # Check deprecation message +- assert "TqdmDeprecationWarning" in our_file.getvalue() +- assert "instead of `tqdm_pandas(tqdm, ...)`" in our_file.getvalue() ++ "\nExpected:\n{0}\nIn:{1}\n".format(exres, our_file.read())) ++ ++ def test_pandas_apply_args_deprecation(self): ++ """Test warning info in ++ `pandas.Dataframe(Series).progress_apply(func, *args)`""" ++ try: ++ from numpy.random import randint ++ from tqdm import tqdm_pandas ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True, ncols=20)) ++ df = pd.DataFrame(randint(0, 50, (500, 3))) ++ df.progress_apply(lambda x: None, 1) # 1 shall cause a warning ++ # Check deprecation message ++ res = our_file.getvalue() ++ assert all([i in res for i in ( ++ "TqdmDeprecationWarning", "not supported", ++ "keyword arguments instead")]) ++ ++ def test_pandas_deprecation(self): ++ """Test bar object instance as argument deprecation""" ++ try: ++ from numpy.random import randint ++ from tqdm import tqdm_pandas ++ import pandas as pd ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with closing(StringIO()) as our_file: ++ tqdm_pandas(tqdm(file=our_file, leave=False, ascii=True, ncols=20)) ++ df = pd.DataFrame(randint(0, 50, (500, 3))) ++ df.groupby(0).progress_apply(lambda x: None) ++ # Check deprecation message ++ assert "TqdmDeprecationWarning" in our_file.getvalue() ++ assert "instead of `tqdm_pandas(tqdm(...))`" in our_file.getvalue() ++ ++ with closing(StringIO()) as our_file: ++ tqdm_pandas(tqdm, file=our_file, leave=False, ascii=True, ncols=20) ++ df = pd.DataFrame(randint(0, 50, (500, 3))) ++ df.groupby(0).progress_apply(lambda x: None) ++ # Check deprecation message ++ assert "TqdmDeprecationWarning" in our_file.getvalue() ++ assert "instead of `tqdm_pandas(tqdm, ...)`" in our_file.getvalue() +diff --git a/tqdm/tests/tests_perf.py b/tqdm/tests/tests_perf.py +index 8e808f7d..b6f558fb 100644 +--- a/tqdm/tests/tests_perf.py ++++ b/tqdm/tests/tests_perf.py +@@ -1,4 +1,5 @@ + from __future__ import print_function, division ++import unittest + from contextlib import contextmanager + from functools import wraps + from time import sleep, time +@@ -11,11 +12,9 @@ + import sys + + from tqdm import tqdm, trange +-from tests_tqdm import with_setup, pretest, posttest, StringIO, closing, \ ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing, \ + _range, patch_lock + +-from nose.plugins.skip import SkipTest +- + + def cpu_sleep(t): + """Sleep the given amount of cpu time""" +@@ -41,7 +40,7 @@ def checkCpuTime(sleeptime=0.2): + if abs(t1) < 0.0001 and t1 < t2 / 10: + checkCpuTime.passed = True + return True +- raise SkipTest ++ raise unittest.SkipTest + + + checkCpuTime.passed = False +@@ -74,7 +73,7 @@ def test_inner(*args, **kwargs): + if check_cpu_time: + checkCpuTime() + func(*args, **kwargs) +- except SkipTest: ++ except unittest.SkipTest: + raise + except Exception: + if i >= n: +@@ -170,195 +169,184 @@ def assert_performance(thresh, name_left, time_left, name_right, time_right): + ratio=time_left / time_right, thresh=thresh)) + + +-@with_setup(pretest, posttest) +-@retry_on_except() +-def test_iter_basic_overhead(): +- """Test overhead of iteration based tqdm""" ++def worker(total, blocking=True): ++ def incr_bar(x): ++ with closing(StringIO()) as our_file: ++ for _ in trange( ++ total, file=our_file, ++ lock_args=None if blocking else (False,), ++ miniters=1, mininterval=0, maxinterval=0): ++ pass ++ return x + 1 ++ return incr_bar + +- total = int(1e6) + +- with closing(MockIO()) as our_file: +- a = 0 +- with trange(total, file=our_file) as t: +- with relative_timer() as time_tqdm: +- for i in t: +- a += i +- assert a == (total * total - total) / 2.0 ++class TestTqdmPerf(TestWithInstancesCheck): ++ @retry_on_except() ++ def test_iter_basic_overhead(self): ++ """Test overhead of iteration based tqdm""" ++ ++ total = int(1e6) ++ ++ with closing(MockIO()) as our_file: ++ a = 0 ++ with trange(total, file=our_file) as t: ++ with relative_timer() as time_tqdm: ++ for i in t: ++ a += i ++ assert a == (total * total - total) / 2.0 + +- a = 0 +- with relative_timer() as time_bench: +- for i in _range(total): +- a += i +- our_file.write(a) ++ a = 0 ++ with relative_timer() as time_bench: ++ for i in _range(total): ++ a += i ++ our_file.write(a) + +- assert_performance(3, 'trange', time_tqdm(), 'range', time_bench()) ++ assert_performance(3, 'trange', time_tqdm(), 'range', time_bench()) + ++ @retry_on_except() ++ def test_manual_basic_overhead(self): ++ """Test overhead of manual tqdm""" + +-@with_setup(pretest, posttest) +-@retry_on_except() +-def test_manual_basic_overhead(): +- """Test overhead of manual tqdm""" ++ total = int(1e6) + +- total = int(1e6) ++ with closing(MockIO()) as our_file: ++ with tqdm(total=total * 10, file=our_file, leave=True) as t: ++ a = 0 ++ with relative_timer() as time_tqdm: ++ for i in _range(total): ++ a += i ++ t.update(10) + +- with closing(MockIO()) as our_file: +- with tqdm(total=total * 10, file=our_file, leave=True) as t: + a = 0 +- with relative_timer() as time_tqdm: ++ with relative_timer() as time_bench: + for i in _range(total): + a += i +- t.update(10) ++ our_file.write(a) + +- a = 0 +- with relative_timer() as time_bench: +- for i in _range(total): +- a += i +- our_file.write(a) ++ assert_performance(5, 'tqdm', time_tqdm(), 'range', time_bench()) + +- assert_performance(5, 'tqdm', time_tqdm(), 'range', time_bench()) ++ @retry_on_except() ++ @patch_lock(thread=True) ++ def test_lock_args(self): ++ """Test overhead of nonblocking threads""" ++ try: ++ from concurrent.futures import ThreadPoolExecutor ++ except ImportError: ++ raise unittest.SkipTest + ++ total = 16 ++ subtotal = 10000 + +-def worker(total, blocking=True): +- def incr_bar(x): +- with closing(StringIO()) as our_file: +- for _ in trange( +- total, file=our_file, +- lock_args=None if blocking else (False,), +- miniters=1, mininterval=0, maxinterval=0): +- pass +- return x + 1 +- return incr_bar ++ with ThreadPoolExecutor() as pool: ++ sys.stderr.write('block ... ') ++ sys.stderr.flush() ++ with relative_timer() as time_tqdm: ++ res = list(pool.map(worker(subtotal, True), range(total))) ++ assert sum(res) == sum(range(total)) + total ++ sys.stderr.write('noblock ... ') ++ sys.stderr.flush() ++ with relative_timer() as time_noblock: ++ res = list(pool.map(worker(subtotal, False), range(total))) ++ assert sum(res) == sum(range(total)) + total + ++ assert_performance(0.5, 'noblock', time_noblock(), 'tqdm', time_tqdm()) + +-@with_setup(pretest, posttest) +-@retry_on_except() +-@patch_lock(thread=True) +-def test_lock_args(): +- """Test overhead of nonblocking threads""" +- try: +- from concurrent.futures import ThreadPoolExecutor +- except ImportError: +- raise SkipTest +- +- total = 16 +- subtotal = 10000 +- +- with ThreadPoolExecutor() as pool: +- sys.stderr.write('block ... ') +- sys.stderr.flush() +- with relative_timer() as time_tqdm: +- res = list(pool.map(worker(subtotal, True), range(total))) +- assert sum(res) == sum(range(total)) + total +- sys.stderr.write('noblock ... ') +- sys.stderr.flush() +- with relative_timer() as time_noblock: +- res = list(pool.map(worker(subtotal, False), range(total))) +- assert sum(res) == sum(range(total)) + total +- +- assert_performance(0.5, 'noblock', time_noblock(), 'tqdm', time_tqdm()) +- +- +-@with_setup(pretest, posttest) +-@retry_on_except() +-def test_iter_overhead_hard(): +- """Test overhead of iteration based tqdm (hard)""" +- +- total = int(1e5) +- +- with closing(MockIO()) as our_file: +- a = 0 +- with trange(total, file=our_file, leave=True, miniters=1, +- mininterval=0, maxinterval=0) as t: +- with relative_timer() as time_tqdm: +- for i in t: +- a += i +- assert a == (total * total - total) / 2.0 ++ @retry_on_except() ++ def test_iter_overhead_hard(self): ++ """Test overhead of iteration based tqdm (hard)""" + +- a = 0 +- with relative_timer() as time_bench: +- for i in _range(total): +- a += i +- our_file.write(("%i" % a) * 40) ++ total = int(1e5) + +- assert_performance(130, 'trange', time_tqdm(), 'range', time_bench()) ++ with closing(MockIO()) as our_file: ++ a = 0 ++ with trange(total, file=our_file, leave=True, miniters=1, ++ mininterval=0, maxinterval=0) as t: ++ with relative_timer() as time_tqdm: ++ for i in t: ++ a += i ++ assert a == (total * total - total) / 2.0 + ++ a = 0 ++ with relative_timer() as time_bench: ++ for i in _range(total): ++ a += i ++ our_file.write(("%i" % a) * 40) ++ ++ assert_performance(130, 'trange', time_tqdm(), 'range', time_bench()) ++ ++ @retry_on_except() ++ def test_manual_overhead_hard(self): ++ """Test overhead of manual tqdm (hard)""" + +-@with_setup(pretest, posttest) +-@retry_on_except() +-def test_manual_overhead_hard(): +- """Test overhead of manual tqdm (hard)""" ++ total = int(1e5) + +- total = int(1e5) ++ with closing(MockIO()) as our_file: ++ with tqdm(total=total * 10, file=our_file, leave=True, miniters=1, ++ mininterval=0, maxinterval=0) as t: ++ a = 0 ++ with relative_timer() as time_tqdm: ++ for i in _range(total): ++ a += i ++ t.update(10) + +- with closing(MockIO()) as our_file: +- with tqdm(total=total * 10, file=our_file, leave=True, miniters=1, +- mininterval=0, maxinterval=0) as t: + a = 0 +- with relative_timer() as time_tqdm: ++ with relative_timer() as time_bench: + for i in _range(total): + a += i +- t.update(10) +- +- a = 0 +- with relative_timer() as time_bench: +- for i in _range(total): +- a += i +- our_file.write(("%i" % a) * 40) ++ our_file.write(("%i" % a) * 40) + +- assert_performance(130, 'tqdm', time_tqdm(), 'range', time_bench()) ++ assert_performance(130, 'tqdm', time_tqdm(), 'range', time_bench()) + ++ @retry_on_except() ++ def test_iter_overhead_simplebar_hard(self): ++ """Test overhead of iteration based tqdm vs simple ++ progress bar (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)""" ++ total = int(1e4) + +- total = int(1e4) ++ with closing(MockIO()) as our_file: ++ a = 0 ++ with trange(total, file=our_file, leave=True, miniters=1, ++ mininterval=0, maxinterval=0) as t: ++ with relative_timer() as time_tqdm: ++ for i in t: ++ a += i ++ assert a == (total * total - total) / 2.0 + +- with closing(MockIO()) as our_file: +- a = 0 +- with trange(total, file=our_file, leave=True, miniters=1, +- mininterval=0, maxinterval=0) as t: +- with relative_timer() as time_tqdm: +- for i in t: ++ a = 0 ++ s = simple_progress(_range(total), file=our_file, leave=True, ++ miniters=1, mininterval=0) ++ with relative_timer() as time_bench: ++ for i in s: + a += i +- assert a == (total * total - total) / 2.0 +- +- a = 0 +- s = simple_progress(_range(total), file=our_file, leave=True, +- miniters=1, mininterval=0) +- with relative_timer() as time_bench: +- for i in s: +- a += i + +- assert_performance( +- 10, 'trange', time_tqdm(), 'simple_progress', time_bench()) ++ assert_performance( ++ 10, 'trange', time_tqdm(), 'simple_progress', time_bench()) + ++ @retry_on_except() ++ def test_manual_overhead_simplebar_hard(self): ++ """Test overhead of manual tqdm vs simple progress bar (hard)""" + +-@with_setup(pretest, posttest) +-@retry_on_except() +-def test_manual_overhead_simplebar_hard(): +- """Test overhead of manual tqdm vs simple progress bar (hard)""" ++ total = int(1e4) + +- total = int(1e4) ++ with closing(MockIO()) as our_file: ++ with tqdm(total=total * 10, file=our_file, leave=True, miniters=1, ++ mininterval=0, maxinterval=0) as t: ++ a = 0 ++ with relative_timer() as time_tqdm: ++ for i in _range(total): ++ a += i ++ t.update(10) + +- with closing(MockIO()) as our_file: +- with tqdm(total=total * 10, file=our_file, leave=True, miniters=1, +- mininterval=0, maxinterval=0) as t: ++ simplebar_update = simple_progress( ++ total=total * 10, file=our_file, leave=True, miniters=1, ++ mininterval=0) + a = 0 +- with relative_timer() as time_tqdm: ++ with relative_timer() as time_bench: + for i in _range(total): + a += i +- t.update(10) +- +- simplebar_update = simple_progress( +- total=total * 10, file=our_file, leave=True, miniters=1, +- mininterval=0) +- a = 0 +- with relative_timer() as time_bench: +- for i in _range(total): +- a += i +- simplebar_update(10) +- +- assert_performance( +- 10, 'tqdm', time_tqdm(), 'simple_progress', time_bench()) ++ simplebar_update(10) ++ ++ assert_performance( ++ 10, 'tqdm', time_tqdm(), 'simple_progress', time_bench()) +diff --git a/tqdm/tests/tests_synchronisation.py b/tqdm/tests/tests_synchronisation.py +index 60b4860a..95774c11 100644 +--- a/tqdm/tests/tests_synchronisation.py ++++ b/tqdm/tests/tests_synchronisation.py +@@ -1,7 +1,7 @@ + from __future__ import division ++import unittest + from tqdm import tqdm, trange, TMonitor +-from tests_tqdm import with_setup, pretest, posttest, SkipTest, \ +- StringIO, closing, patch_lock ++from tests_tqdm import TestWithInstancesCheck, StringIO, closing, patch_lock + from tests_perf import retry_on_except + + from functools import wraps +@@ -102,128 +102,129 @@ def incr_bar(x): + return incr(x) + + +-@patch_sleep +-@with_setup(pretest, posttest) +-def test_monitor_thread(): +- """Test dummy monitoring thread""" +- monitor = TMonitor(FakeTqdm, 10) +- # Test if alive, then killed +- assert monitor.report() +- monitor.exit() +- assert not monitor.report() +- assert not monitor.is_alive() +- del monitor +- +- +-@patch_sleep +-@with_setup(pretest, posttest) +-def test_monitoring_and_cleanup(): +- """Test for stalled tqdm instance and monitor deletion""" +- # Note: should fix miniters for these tests, else with dynamic_miniters +- # it's too complicated to handle with monitoring update and maxinterval... +- maxinterval = tqdm.monitor_interval +- assert maxinterval == 10 +- total = 1000 +- +- with closing(StringIO()) as our_file: +- with tqdm(total=total, file=our_file, miniters=500, mininterval=0.1, +- maxinterval=maxinterval) as t: +- cpu_timify(t, Time) +- # Do a lot of iterations in a small timeframe +- # (smaller than monitor interval) +- Time.fake_sleep(maxinterval / 10) # monitor won't wake up +- t.update(500) +- # check that our fixed miniters is still there +- assert t.miniters <= 500 # TODO: should really be == 500 +- # Then do 1 it after monitor interval, so that monitor kicks in +- Time.fake_sleep(maxinterval) +- t.update(1) +- # Wait for the monitor to get out of sleep's loop and update tqdm.. +- timeend = Time.time() +- while not (t.monitor.woken >= timeend and t.miniters == 1): +- Time.fake_sleep(1) # Force awake up if it woken too soon +- assert t.miniters == 1 # check that monitor corrected miniters +- # Note: at this point, there may be a race condition: monitor saved +- # current woken time but Time.sleep() happen just before monitor +- # sleep. To fix that, either sleep here or increase time in a loop +- # to ensure that monitor wakes up at some point. +- +- # Try again but already at miniters = 1 so nothing will be done +- Time.fake_sleep(maxinterval) +- t.update(2) +- timeend = Time.time() +- while t.monitor.woken < timeend: +- Time.fake_sleep(1) # Force awake if it woken too soon +- # Wait for the monitor to get out of sleep's loop and update tqdm +- assert t.miniters == 1 # check that monitor corrected miniters +- +- +-@patch_sleep +-@with_setup(pretest, posttest) +-def test_monitoring_multi(): +- """Test on multiple bars, one not needing miniters adjustment""" +- # Note: should fix miniters for these tests, else with dynamic_miniters +- # it's too complicated to handle with monitoring update and maxinterval... +- maxinterval = tqdm.monitor_interval +- assert maxinterval == 10 +- total = 1000 +- +- with closing(StringIO()) as our_file: +- with tqdm(total=total, file=our_file, miniters=500, mininterval=0.1, +- maxinterval=maxinterval) as t1: +- # Set high maxinterval for t2 so monitor does not need to adjust it ++class TestTqdmSynchronisation(TestWithInstancesCheck): ++ @patch_sleep ++ def test_monitor_thread(self): ++ """Test dummy monitoring thread""" ++ monitor = TMonitor(FakeTqdm, 10) ++ # Test if alive, then killed ++ assert monitor.report() ++ monitor.exit() ++ assert not monitor.report() ++ assert not monitor.is_alive() ++ del monitor ++ ++ @patch_sleep ++ def test_monitoring_and_cleanup(self): ++ """Test for stalled tqdm instance and monitor deletion""" ++ # Note: should fix miniters for these tests, else with dynamic_miniters ++ # it's too complicated to handle with monitoring update and ++ # maxinterval... ++ maxinterval = tqdm.monitor_interval ++ assert maxinterval == 10 ++ total = 1000 ++ ++ with closing(StringIO()) as our_file: + with tqdm(total=total, file=our_file, miniters=500, mininterval=0.1, +- maxinterval=1E5) as t2: +- cpu_timify(t1, Time) +- cpu_timify(t2, Time) ++ maxinterval=maxinterval) as t: ++ cpu_timify(t, Time) + # Do a lot of iterations in a small timeframe +- Time.fake_sleep(maxinterval / 10) +- t1.update(500) +- t2.update(500) +- assert t1.miniters <= 500 # TODO: should really be == 500 +- assert t2.miniters == 500 ++ # (smaller than monitor interval) ++ Time.fake_sleep(maxinterval / 10) # monitor won't wake up ++ t.update(500) ++ # check that our fixed miniters is still there ++ assert t.miniters <= 500 # TODO: should really be == 500 + # Then do 1 it after monitor interval, so that monitor kicks in + Time.fake_sleep(maxinterval) +- t1.update(1) +- t2.update(1) +- # Wait for the monitor to get out of sleep and update tqdm ++ t.update(1) ++ # Wait for the monitor to get out of sleep's loop and update ++ # tqdm.. + timeend = Time.time() +- while not (t1.monitor.woken >= timeend and t1.miniters == 1): +- Time.fake_sleep(1) +- assert t1.miniters == 1 # check that monitor corrected miniters +- assert t2.miniters == 500 # check that t2 was not adjusted +- +- +-@with_setup(pretest, posttest) +-def test_imap(): +- """Test multiprocessing.Pool""" +- try: +- from multiprocessing import Pool +- except ImportError: +- raise SkipTest +- +- pool = Pool() +- res = list(tqdm(pool.imap(incr, range(100)), disable=True)) +- assert res[-1] == 100 +- +- +-# py2: locks won't propagate to incr_bar so may cause `AttributeError` +-@with_setup(pretest, posttest) +-@retry_on_except(n=3 if sys.version_info < (3,) else 1, check_cpu_time=False) +-@patch_lock(thread=True) +-def test_threadpool(): +- """Test concurrent.futures.ThreadPoolExecutor""" +- try: +- from concurrent.futures import ThreadPoolExecutor +- except ImportError: +- raise SkipTest +- +- with ThreadPoolExecutor(8) as pool: ++ while not (t.monitor.woken >= timeend and t.miniters == 1): ++ Time.fake_sleep(1) # Force awake up if it woken too soon ++ assert t.miniters == 1 # check that monitor corrected miniters ++ # Note: at this point, there may be a race condition: monitor ++ # saved current woken time but Time.sleep() happen just before ++ # monitor sleep. To fix that, either sleep here or increase time ++ # in a loop to ensure that monitor wakes up at some point. ++ ++ # Try again but already at miniters = 1 so nothing will be done ++ Time.fake_sleep(maxinterval) ++ t.update(2) ++ timeend = Time.time() ++ while t.monitor.woken < timeend: ++ Time.fake_sleep(1) # Force awake if it woken too soon ++ # Wait for the monitor to get out of sleep's loop and update ++ # tqdm ++ assert t.miniters == 1 # check that monitor corrected miniters ++ ++ @patch_sleep ++ def test_monitoring_multi(self): ++ """Test on multiple bars, one not needing miniters adjustment""" ++ # Note: should fix miniters for these tests, else with dynamic_miniters ++ # it's too complicated to handle with monitoring update and ++ # maxinterval... ++ maxinterval = tqdm.monitor_interval ++ assert maxinterval == 10 ++ total = 1000 ++ ++ with closing(StringIO()) as our_file: ++ with tqdm(total=total, file=our_file, miniters=500, mininterval=0.1, ++ maxinterval=maxinterval) as t1: ++ # Set high maxinterval for t2 so monitor does not need to adjust ++ # it ++ with tqdm(total=total, file=our_file, miniters=500, ++ mininterval=0.1, maxinterval=1E5) as t2: ++ cpu_timify(t1, Time) ++ cpu_timify(t2, Time) ++ # Do a lot of iterations in a small timeframe ++ Time.fake_sleep(maxinterval / 10) ++ t1.update(500) ++ t2.update(500) ++ assert t1.miniters <= 500 # TODO: should really be == 500 ++ assert t2.miniters == 500 ++ # Then do 1 it after monitor interval, so that monitor ++ # kicks in ++ Time.fake_sleep(maxinterval) ++ t1.update(1) ++ t2.update(1) ++ # Wait for the monitor to get out of sleep and update tqdm ++ timeend = Time.time() ++ while not (t1.monitor.woken ++ >= timeend and t1.miniters == 1): ++ Time.fake_sleep(1) ++ # check that monitor corrected miniters ++ assert t1.miniters == 1 ++ assert t2.miniters == 500 # check that t2 was not adjusted ++ ++ def test_imap(self): ++ """Test multiprocessing.Pool""" ++ try: ++ from multiprocessing import Pool ++ except ImportError: ++ raise unittest.SkipTest ++ ++ pool = Pool() ++ res = list(tqdm(pool.imap(incr, range(100)), disable=True)) ++ assert res[-1] == 100 ++ ++ # py2: locks won't propagate to incr_bar so may cause `AttributeError` ++ @retry_on_except(n=3 if sys.version_info < (3,) else 1, ++ check_cpu_time=False) ++ @patch_lock(thread=True) ++ def test_threadpool(self): ++ """Test concurrent.futures.ThreadPoolExecutor""" + try: +- res = list(tqdm(pool.map(incr_bar, range(100)), disable=True)) +- except AttributeError: +- if sys.version_info < (3,): +- raise SkipTest +- else: +- raise +- assert sum(res) == sum(range(1, 101)) ++ from concurrent.futures import ThreadPoolExecutor ++ except ImportError: ++ raise unittest.SkipTest ++ ++ with ThreadPoolExecutor(8) as pool: ++ try: ++ res = list(tqdm(pool.map(incr_bar, range(100)), disable=True)) ++ except AttributeError: ++ if sys.version_info < (3,): ++ raise unittest.SkipTest ++ else: ++ raise ++ assert sum(res) == sum(range(1, 101)) +diff --git a/tqdm/tests/tests_tqdm.py b/tqdm/tests/tests_tqdm.py +index af2f2601..2eb8eef1 100644 +--- a/tqdm/tests/tests_tqdm.py ++++ b/tqdm/tests/tests_tqdm.py +@@ -6,11 +6,9 @@ + import csv + import re + import os ++import pytest ++import unittest + from functools import wraps +-from nose import with_setup +-from nose.plugins.skip import SkipTest +-from nose.tools import assert_raises +-from nose.tools import eq_ + from contextlib import contextmanager + from warnings import catch_warnings, simplefilter + +@@ -122,30 +120,6 @@ def cpu_timify(t, timer=None): + return timer + + +-def pretest(): +- # setcheckinterval is deprecated +- try: +- sys.setswitchinterval(1) +- except AttributeError: +- sys.setcheckinterval(100) +- +- if getattr(tqdm, "_instances", False): +- n = len(tqdm._instances) +- if n: +- tqdm._instances.clear() +- raise EnvironmentError( +- "{0} `tqdm` instances still in existence PRE-test".format(n)) +- +- +-def posttest(): +- if getattr(tqdm, "_instances", False): +- n = len(tqdm._instances) +- if n: +- tqdm._instances.clear() +- raise EnvironmentError( +- "{0} `tqdm` instances still in existence POST-test".format(n)) +- +- + class UnicodeIO(IOBase): + """Unicode version of StringIO""" + +@@ -232,1812 +206,1735 @@ def squash_ctrlchars(s): + return lines + + +-def test_format_interval(): +- """Test time interval format""" +- format_interval = tqdm.format_interval +- +- assert format_interval(60) == '01:00' +- assert format_interval(6160) == '1:42:40' +- assert format_interval(238113) == '66:08:33' ++def _rlock_creation_target(): ++ """Check that the RLock has not been constructed.""" ++ from unittest.mock import patch ++ import multiprocessing as mp + ++ # Patch the RLock class/method but use the original implementation ++ with patch('multiprocessing.RLock', wraps=mp.RLock) as rlock_mock: ++ # Importing the module should not create a lock ++ from tqdm import tqdm ++ assert rlock_mock.call_count == 0 ++ # Creating a progress bar should initialize the lock ++ with closing(StringIO()) as our_file: ++ with tqdm(file=our_file) as _: # NOQA ++ pass ++ assert rlock_mock.call_count == 1 ++ # Creating a progress bar again should reuse the lock ++ with closing(StringIO()) as our_file: ++ with tqdm(file=our_file) as _: # NOQA ++ pass ++ assert rlock_mock.call_count == 1 + +-def test_format_num(): +- """Test number format""" +- format_num = tqdm.format_num + +- assert float(format_num(1337)) == 1337 +- assert format_num(int(1e6)) == '1e+6' +- assert format_num(1239876) == '1''239''876' ++@contextmanager ++def std_out_err_redirect_tqdm(tqdm_file=sys.stderr): ++ orig_out_err = sys.stdout, sys.stderr ++ try: ++ sys.stdout = sys.stderr = DummyTqdmFile(tqdm_file) ++ yield orig_out_err[0] ++ # Relay exceptions ++ except Exception as exc: ++ raise exc ++ # Always restore sys.stdout/err if necessary ++ finally: ++ sys.stdout, sys.stderr = orig_out_err + + +-def test_format_meter(): +- """Test statistics and progress bar formatting""" ++def patch_lock(thread=True): ++ """decorator replacing tqdm's lock with vanilla threading/multiprocessing""" + try: +- unich = unichr +- except NameError: +- unich = chr +- +- format_meter = tqdm.format_meter +- +- assert format_meter(0, 1000, 13) == \ +- " 0%| | 0/1000 [00:13= (3,): +- assert format_meter(0, 1000, 13, ncols=68, prefix='fullwidth: ') == \ +- "fullwidth: 0%| | 0/1000 [00:13= (3,): ++# assert format_meter(0, 1000, 13, ncols=68, ++# prefix='fullwidth: ') == \ ++# "fullwidth: 0%|" \ ++# " | 0/1000 [00:135.2f}", ++ postfix=[dict(name="foo"), 42]) as t: ++ for i in range(10): ++ if i % 2: ++ t.postfix[0]["name"] = "abcdefghij"[i] ++ else: ++ t.postfix[1] = i ++ t.update() ++ res = our_file.getvalue() ++ assert "f 6.00" in res ++ assert "h 6.00" in res ++ assert "h 8.00" in res ++ assert "j 8.00" in res ++ ++ ++class TestTqdmWithSetUp(TestWithInstancesCheck): ++ def test_all_defaults(self): ++ """Test default kwargs""" ++ with closing(UnicodeIO()) as our_file: ++ with tqdm(range(10), file=our_file) as progressbar: ++ assert len(progressbar) == 10 ++ for _ in progressbar: ++ pass ++ # restore stdout/stderr output for `nosetest` interface ++ # try: ++ # sys.stderr.write('\x1b[A') ++ # except: ++ # pass ++ sys.stderr.write('\rTest default kwargs ... ') ++ ++ def test_native_string_io_for_default_file(self): ++ """Native strings written to unspecified files""" ++ stderr = sys.stderr ++ try: ++ sys.stderr = WriteTypeChecker(expected_type=type('')) ++ for _ in tqdm(range(3)): + pass +- # restore stdout/stderr output for `nosetest` interface +- # try: +- # sys.stderr.write('\x1b[A') +- # except: +- # pass +- sys.stderr.write('\rTest default kwargs ... ') +- +- +-class WriteTypeChecker(BytesIO): +- """File-like to assert the expected type is written""" +- def __init__(self, expected_type): +- super(WriteTypeChecker, self).__init__() +- self.expected_type = expected_type +- +- def write(self, s): +- assert isinstance(s, self.expected_type) +- ++ sys.stderr.encoding = None # py2 behaviour ++ for _ in tqdm(range(3)): ++ pass ++ finally: ++ sys.stderr = stderr + +-@with_setup(pretest, posttest) +-def test_native_string_io_for_default_file(): +- """Native strings written to unspecified files""" +- stderr = sys.stderr +- try: +- sys.stderr = WriteTypeChecker(expected_type=type('')) +- for _ in tqdm(range(3)): +- pass +- sys.stderr.encoding = None # py2 behaviour +- for _ in tqdm(range(3)): ++ def test_unicode_string_io_for_specified_file(self): ++ """Unicode strings written to specified files""" ++ for _ in tqdm(range(3), file=WriteTypeChecker(expected_type=type(u''))): + pass +- finally: +- sys.stderr = stderr +- + +-@with_setup(pretest, posttest) +-def test_unicode_string_io_for_specified_file(): +- """Unicode strings written to specified files""" +- for _ in tqdm(range(3), file=WriteTypeChecker(expected_type=type(u''))): +- pass +- +- +-@with_setup(pretest, posttest) +-def test_write_bytes(): +- """Test write_bytes argument with and without `file`""" +- # specified file (and bytes) +- for _ in tqdm(range(3), file=WriteTypeChecker(expected_type=type(b'')), +- write_bytes=True): +- pass +- # unspecified file (and unicode) +- stderr = sys.stderr +- try: +- sys.stderr = WriteTypeChecker(expected_type=type(u'')) +- for _ in tqdm(range(3), write_bytes=False): ++ def test_write_bytes(self): ++ """Test write_bytes argument with and without `file`""" ++ # specified file (and bytes) ++ for _ in tqdm(range(3), file=WriteTypeChecker(expected_type=type(b'')), ++ write_bytes=True): + pass +- finally: +- sys.stderr = stderr +- ++ # unspecified file (and unicode) ++ stderr = sys.stderr ++ try: ++ sys.stderr = WriteTypeChecker(expected_type=type(u'')) ++ for _ in tqdm(range(3), write_bytes=False): ++ pass ++ finally: ++ sys.stderr = stderr + +-@with_setup(pretest, posttest) +-def test_iterate_over_csv_rows(): +- """Test csv iterator""" +- # Create a test csv pseudo file +- with closing(StringIO()) as test_csv_file: +- writer = csv.writer(test_csv_file) +- for _ in _range(3): +- writer.writerow(['test'] * 3) +- test_csv_file.seek(0) ++ def test_iterate_over_csv_rows(self): ++ """Test csv iterator""" ++ # Create a test csv pseudo file ++ with closing(StringIO()) as test_csv_file: ++ writer = csv.writer(test_csv_file) ++ for _ in _range(3): ++ writer.writerow(['test'] * 3) ++ test_csv_file.seek(0) ++ ++ # Test that nothing fails if we iterate over rows ++ reader = csv.DictReader(test_csv_file, ++ fieldnames=('row1', 'row2', 'row3')) ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(reader, file=our_file): ++ pass ++ ++ def test_file_output(self): ++ """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: ++ our_file.seek(0) ++ assert '0/3' in our_file.read() + +- # Test that nothing fails if we iterate over rows +- reader = csv.DictReader(test_csv_file, +- fieldnames=('row1', 'row2', 'row3')) ++ def test_leave_option(self): ++ """Test `leave=True` always prints info about the last iteration""" + with closing(StringIO()) as our_file: +- for _ in tqdm(reader, file=our_file): ++ for _ in tqdm(_range(3), file=our_file, leave=True): + pass ++ res = our_file.getvalue() ++ assert '| 3/3 ' in res ++ assert '\n' == res[-1] # not '\r' + +- +-@with_setup(pretest, posttest) +-def test_file_output(): +- """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: +- our_file.seek(0) +- assert '0/3' in our_file.read() +- +- +-@with_setup(pretest, posttest) +-def test_leave_option(): +- """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 +- res = our_file.getvalue() +- assert '| 3/3 ' in res +- assert '\n' == res[-1] # not '\r' +- +- with closing(StringIO()) as our_file2: +- for _ in tqdm(_range(3), file=our_file2, leave=False): +- pass +- assert '| 3/3 ' not in our_file2.getvalue() +- +- +-@with_setup(pretest, posttest) +-def test_trange(): +- """Test trange""" +- with closing(StringIO()) as our_file: +- for _ in trange(3, file=our_file, leave=True): +- pass +- assert '| 3/3 ' in our_file.getvalue() +- +- with closing(StringIO()) as our_file2: +- for _ in trange(3, file=our_file2, leave=False): +- pass +- assert '| 3/3 ' not in our_file2.getvalue() +- +- +-@with_setup(pretest, posttest) +-def test_min_interval(): +- """Test mininterval""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, mininterval=1e-10): +- pass +- assert " 0%| | 0/3 [00:00<" in our_file.getvalue() +- +- +-@with_setup(pretest, posttest) +-def test_max_interval(): +- """Test maxinterval""" +- total = 100 +- bigstep = 10 +- smallstep = 5 +- +- # Test without maxinterval +- timer = DiscreteTimer() +- with closing(StringIO()) as our_file: + with closing(StringIO()) as our_file2: +- # with maxinterval but higher than loop sleep time +- t = tqdm(total=total, file=our_file, miniters=None, mininterval=0, +- smoothing=1, maxinterval=1e-2) +- cpu_timify(t, timer) ++ for _ in tqdm(_range(3), file=our_file2, leave=False): ++ pass ++ assert '| 3/3 ' not in our_file2.getvalue() + +- # without maxinterval +- t2 = tqdm(total=total, file=our_file2, miniters=None, mininterval=0, +- smoothing=1, maxinterval=None) +- cpu_timify(t2, timer) ++ def test_trange(self): ++ """Test trange""" ++ with closing(StringIO()) as our_file: ++ for _ in trange(3, file=our_file, leave=True): ++ pass ++ assert '| 3/3 ' in our_file.getvalue() + +- assert t.dynamic_miniters +- assert t2.dynamic_miniters +- +- # Increase 10 iterations at once +- t.update(bigstep) +- t2.update(bigstep) +- # The next iterations should not trigger maxinterval (step 10) +- for _ in _range(4): +- t.update(smallstep) +- t2.update(smallstep) +- timer.sleep(1e-5) +- t.close() # because PyPy doesn't gc immediately +- t2.close() # as above +- +- assert "25%" not in our_file2.getvalue() +- assert "25%" not in our_file.getvalue() +- +- # Test with maxinterval effect +- timer = DiscreteTimer() +- with closing(StringIO()) as our_file: +- with tqdm(total=total, file=our_file, miniters=None, mininterval=0, +- smoothing=1, maxinterval=1e-4) as t: +- cpu_timify(t, timer) ++ with closing(StringIO()) as our_file2: ++ for _ in trange(3, file=our_file2, leave=False): ++ pass ++ assert '| 3/3 ' not in our_file2.getvalue() + +- # Increase 10 iterations at once +- t.update(bigstep) +- # The next iterations should trigger maxinterval (step 5) +- for _ in _range(4): +- t.update(smallstep) +- timer.sleep(1e-2) ++ def test_min_interval(self): ++ """Test mininterval""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, mininterval=1e-10): ++ pass ++ assert " 0%| | 0/3 [00:00<" in our_file.getvalue() + +- assert "25%" in our_file.getvalue() ++ def test_max_interval(self): ++ """Test maxinterval""" ++ total = 100 ++ bigstep = 10 ++ smallstep = 5 + +- # Test iteration based tqdm with maxinterval effect +- timer = DiscreteTimer() +- with closing(StringIO()) as our_file: +- with tqdm(_range(total), file=our_file, miniters=None, +- mininterval=1e-5, smoothing=1, maxinterval=1e-4) as t2: +- cpu_timify(t2, timer) ++ # Test without maxinterval ++ timer = DiscreteTimer() ++ with closing(StringIO()) as our_file: ++ with closing(StringIO()) as our_file2: ++ # with maxinterval but higher than loop sleep time ++ t = tqdm(total=total, file=our_file, miniters=None, ++ mininterval=0, smoothing=1, maxinterval=1e-2) ++ cpu_timify(t, timer) ++ ++ # without maxinterval ++ t2 = tqdm(total=total, file=our_file2, miniters=None, ++ mininterval=0, smoothing=1, maxinterval=None) ++ cpu_timify(t2, timer) + +- for i in t2: +- if i >= (bigstep - 1) and \ +- ((i - (bigstep - 1)) % smallstep) == 0: +- timer.sleep(1e-2) +- if i >= 3 * bigstep: +- break +- +- assert "15%" in our_file.getvalue() +- +- # Test different behavior with and without mininterval +- timer = DiscreteTimer() +- total = 1000 +- mininterval = 0.1 +- maxinterval = 10 +- with closing(StringIO()) as our_file: +- with tqdm(total=total, file=our_file, miniters=None, smoothing=1, +- mininterval=mininterval, maxinterval=maxinterval) as tm1: +- with tqdm(total=total, file=our_file, miniters=None, smoothing=1, +- mininterval=0, maxinterval=maxinterval) as tm2: +- +- cpu_timify(tm1, timer) +- cpu_timify(tm2, timer) +- +- # Fast iterations, check if dynamic_miniters triggers +- timer.sleep(mininterval) # to force update for t1 +- tm1.update(total / 2) +- tm2.update(total / 2) +- assert int(tm1.miniters) == tm2.miniters == total / 2 +- +- # Slow iterations, check different miniters if mininterval +- timer.sleep(maxinterval * 2) +- tm1.update(total / 2) +- tm2.update(total / 2) +- res = [tm1.miniters, tm2.miniters] +- assert res == [(total / 2) * mininterval / (maxinterval * 2), +- (total / 2) * maxinterval / (maxinterval * 2)] +- +- # Same with iterable based tqdm +- timer1 = DiscreteTimer() # need 2 timers for each bar because zip not work +- timer2 = DiscreteTimer() +- total = 100 +- mininterval = 0.1 +- maxinterval = 10 +- with closing(StringIO()) as our_file: +- t1 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1, +- mininterval=mininterval, maxinterval=maxinterval) +- t2 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1, +- mininterval=0, maxinterval=maxinterval) +- +- cpu_timify(t1, timer1) +- cpu_timify(t2, timer2) +- +- for i in t1: +- if i == ((total / 2) - 2): +- timer1.sleep(mininterval) +- if i == (total - 1): +- timer1.sleep(maxinterval * 2) +- +- for i in t2: +- if i == ((total / 2) - 2): +- timer2.sleep(mininterval) +- if i == (total - 1): +- timer2.sleep(maxinterval * 2) +- +- assert t1.miniters == 0.255 +- assert t2.miniters == 0.5 ++ assert t.dynamic_miniters ++ assert t2.dynamic_miniters + +- t1.close() +- t2.close() ++ # Increase 10 iterations at once ++ t.update(bigstep) ++ t2.update(bigstep) ++ # The next iterations should not trigger maxinterval (step 10) ++ for _ in _range(4): ++ t.update(smallstep) ++ t2.update(smallstep) ++ timer.sleep(1e-5) ++ t.close() # because PyPy doesn't gc immediately ++ t2.close() # as above + ++ assert "25%" not in our_file2.getvalue() ++ assert "25%" not in our_file.getvalue() + +-@with_setup(pretest, posttest) +-def test_min_iters(): +- """Test miniters""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, +- miniters=2): +- pass ++ # Test with maxinterval effect ++ timer = DiscreteTimer() ++ with closing(StringIO()) as our_file: ++ with tqdm(total=total, file=our_file, miniters=None, mininterval=0, ++ smoothing=1, maxinterval=1e-4) as t: ++ cpu_timify(t, timer) ++ ++ # Increase 10 iterations at once ++ t.update(bigstep) ++ # The next iterations should trigger maxinterval (step 5) ++ for _ in _range(4): ++ t.update(smallstep) ++ timer.sleep(1e-2) + +- out = our_file.getvalue() +- assert '| 0/3 ' in out +- assert '| 1/3 ' not in out +- assert '| 2/3 ' in out +- assert '| 3/3 ' in out +- +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, +- miniters=1): +- pass ++ assert "25%" in our_file.getvalue() + +- out = our_file.getvalue() +- assert '| 0/3 ' in out +- assert '| 1/3 ' in out +- assert '| 2/3 ' in out +- assert '| 3/3 ' in out ++ # Test iteration based tqdm with maxinterval effect ++ timer = DiscreteTimer() ++ with closing(StringIO()) as our_file: ++ with tqdm(_range(total), file=our_file, miniters=None, ++ mininterval=1e-5, smoothing=1, maxinterval=1e-4) as t2: ++ cpu_timify(t2, timer) + ++ for i in t2: ++ if i >= (bigstep - 1) and \ ++ ((i - (bigstep - 1)) % smallstep) == 0: ++ timer.sleep(1e-2) ++ if i >= 3 * bigstep: ++ break + +-@with_setup(pretest, posttest) +-def test_dynamic_min_iters(): +- """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, +- smoothing=1) ++ assert "15%" in our_file.getvalue() + +- t.update() +- # Increase 3 iterations +- t.update(3) +- # The next two iterations should be skipped because of dynamic_miniters +- t.update() +- t.update() +- # The third iteration should be displayed +- t.update() ++ # Test different behavior with and without mininterval ++ timer = DiscreteTimer() ++ total = 1000 ++ mininterval = 0.1 ++ maxinterval = 10 ++ with closing(StringIO()) as our_file: ++ with tqdm(total=total, file=our_file, miniters=None, smoothing=1, ++ mininterval=mininterval, maxinterval=maxinterval) as tm1: ++ with tqdm(total=total, file=our_file, ++ miniters=None, smoothing=1, ++ mininterval=0, maxinterval=maxinterval) as tm2: ++ ++ cpu_timify(tm1, timer) ++ cpu_timify(tm2, timer) ++ ++ # Fast iterations, check if dynamic_miniters triggers ++ timer.sleep(mininterval) # to force update for t1 ++ tm1.update(total / 2) ++ tm2.update(total / 2) ++ assert int(tm1.miniters) == tm2.miniters == total / 2 ++ ++ # Slow iterations, check different miniters if mininterval ++ timer.sleep(maxinterval * 2) ++ tm1.update(total / 2) ++ tm2.update(total / 2) ++ res = [tm1.miniters, tm2.miniters] ++ assert res == \ ++ [(total / 2) * mininterval / (maxinterval * 2), ++ (total / 2) * maxinterval / (maxinterval * 2)] ++ ++ # Same with iterable based tqdm ++ # need 2 timers for each bar because zip not work ++ timer1 = DiscreteTimer() ++ timer2 = DiscreteTimer() ++ total = 100 ++ mininterval = 0.1 ++ maxinterval = 10 ++ with closing(StringIO()) as our_file: ++ t1 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1, ++ mininterval=mininterval, maxinterval=maxinterval) ++ t2 = tqdm(_range(total), file=our_file, miniters=None, smoothing=1, ++ mininterval=0, maxinterval=maxinterval) + +- out = our_file.getvalue() +- assert t.dynamic_miniters +- t.__del__() # simulate immediate del gc ++ cpu_timify(t1, timer1) ++ cpu_timify(t2, timer2) + +- assert ' 0%| | 0/10 [00:00<' in out +- assert '40%' in out +- assert '50%' not in out +- assert '60%' not in out +- assert '70%' in out ++ for i in t1: ++ if i == ((total / 2) - 2): ++ timer1.sleep(mininterval) ++ if i == (total - 1): ++ timer1.sleep(maxinterval * 2) + +- # Check with smoothing=0, miniters should be set to max update seen so far +- with closing(StringIO()) as our_file: +- total = 10 +- t = tqdm(total=total, file=our_file, miniters=None, mininterval=0, +- smoothing=0) ++ for i in t2: ++ if i == ((total / 2) - 2): ++ timer2.sleep(mininterval) ++ if i == (total - 1): ++ timer2.sleep(maxinterval * 2) + +- t.update() +- t.update(2) +- t.update(5) # this should be stored as miniters +- t.update(1) ++ assert t1.miniters == 0.255 ++ assert t2.miniters == 0.5 + +- out = our_file.getvalue() +- assert all(i in out for i in ("0/10", "1/10", "3/10")) +- assert "2/10" not in out +- assert t.dynamic_miniters and not t.smoothing +- assert t.miniters == 5 +- t.close() ++ t1.close() ++ t2.close() + +- # Check iterable based tqdm +- with closing(StringIO()) as our_file: +- t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None, +- smoothing=0.5) +- for _ in t: +- pass +- assert t.dynamic_miniters ++ def test_min_iters(self): ++ """Test miniters""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, ++ miniters=2): ++ pass + +- # No smoothing +- with closing(StringIO()) as our_file: +- t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None, +- smoothing=0) +- for _ in t: +- pass +- assert t.dynamic_miniters ++ out = our_file.getvalue() ++ assert '| 0/3 ' in out ++ assert '| 1/3 ' not in out ++ assert '| 2/3 ' in out ++ assert '| 3/3 ' in out + +- # No dynamic_miniters (miniters is fixed manually) +- with closing(StringIO()) as our_file: +- t = tqdm(_range(10), file=our_file, miniters=1, mininterval=None) +- for _ in t: +- pass +- assert not t.dynamic_miniters ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, leave=True, mininterval=0, ++ miniters=1): ++ pass + ++ out = our_file.getvalue() ++ assert '| 0/3 ' in out ++ assert '| 1/3 ' in out ++ assert '| 2/3 ' in out ++ assert '| 3/3 ' in out + +-@with_setup(pretest, posttest) +-def test_big_min_interval(): +- """Test large mininterval""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(2), file=our_file, mininterval=1E10): +- pass +- assert '50%' not in our_file.getvalue() ++ def test_dynamic_min_iters(self): ++ """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, ++ smoothing=1) + +- with closing(StringIO()) as our_file: +- with tqdm(_range(2), file=our_file, mininterval=1E10) as t: ++ t.update() ++ # Increase 3 iterations ++ t.update(3) ++ # The next two iterations should be skipped because of ++ # dynamic_miniters + t.update() + t.update() +- assert '50%' not in our_file.getvalue() ++ # The third iteration should be displayed ++ t.update() + ++ out = our_file.getvalue() ++ assert t.dynamic_miniters ++ t.__del__() # simulate immediate del gc + +-@with_setup(pretest, posttest) +-def test_smoothed_dynamic_min_iters(): +- """Test smoothed dynamic miniters""" +- timer = DiscreteTimer() ++ assert ' 0%| | 0/10 [00:00<' in out ++ assert '40%' in out ++ assert '50%' not in out ++ assert '60%' not in out ++ assert '70%' in out + +- with closing(StringIO()) as our_file: +- with tqdm(total=100, file=our_file, miniters=None, mininterval=0, +- smoothing=0.5, maxinterval=0) as t: +- cpu_timify(t, timer) ++ # Check with smoothing=0, miniters should be set to max update ++ # seen so far ++ with closing(StringIO()) as our_file: ++ total = 10 ++ t = tqdm(total=total, file=our_file, miniters=None, mininterval=0, ++ smoothing=0) + +- # Increase 10 iterations at once +- t.update(10) +- # The next iterations should be partially skipped +- for _ in _range(2): +- t.update(4) +- for _ in _range(20): +- t.update() ++ t.update() ++ t.update(2) ++ t.update(5) # this should be stored as miniters ++ t.update(1) + + out = our_file.getvalue() +- assert t.dynamic_miniters +- assert ' 0%| | 0/100 [00:00<' in out +- assert '10%' in out +- assert '14%' not in out +- assert '18%' in out +- assert '20%' not in out +- assert '25%' in out +- assert '30%' not in out +- assert '32%' in out +- +- +-@with_setup(pretest, posttest) +-def test_smoothed_dynamic_min_iters_with_min_interval(): +- """Test smoothed dynamic miniters with mininterval""" +- timer = DiscreteTimer() +- +- # In this test, `miniters` should gradually decline +- total = 100 +- +- with closing(StringIO()) as our_file: +- # Test manual updating tqdm +- with tqdm(total=total, file=our_file, miniters=None, mininterval=1e-3, +- smoothing=1, maxinterval=0) as t: +- cpu_timify(t, timer) ++ assert all(i in out for i in ("0/10", "1/10", "3/10")) ++ assert "2/10" not in out ++ assert t.dynamic_miniters and not t.smoothing ++ assert t.miniters == 5 ++ t.close() + +- t.update(10) +- timer.sleep(1e-2) +- for _ in _range(4): +- t.update() +- timer.sleep(1e-2) +- out = our_file.getvalue() ++ # Check iterable based tqdm ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None, ++ smoothing=0.5) ++ for _ in t: ++ pass + assert t.dynamic_miniters + +- with closing(StringIO()) as our_file: +- # Test iteration-based tqdm +- with tqdm(_range(total), file=our_file, miniters=None, +- mininterval=0.01, smoothing=1, maxinterval=0) as t2: +- cpu_timify(t2, timer) ++ # No smoothing ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(10), file=our_file, miniters=None, mininterval=None, ++ smoothing=0) ++ for _ in t: ++ pass ++ assert t.dynamic_miniters + +- for i in t2: +- if i >= 10: +- timer.sleep(0.1) +- if i >= 14: +- break +- out2 = our_file.getvalue() +- +- assert t.dynamic_miniters +- assert ' 0%| | 0/100 [00:00<' in out +- assert '11%' in out and '11%' in out2 +- # assert '12%' not in out and '12%' in out2 +- assert '13%' in out and '13%' in out2 +- assert '14%' in out and '14%' in out2 +- +- +-@with_setup(pretest, posttest) +-def test_rlock_creation(): +- """Test that importing tqdm does not create multiprocessing objects.""" +- import multiprocessing as mp +- if sys.version_info < (3, 3): +- # unittest.mock is a 3.3+ feature +- raise SkipTest ++ # No dynamic_miniters (miniters is fixed manually) ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(10), file=our_file, miniters=1, mininterval=None) ++ for _ in t: ++ pass ++ assert not t.dynamic_miniters + +- # Use 'spawn' instead of 'fork' so that the process does not inherit any +- # globals that have been constructed by running other tests +- ctx = mp.get_context('spawn') +- with ctx.Pool(1) as pool: +- # The pool will propagate the error if the target method fails +- pool.apply(_rlock_creation_target) ++ def test_big_min_interval(self): ++ """Test large mininterval""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(2), file=our_file, mininterval=1E10): ++ pass ++ assert '50%' not in our_file.getvalue() + ++ with closing(StringIO()) as our_file: ++ with tqdm(_range(2), file=our_file, mininterval=1E10) as t: ++ t.update() ++ t.update() ++ assert '50%' not in our_file.getvalue() + +-def _rlock_creation_target(): +- """Check that the RLock has not been constructed.""" +- from unittest.mock import patch +- import multiprocessing as mp ++ def test_smoothed_dynamic_min_iters(self): ++ """Test smoothed dynamic miniters""" ++ timer = DiscreteTimer() + +- # Patch the RLock class/method but use the original implementation +- with patch('multiprocessing.RLock', wraps=mp.RLock) as rlock_mock: +- # Importing the module should not create a lock +- from tqdm import tqdm +- assert rlock_mock.call_count == 0 +- # Creating a progress bar should initialize the lock + with closing(StringIO()) as our_file: +- with tqdm(file=our_file) as _: # NOQA +- pass +- assert rlock_mock.call_count == 1 +- # Creating a progress bar again should reuse the lock +- with closing(StringIO()) as our_file: +- with tqdm(file=our_file) as _: # NOQA +- pass +- assert rlock_mock.call_count == 1 ++ with tqdm(total=100, file=our_file, miniters=None, mininterval=0, ++ smoothing=0.5, maxinterval=0) as t: ++ cpu_timify(t, timer) ++ ++ # Increase 10 iterations at once ++ t.update(10) ++ # The next iterations should be partially skipped ++ for _ in _range(2): ++ t.update(4) ++ for _ in _range(20): ++ t.update() + ++ out = our_file.getvalue() ++ assert t.dynamic_miniters ++ assert ' 0%| | 0/100 [00:00<' in out ++ assert '10%' in out ++ assert '14%' not in out ++ assert '18%' in out ++ assert '20%' not in out ++ assert '25%' in out ++ assert '30%' not in out ++ assert '32%' in out ++ ++ def test_smoothed_dynamic_min_iters_with_min_interval(self): ++ """Test smoothed dynamic miniters with mininterval""" ++ timer = DiscreteTimer() + +-@with_setup(pretest, posttest) +-def test_disable(): +- """Test disable""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, disable=True): +- pass +- assert our_file.getvalue() == '' ++ # In this test, `miniters` should gradually decline ++ total = 100 + +- with closing(StringIO()) as our_file: +- progressbar = tqdm(total=3, file=our_file, miniters=1, disable=True) +- progressbar.update(3) +- progressbar.close() +- assert our_file.getvalue() == '' ++ with closing(StringIO()) as our_file: ++ # Test manual updating tqdm ++ with tqdm(total=total, file=our_file, miniters=None, ++ mininterval=1e-3, smoothing=1, maxinterval=0) as t: ++ cpu_timify(t, timer) + ++ t.update(10) ++ timer.sleep(1e-2) ++ for _ in _range(4): ++ t.update() ++ timer.sleep(1e-2) ++ out = our_file.getvalue() ++ assert t.dynamic_miniters + +-@with_setup(pretest, posttest) +-def test_infinite_total(): +- """Test treatment of infinite total""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, total=float("inf")): +- pass ++ with closing(StringIO()) as our_file: ++ # Test iteration-based tqdm ++ with tqdm(_range(total), file=our_file, miniters=None, ++ mininterval=0.01, smoothing=1, maxinterval=0) as t2: ++ cpu_timify(t2, timer) + ++ for i in t2: ++ if i >= 10: ++ timer.sleep(0.1) ++ if i >= 14: ++ break ++ out2 = our_file.getvalue() + +-@with_setup(pretest, posttest) +-def test_nototal(): +- """Test unknown total length""" +- with closing(StringIO()) as our_file: +- for _ in tqdm((i for i in range(10)), file=our_file, unit_scale=10): +- pass +- assert "100it" in our_file.getvalue() ++ assert t.dynamic_miniters ++ assert ' 0%| | 0/100 [00:00<' in out ++ assert '11%' in out and '11%' in out2 ++ # assert '12%' not in out and '12%' in out2 ++ assert '13%' in out and '13%' in out2 ++ assert '14%' in out and '14%' in out2 ++ ++ def test_rlock_creation(self): ++ """Test that importing tqdm does not create multiprocessing objects.""" ++ import multiprocessing as mp ++ if sys.version_info < (3, 3): ++ # unittest.mock is a 3.3+ feature ++ raise unittest.SkipTest ++ ++ # Use 'spawn' instead of 'fork' so that the process does not inherit any ++ # globals that have been constructed by running other tests ++ ctx = mp.get_context('spawn') ++ with ctx.Pool(1) as pool: ++ # The pool will propagate the error if the target method fails ++ pool.apply(_rlock_creation_target) ++ ++ def test_disable(self): ++ """Test disable""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, disable=True): ++ pass ++ assert our_file.getvalue() == '' + +- with closing(StringIO()) as our_file: +- for _ in tqdm((i for i in range(10)), file=our_file, +- bar_format="{l_bar}{bar}{r_bar}"): +- pass +- assert "10/?" in our_file.getvalue() ++ with closing(StringIO()) as our_file: ++ progressbar = tqdm(total=3, file=our_file, miniters=1, disable=True) ++ progressbar.update(3) ++ progressbar.close() ++ assert our_file.getvalue() == '' + ++ def test_infinite_total(self): ++ """Test treatment of infinite total""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, total=float("inf")): ++ pass + +-@with_setup(pretest, posttest) +-def test_unit(): +- """Test SI unit prefix""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), file=our_file, miniters=1, unit="bytes"): +- pass +- assert 'bytes/s' in our_file.getvalue() ++ def test_nototal(self): ++ """Test unknown total length""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm((i for i in range(10)), file=our_file, unit_scale=10): ++ pass ++ assert "100it" in our_file.getvalue() + ++ with closing(StringIO()) as our_file: ++ for _ in tqdm((i for i in range(10)), file=our_file, ++ bar_format="{l_bar}{bar}{r_bar}"): ++ pass ++ assert "10/?" in our_file.getvalue() + +-@with_setup(pretest, posttest) +-def test_ascii(): +- """Test ascii/unicode bar""" +- # Test ascii autodetection +- with closing(StringIO()) as our_file: +- with tqdm(total=10, file=our_file, ascii=None) as t: +- assert t.ascii # TODO: this may fail in the future ++ def test_unit(self): ++ """Test SI unit prefix""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(3), file=our_file, miniters=1, unit="bytes"): ++ pass ++ assert 'bytes/s' in our_file.getvalue() + +- # Test ascii bar +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(3), total=15, file=our_file, miniters=1, +- mininterval=0, ascii=True): +- pass +- res = our_file.getvalue().strip("\r").split("\r") +- assert '7%|6' in res[1] +- assert '13%|#3' in res[2] +- assert '20%|##' in res[3] +- +- # Test unicode bar +- with closing(UnicodeIO()) as our_file: +- with tqdm(total=15, file=our_file, ascii=False, mininterval=0) as t: +- for _ in _range(3): +- t.update() +- res = our_file.getvalue().strip("\r").split("\r") +- assert u"7%|\u258b" in res[1] +- assert u"13%|\u2588\u258e" in res[2] +- assert u"20%|\u2588\u2588" in res[3] ++ def test_ascii(self): ++ """Test ascii/unicode bar""" ++ # Test ascii autodetection ++ with closing(StringIO()) as our_file: ++ with tqdm(total=10, file=our_file, ascii=None) as t: ++ assert t.ascii # TODO: this may fail in the future + +- # Test custom bar +- for bars in [" .oO0", " #"]: ++ # Test ascii bar + with closing(StringIO()) as our_file: +- for _ in tqdm(_range(len(bars) - 1), file=our_file, miniters=1, +- mininterval=0, ascii=bars, ncols=27): ++ for _ in tqdm(_range(3), total=15, file=our_file, miniters=1, ++ mininterval=0, ascii=True): + pass + res = our_file.getvalue().strip("\r").split("\r") +- for b, line in zip(bars, res): +- assert '|' + b + '|' in line +- +- +-@with_setup(pretest, posttest) +-def test_update(): +- """Test manual creation and updates""" +- res = None +- with closing(StringIO()) as our_file: +- with tqdm(total=2, file=our_file, miniters=1, mininterval=0) \ +- as progressbar: +- assert len(progressbar) == 2 +- progressbar.update(2) +- assert '| 2/2' in our_file.getvalue() +- progressbar.desc = 'dynamically notify of 4 increments in total' +- progressbar.total = 4 +- progressbar.update(-1) +- progressbar.update(2) +- res = our_file.getvalue() +- assert '| 3/4 ' in res +- assert 'dynamically notify of 4 increments in total' in res +- +- +-@with_setup(pretest, posttest) +-def test_close(): +- """Test manual creation and closure and n_instances""" +- +- # With `leave` option +- with closing(StringIO()) as our_file: +- progressbar = tqdm(total=3, file=our_file, miniters=10) +- progressbar.update(3) +- assert '| 3/3 ' not in our_file.getvalue() # Should be blank +- assert len(tqdm._instances) == 1 +- progressbar.close() +- assert len(tqdm._instances) == 0 +- assert '| 3/3 ' in our_file.getvalue() +- +- # Without `leave` option +- with closing(StringIO()) as our_file: +- progressbar = tqdm(total=3, file=our_file, miniters=10, leave=False) +- progressbar.update(3) +- progressbar.close() +- assert '| 3/3 ' not in our_file.getvalue() # Should be blank +- +- # With all updates +- with closing(StringIO()) as our_file: +- assert len(tqdm._instances) == 0 +- with tqdm(total=3, file=our_file, miniters=0, mininterval=0, +- leave=True) as progressbar: ++ assert '7%|6' in res[1] ++ assert '13%|#3' in res[2] ++ assert '20%|##' in res[3] ++ ++ # Test unicode bar ++ with closing(UnicodeIO()) as our_file: ++ with tqdm(total=15, file=our_file, ascii=False, mininterval=0) as t: ++ for _ in _range(3): ++ t.update() ++ res = our_file.getvalue().strip("\r").split("\r") ++ assert u"7%|\u258b" in res[1] ++ assert u"13%|\u2588\u258e" in res[2] ++ assert u"20%|\u2588\u2588" in res[3] ++ ++ # Test custom bar ++ for bars in [" .oO0", " #"]: ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(len(bars) - 1), file=our_file, miniters=1, ++ mininterval=0, ascii=bars, ncols=27): ++ pass ++ res = our_file.getvalue().strip("\r").split("\r") ++ for b, line in zip(bars, res): ++ assert '|' + b + '|' in line ++ ++ def test_update(self): ++ """Test manual creation and updates""" ++ res = None ++ with closing(StringIO()) as our_file: ++ with tqdm(total=2, file=our_file, miniters=1, mininterval=0) \ ++ as progressbar: ++ assert len(progressbar) == 2 ++ progressbar.update(2) ++ assert '| 2/2' in our_file.getvalue() ++ progressbar.desc = 'dynamically notify of 4 increments in total' ++ progressbar.total = 4 ++ progressbar.update(-1) ++ progressbar.update(2) ++ res = our_file.getvalue() ++ assert '| 3/4 ' in res ++ assert 'dynamically notify of 4 increments in total' in res ++ ++ def test_close(self): ++ """Test manual creation and closure and n_instances""" ++ ++ # With `leave` option ++ with closing(StringIO()) as our_file: ++ progressbar = tqdm(total=3, file=our_file, miniters=10) ++ progressbar.update(3) ++ assert '| 3/3 ' not in our_file.getvalue() # Should be blank + assert len(tqdm._instances) == 1 ++ progressbar.close() ++ assert len(tqdm._instances) == 0 ++ assert '| 3/3 ' in our_file.getvalue() ++ ++ # Without `leave` option ++ with closing(StringIO()) as our_file: ++ progressbar = tqdm(total=3, file=our_file, miniters=10, leave=False) + progressbar.update(3) ++ progressbar.close() ++ assert '| 3/3 ' not in our_file.getvalue() # Should be blank ++ ++ # With all updates ++ with closing(StringIO()) as our_file: ++ assert len(tqdm._instances) == 0 ++ with tqdm(total=3, file=our_file, miniters=0, mininterval=0, ++ leave=True) as progressbar: ++ assert len(tqdm._instances) == 1 ++ progressbar.update(3) ++ res = our_file.getvalue() ++ assert '| 3/3 ' in res # Should be blank ++ assert '\n' not in res ++ # close() called ++ assert len(tqdm._instances) == 0 ++ ++ exres = res.rsplit(', ', 1)[0] + res = our_file.getvalue() +- assert '| 3/3 ' in res # Should be blank +- assert '\n' not in res +- # close() called +- assert len(tqdm._instances) == 0 +- +- exres = res.rsplit(', ', 1)[0] +- res = our_file.getvalue() +- assert res[-1] == '\n' +- if not res.startswith(exres): +- raise AssertionError( +- "\n<<< Expected:\n{0}\n>>> Got:\n{1}\n===".format( +- exres + ', ...it/s]\n', our_file.getvalue())) +- +- # Closing after the output stream has closed +- with closing(StringIO()) as our_file: +- t = tqdm(total=2, file=our_file) +- t.update() +- t.update() +- t.close() ++ assert res[-1] == '\n' ++ if not res.startswith(exres): ++ raise AssertionError( ++ "\n<<< Expected:\n{0}\n>>> Got:\n{1}\n===".format( ++ exres + ', ...it/s]\n', our_file.getvalue())) + ++ # Closing after the output stream has closed ++ with closing(StringIO()) as our_file: ++ t = tqdm(total=2, file=our_file) ++ t.update() ++ t.update() ++ t.close() + +-@with_setup(pretest, posttest) +-def test_smoothing(): +- """Test exponential weighted average smoothing""" +- timer = DiscreteTimer() ++ def test_smoothing(self): ++ """Test exponential weighted average smoothing""" ++ timer = DiscreteTimer() + +- # -- Test disabling smoothing +- with closing(StringIO()) as our_file: +- with tqdm(_range(3), file=our_file, smoothing=None, leave=True) as t: +- cpu_timify(t, timer) ++ # -- Test disabling smoothing ++ with closing(StringIO()) as our_file: ++ with tqdm(_range(3), file=our_file, smoothing=None, ++ leave=True) as t: ++ cpu_timify(t, timer) + +- for _ in t: +- pass +- assert '| 3/3 ' in our_file.getvalue() ++ for _ in t: ++ pass ++ assert '| 3/3 ' in our_file.getvalue() + +- # -- Test smoothing +- # Compile the regex to find the rate +- # 1st case: no smoothing (only use average) +- with closing(StringIO()) as our_file2: +- with closing(StringIO()) as our_file: +- t = tqdm(_range(3), file=our_file2, smoothing=None, leave=True, +- miniters=1, mininterval=0) +- cpu_timify(t, timer) ++ # -- Test smoothing ++ # Compile the regex to find the rate ++ # 1st case: no smoothing (only use average) ++ with closing(StringIO()) as our_file2: ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(3), file=our_file2, smoothing=None, leave=True, ++ miniters=1, mininterval=0) ++ cpu_timify(t, timer) ++ ++ with tqdm(_range(3), file=our_file, smoothing=None, leave=True, ++ miniters=1, mininterval=0) as t2: ++ cpu_timify(t2, timer) ++ ++ for i in t2: ++ # Sleep more for first iteration and ++ # see how quickly rate is updated ++ if i == 0: ++ timer.sleep(0.01) ++ else: ++ # Need to sleep in all iterations ++ # to calculate smoothed rate ++ # (else delta_t is 0!) ++ timer.sleep(0.001) ++ t.update() ++ n_old = len(tqdm._instances) ++ t.close() ++ assert len(tqdm._instances) == n_old - 1 ++ # Get result for iter-based bar ++ a = progressbar_rate(get_bar(our_file.getvalue(), 3)) ++ # Get result for manually updated bar ++ a2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) ++ ++ # 2nd case: use max smoothing (= instant rate) ++ with closing(StringIO()) as our_file2: ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(3), file=our_file2, smoothing=1, leave=True, ++ miniters=1, mininterval=0) ++ cpu_timify(t, timer) ++ ++ with tqdm(_range(3), file=our_file, smoothing=1, leave=True, ++ miniters=1, mininterval=0) as t2: ++ cpu_timify(t2, timer) ++ ++ for i in t2: ++ if i == 0: ++ timer.sleep(0.01) ++ else: ++ timer.sleep(0.001) ++ t.update() ++ t.close() ++ # Get result for iter-based bar ++ b = progressbar_rate(get_bar(our_file.getvalue(), 3)) ++ # Get result for manually updated bar ++ b2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) ++ ++ # 3rd case: use medium smoothing ++ with closing(StringIO()) as our_file2: ++ with closing(StringIO()) as our_file: ++ t = tqdm(_range(3), file=our_file2, smoothing=0.5, leave=True, ++ miniters=1, mininterval=0) ++ cpu_timify(t, timer) + +- with tqdm(_range(3), file=our_file, smoothing=None, leave=True, +- miniters=1, mininterval=0) as t2: ++ t2 = tqdm(_range(3), file=our_file, smoothing=0.5, leave=True, ++ miniters=1, mininterval=0) + cpu_timify(t2, timer) + + for i in t2: +- # Sleep more for first iteration and +- # see how quickly rate is updated + if i == 0: + timer.sleep(0.01) + else: +- # Need to sleep in all iterations +- # to calculate smoothed rate +- # (else delta_t is 0!) + timer.sleep(0.001) + t.update() +- n_old = len(tqdm._instances) +- t.close() +- assert len(tqdm._instances) == n_old - 1 +- # Get result for iter-based bar +- a = progressbar_rate(get_bar(our_file.getvalue(), 3)) +- # Get result for manually updated bar +- a2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) ++ t2.close() ++ t.close() ++ # Get result for iter-based bar ++ c = progressbar_rate(get_bar(our_file.getvalue(), 3)) ++ # Get result for manually updated bar ++ c2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) ++ ++ # Check that medium smoothing's rate is between no and max ++ # smoothing rates ++ assert a <= c <= b ++ assert a2 <= c2 <= b2 ++ ++ def test_deprecated_nested(self): ++ """Test nested progress bars""" ++ if nt_and_no_colorama: ++ raise unittest.SkipTest ++ # TODO: test degradation on windows without colorama? ++ ++ # Artificially test nested loop printing ++ # Without leave ++ our_file = StringIO() ++ try: ++ tqdm(total=2, file=our_file, nested=True) ++ except TqdmDeprecationWarning: ++ if """`nested` is deprecated and automated""" \ ++ not in our_file.getvalue(): ++ raise ++ else: ++ raise DeprecationError("Should not allow nested kwarg") + +- # 2nd case: use max smoothing (= instant rate) +- with closing(StringIO()) as our_file2: ++ def test_bar_format(self): ++ """Test custom bar formatting""" + with closing(StringIO()) as our_file: +- t = tqdm(_range(3), file=our_file2, smoothing=1, leave=True, +- miniters=1, mininterval=0) +- cpu_timify(t, timer) ++ bar_format = ('{l_bar}{bar}|{n_fmt}/{total_fmt}-{n}/{total}' ++ '{percentage}{rate}{rate_fmt}{elapsed}{remaining}') ++ for _ in trange(2, file=our_file, leave=True, ++ bar_format=bar_format): ++ pass ++ out = our_file.getvalue() ++ assert "\r 0%| |0/2-0/20.0None?it/s00:00?\r" in out + +- with tqdm(_range(3), file=our_file, smoothing=1, leave=True, +- miniters=1, mininterval=0) as t2: +- cpu_timify(t2, timer) ++ # Test unicode string auto conversion ++ with closing(StringIO()) as our_file: ++ bar_format = r'hello world' ++ with tqdm(ascii=False, bar_format=bar_format, file=our_file) as t: ++ assert isinstance(t.bar_format, _unicode) ++ ++ def test_custom_format(self): ++ """Test adding additional derived format arguments""" ++ class TqdmExtraFormat(tqdm): ++ """Provides a `total_time` format parameter""" ++ @property ++ def format_dict(self): ++ d = super(TqdmExtraFormat, self).format_dict ++ total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) ++ d.update(total_time=self.format_interval(total_time) ++ + " in total") ++ return d + +- for i in t2: +- if i == 0: +- timer.sleep(0.01) +- else: +- timer.sleep(0.001) +- t.update() +- t.close() +- # Get result for iter-based bar +- b = progressbar_rate(get_bar(our_file.getvalue(), 3)) +- # Get result for manually updated bar +- b2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) ++ with closing(StringIO()) as our_file: ++ for _ in TqdmExtraFormat( ++ range(10), file=our_file, ++ bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"): ++ pass ++ assert "00:00 in total" in our_file.getvalue() + +- # 3rd case: use medium smoothing +- with closing(StringIO()) as our_file2: ++ def test_unpause(self): ++ """Test unpause""" ++ timer = DiscreteTimer() + with closing(StringIO()) as our_file: +- t = tqdm(_range(3), file=our_file2, smoothing=0.5, leave=True, +- miniters=1, mininterval=0) ++ t = trange(10, file=our_file, leave=True, mininterval=0) + cpu_timify(t, timer) ++ timer.sleep(0.01) ++ t.update() ++ timer.sleep(0.01) ++ t.update() ++ timer.sleep(0.1) # longer wait time ++ t.unpause() ++ timer.sleep(0.01) ++ t.update() ++ timer.sleep(0.01) ++ t.update() ++ t.close() ++ r_before = progressbar_rate(get_bar(our_file.getvalue(), 2)) ++ r_after = progressbar_rate(get_bar(our_file.getvalue(), 3)) ++ assert r_before == r_after + +- t2 = tqdm(_range(3), file=our_file, smoothing=0.5, leave=True, +- miniters=1, mininterval=0) +- cpu_timify(t2, timer) +- +- for i in t2: +- if i == 0: +- timer.sleep(0.01) +- else: +- timer.sleep(0.001) ++ def test_reset(self): ++ """Test resetting a bar for re-use""" ++ with closing(StringIO()) as our_file: ++ with tqdm(total=10, file=our_file, ++ miniters=1, mininterval=0, maxinterval=0) as t: ++ t.update(9) ++ t.reset() + t.update() +- t2.close() +- t.close() +- # Get result for iter-based bar +- c = progressbar_rate(get_bar(our_file.getvalue(), 3)) +- # Get result for manually updated bar +- c2 = progressbar_rate(get_bar(our_file2.getvalue(), 3)) +- +- # Check that medium smoothing's rate is between no and max smoothing rates +- assert a <= c <= b +- assert a2 <= c2 <= b2 +- +- +-@with_setup(pretest, posttest) +-def test_deprecated_nested(): +- """Test nested progress bars""" +- if nt_and_no_colorama: +- raise SkipTest +- # TODO: test degradation on windows without colorama? +- +- # Artificially test nested loop printing +- # Without leave +- our_file = StringIO() +- try: +- tqdm(total=2, file=our_file, nested=True) +- except TqdmDeprecationWarning: +- if """`nested` is deprecated and automated. +-Use `position` instead for manual control.""" not in our_file.getvalue(): +- raise +- else: +- raise DeprecationError("Should not allow nested kwarg") +- +- +-@with_setup(pretest, posttest) +-def test_bar_format(): +- """Test custom bar formatting""" +- with closing(StringIO()) as our_file: +- bar_format = ('{l_bar}{bar}|{n_fmt}/{total_fmt}-{n}/{total}' +- '{percentage}{rate}{rate_fmt}{elapsed}{remaining}') +- for _ in trange(2, file=our_file, leave=True, bar_format=bar_format): +- pass ++ t.reset(total=12) ++ t.update(10) ++ assert '| 1/10' in our_file.getvalue() ++ assert '| 10/12' in our_file.getvalue() ++ ++ def test_position(self): ++ """Test positioned progress bars""" ++ if nt_and_no_colorama: ++ raise unittest.SkipTest ++ ++ # Artificially test nested loop printing ++ # Without leave ++ our_file = StringIO() ++ kwargs = dict(file=our_file, miniters=1, mininterval=0, maxinterval=0) ++ t = tqdm(total=2, desc='pos2 bar', leave=False, position=2, **kwargs) ++ t.update() ++ t.close() + out = our_file.getvalue() +- assert "\r 0%| |0/2-0/20.0None?it/s00:00?\r" in out +- +- # Test unicode string auto conversion +- with closing(StringIO()) as our_file: +- bar_format = r'hello world' +- with tqdm(ascii=False, bar_format=bar_format, file=our_file) as t: +- assert isinstance(t.bar_format, _unicode) +- +- +-@with_setup(pretest, posttest) +-def test_custom_format(): +- """Test adding additional derived format arguments""" +- class TqdmExtraFormat(tqdm): +- """Provides a `total_time` format parameter""" +- @property +- def format_dict(self): +- d = super(TqdmExtraFormat, self).format_dict +- total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) +- d.update(total_time=self.format_interval(total_time) + " in total") +- return d +- +- with closing(StringIO()) as our_file: +- for _ in TqdmExtraFormat( +- range(10), file=our_file, +- bar_format="{total_time}: {percentage:.0f}%|{bar}{r_bar}"): +- pass +- assert "00:00 in total" in our_file.getvalue() ++ res = [m[0] for m in RE_pos.findall(out)] ++ exres = ['\n\n\rpos2 bar: 0%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\n\r '] + ++ pos_line_diff(res, exres) + +-@with_setup(pretest, posttest) +-def test_unpause(): +- """Test unpause""" +- timer = DiscreteTimer() +- with closing(StringIO()) as our_file: +- t = trange(10, file=our_file, leave=True, mininterval=0) +- cpu_timify(t, timer) +- timer.sleep(0.01) +- t.update() +- timer.sleep(0.01) +- t.update() +- timer.sleep(0.1) # longer wait time +- t.unpause() +- timer.sleep(0.01) +- t.update() +- timer.sleep(0.01) +- t.update() +- t.close() +- r_before = progressbar_rate(get_bar(our_file.getvalue(), 2)) +- r_after = progressbar_rate(get_bar(our_file.getvalue(), 3)) +- assert r_before == r_after +- +- +-@with_setup(pretest, posttest) +-def test_reset(): +- """Test resetting a bar for re-use""" +- with closing(StringIO()) as our_file: +- with tqdm(total=10, file=our_file, +- miniters=1, mininterval=0, maxinterval=0) as t: +- t.update(9) +- t.reset() +- t.update() +- t.reset(total=12) +- t.update(10) +- assert '| 1/10' in our_file.getvalue() +- assert '| 10/12' in our_file.getvalue() +- +- +-@with_setup(pretest, posttest) +-def test_position(): +- """Test positioned progress bars""" +- if nt_and_no_colorama: +- raise SkipTest +- +- # Artificially test nested loop printing +- # Without leave +- our_file = StringIO() +- kwargs = dict(file=our_file, miniters=1, mininterval=0, maxinterval=0) +- t = tqdm(total=2, desc='pos2 bar', leave=False, position=2, **kwargs) +- t.update() +- t.close() +- out = our_file.getvalue() +- res = [m[0] for m in RE_pos.findall(out)] +- exres = ['\n\n\rpos2 bar: 0%', +- '\n\n\rpos2 bar: 50%', +- '\n\n\r '] +- +- pos_line_diff(res, exres) +- +- # Test iteration-based tqdm positioning +- our_file = StringIO() +- kwargs["file"] = our_file +- for _ in trange(2, desc='pos0 bar', position=0, **kwargs): +- for _ in trange(2, desc='pos1 bar', position=1, **kwargs): +- for _ in trange(2, desc='pos2 bar', position=2, **kwargs): +- pass +- out = our_file.getvalue() +- res = [m[0] for m in RE_pos.findall(out)] +- exres = ['\rpos0 bar: 0%', +- '\n\rpos1 bar: 0%', +- '\n\n\rpos2 bar: 0%', +- '\n\n\rpos2 bar: 50%', +- '\n\n\rpos2 bar: 100%', +- '\rpos2 bar: 100%', +- '\n\n\rpos1 bar: 50%', +- '\n\n\rpos2 bar: 0%', +- '\n\n\rpos2 bar: 50%', +- '\n\n\rpos2 bar: 100%', +- '\rpos2 bar: 100%', +- '\n\n\rpos1 bar: 100%', +- '\rpos1 bar: 100%', +- '\n\rpos0 bar: 50%', +- '\n\rpos1 bar: 0%', +- '\n\n\rpos2 bar: 0%', +- '\n\n\rpos2 bar: 50%', +- '\n\n\rpos2 bar: 100%', +- '\rpos2 bar: 100%', +- '\n\n\rpos1 bar: 50%', +- '\n\n\rpos2 bar: 0%', +- '\n\n\rpos2 bar: 50%', +- '\n\n\rpos2 bar: 100%', +- '\rpos2 bar: 100%', +- '\n\n\rpos1 bar: 100%', +- '\rpos1 bar: 100%', +- '\n\rpos0 bar: 100%', +- '\rpos0 bar: 100%', +- '\n'] +- pos_line_diff(res, exres) +- +- # Test manual tqdm positioning +- our_file = StringIO() +- kwargs["file"] = our_file +- kwargs["total"] = 2 +- t1 = tqdm(desc='pos0 bar', position=0, **kwargs) +- t2 = tqdm(desc='pos1 bar', position=1, **kwargs) +- t3 = tqdm(desc='pos2 bar', position=2, **kwargs) +- for _ in _range(2): +- t1.update() +- t3.update() +- t2.update() +- out = our_file.getvalue() +- res = [m[0] for m in RE_pos.findall(out)] +- exres = ['\rpos0 bar: 0%', +- '\n\rpos1 bar: 0%', +- '\n\n\rpos2 bar: 0%', +- '\rpos0 bar: 50%', +- '\n\n\rpos2 bar: 50%', +- '\n\rpos1 bar: 50%', +- '\rpos0 bar: 100%', +- '\n\n\rpos2 bar: 100%', +- '\n\rpos1 bar: 100%'] +- pos_line_diff(res, exres) +- t1.close() +- t2.close() +- t3.close() +- +- # Test auto repositioning of bars when a bar is prematurely closed +- # tqdm._instances.clear() # reset number of instances +- with closing(StringIO()) as our_file: +- t1 = tqdm(total=10, file=our_file, desc='1.pos0 bar', mininterval=0) +- t2 = tqdm(total=10, file=our_file, desc='2.pos1 bar', mininterval=0) +- t3 = tqdm(total=10, file=our_file, desc='3.pos2 bar', mininterval=0) +- res = [m[0] for m in RE_pos.findall(our_file.getvalue())] +- exres = ['\r1.pos0 bar: 0%', +- '\n\r2.pos1 bar: 0%', +- '\n\n\r3.pos2 bar: 0%'] ++ # Test iteration-based tqdm positioning ++ our_file = StringIO() ++ kwargs["file"] = our_file ++ for _ in trange(2, desc='pos0 bar', position=0, **kwargs): ++ for _ in trange(2, desc='pos1 bar', position=1, **kwargs): ++ for _ in trange(2, desc='pos2 bar', position=2, **kwargs): ++ pass ++ out = our_file.getvalue() ++ res = [m[0] for m in RE_pos.findall(out)] ++ exres = ['\rpos0 bar: 0%', ++ '\n\rpos1 bar: 0%', ++ '\n\n\rpos2 bar: 0%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\n\rpos2 bar: 100%', ++ '\rpos2 bar: 100%', ++ '\n\n\rpos1 bar: 50%', ++ '\n\n\rpos2 bar: 0%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\n\rpos2 bar: 100%', ++ '\rpos2 bar: 100%', ++ '\n\n\rpos1 bar: 100%', ++ '\rpos1 bar: 100%', ++ '\n\rpos0 bar: 50%', ++ '\n\rpos1 bar: 0%', ++ '\n\n\rpos2 bar: 0%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\n\rpos2 bar: 100%', ++ '\rpos2 bar: 100%', ++ '\n\n\rpos1 bar: 50%', ++ '\n\n\rpos2 bar: 0%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\n\rpos2 bar: 100%', ++ '\rpos2 bar: 100%', ++ '\n\n\rpos1 bar: 100%', ++ '\rpos1 bar: 100%', ++ '\n\rpos0 bar: 100%', ++ '\rpos0 bar: 100%', ++ '\n'] + pos_line_diff(res, exres) + +- t2.close() +- t4 = tqdm(total=10, file=our_file, desc='4.pos2 bar', mininterval=0) +- t1.update(1) +- t3.update(1) +- t4.update(1) +- res = [m[0] for m in RE_pos.findall(our_file.getvalue())] +- exres = ['\r1.pos0 bar: 0%', +- '\n\r2.pos1 bar: 0%', +- '\n\n\r3.pos2 bar: 0%', +- '\r2.pos1 bar: 0%', +- '\n\n\r4.pos2 bar: 0%', +- '\r1.pos0 bar: 10%', +- '\n\n\r3.pos2 bar: 10%', +- '\n\r4.pos2 bar: 10%'] ++ # Test manual tqdm positioning ++ our_file = StringIO() ++ kwargs["file"] = our_file ++ kwargs["total"] = 2 ++ t1 = tqdm(desc='pos0 bar', position=0, **kwargs) ++ t2 = tqdm(desc='pos1 bar', position=1, **kwargs) ++ t3 = tqdm(desc='pos2 bar', position=2, **kwargs) ++ for _ in _range(2): ++ t1.update() ++ t3.update() ++ t2.update() ++ out = our_file.getvalue() ++ res = [m[0] for m in RE_pos.findall(out)] ++ exres = ['\rpos0 bar: 0%', ++ '\n\rpos1 bar: 0%', ++ '\n\n\rpos2 bar: 0%', ++ '\rpos0 bar: 50%', ++ '\n\n\rpos2 bar: 50%', ++ '\n\rpos1 bar: 50%', ++ '\rpos0 bar: 100%', ++ '\n\n\rpos2 bar: 100%', ++ '\n\rpos1 bar: 100%'] + pos_line_diff(res, exres) +- t4.close() +- t3.close() + t1.close() ++ t2.close() ++ t3.close() + ++ # Test auto repositioning of bars when a bar is prematurely closed ++ # tqdm._instances.clear() # reset number of instances ++ with closing(StringIO()) as our_file: ++ t1 = tqdm(total=10, file=our_file, desc='1.pos0 bar', mininterval=0) ++ t2 = tqdm(total=10, file=our_file, desc='2.pos1 bar', mininterval=0) ++ t3 = tqdm(total=10, file=our_file, desc='3.pos2 bar', mininterval=0) ++ res = [m[0] for m in RE_pos.findall(our_file.getvalue())] ++ exres = ['\r1.pos0 bar: 0%', ++ '\n\r2.pos1 bar: 0%', ++ '\n\n\r3.pos2 bar: 0%'] ++ pos_line_diff(res, exres) + +-@with_setup(pretest, posttest) +-def 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' +- t.set_description_str('World') +- assert t.desc == 'World' +- t.set_description() +- assert t.desc == '' +- t.set_description('Bye') +- assert t.desc == 'Bye: ' +- assert "World" in our_file.getvalue() +- +- # without refresh +- with closing(StringIO()) as our_file: +- with tqdm(desc='Hello', file=our_file) as t: +- assert t.desc == 'Hello' +- t.set_description_str('World', False) +- assert t.desc == 'World' +- t.set_description(None, False) +- assert t.desc == '' +- assert "World" not in our_file.getvalue() +- +- # unicode +- with closing(StringIO()) as our_file: +- with tqdm(total=10, file=our_file) as t: +- t.set_description(u"\xe1\xe9\xed\xf3\xfa") +- +- +-@with_setup(pretest, posttest) +-def test_deprecated_gui(): +- """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) +- assert not hasattr(t, "sp") +- try: +- t.update(1) +- except TqdmDeprecationWarning as e: +- if ('Please use `tqdm.gui.tqdm(...)` instead of' +- ' `tqdm(..., gui=True)`') \ +- not in our_file.getvalue(): +- raise e +- else: +- raise DeprecationError('Should not allow manual gui=True without' +- ' overriding __iter__() and update()') +- finally: +- t._instances.clear() +- # t.close() +- # len(tqdm._instances) += 1 # undo the close() decrement ++ t2.close() ++ t4 = tqdm(total=10, file=our_file, desc='4.pos2 bar', mininterval=0) ++ t1.update(1) ++ t3.update(1) ++ t4.update(1) ++ res = [m[0] for m in RE_pos.findall(our_file.getvalue())] ++ exres = ['\r1.pos0 bar: 0%', ++ '\n\r2.pos1 bar: 0%', ++ '\n\n\r3.pos2 bar: 0%', ++ '\r2.pos1 bar: 0%', ++ '\n\n\r4.pos2 bar: 0%', ++ '\r1.pos0 bar: 10%', ++ '\n\n\r3.pos2 bar: 10%', ++ '\n\r4.pos2 bar: 10%'] ++ pos_line_diff(res, exres) ++ t4.close() ++ t3.close() ++ t1.close() + +- t = tqdm(_range(3), gui=True, file=our_file, miniters=1, mininterval=0) +- try: +- for _ in t: +- pass +- except TqdmDeprecationWarning as e: +- if ('Please use `tqdm.gui.tqdm(...)` instead of' +- ' `tqdm(..., gui=True)`') \ +- not in our_file.getvalue(): +- raise e +- else: +- raise DeprecationError('Should not allow manual gui=True without' +- ' overriding __iter__() and update()') +- finally: +- t._instances.clear() +- # t.close() +- # len(tqdm._instances) += 1 # undo the close() decrement +- +- with tqdm(total=1, gui=False, file=our_file) as t: +- assert hasattr(t, "sp") +- +- +-@with_setup(pretest, posttest) +-def test_cmp(): +- """Test comparison functions""" +- with closing(StringIO()) as our_file: +- t0 = tqdm(total=10, file=our_file) +- t1 = tqdm(total=10, file=our_file) +- t2 = tqdm(total=10, file=our_file) +- +- assert t0 < t1 +- assert t2 >= t0 +- assert t0 <= t2 +- +- t3 = tqdm(total=10, file=our_file) +- t4 = tqdm(total=10, file=our_file) +- t5 = tqdm(total=10, file=our_file) +- t5.close() +- t6 = tqdm(total=10, file=our_file) +- +- assert t3 != t4 +- assert t3 > t2 +- assert t5 == t6 +- t6.close() +- t4.close() +- t3.close() +- t2.close() +- t1.close() +- t0.close() +- +- +-@with_setup(pretest, posttest) +-def test_repr(): +- """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= t0 ++ assert t0 <= t2 ++ ++ t3 = tqdm(total=10, file=our_file) ++ t4 = tqdm(total=10, file=our_file) ++ t5 = tqdm(total=10, file=our_file) ++ t5.close() ++ t6 = tqdm(total=10, file=our_file) ++ ++ assert t3 != t4 ++ assert t3 > t2 ++ assert t5 == t6 ++ t6.close() ++ t4.close() ++ t3.close() ++ t2.close() ++ t1.close() ++ t0.close() + +- before_squashed = squash_ctrlchars(before) +- after_squashed = squash_ctrlchars(after) ++ def test_repr(self): ++ """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 out3.count('\r') ++ assert out4.count(", ".join(expected_order)) == 2 + +-@with_setup(pretest, posttest) +-def test_postfix(): +- """Test postfix""" +- postfix = {'float': 0.321034, 'gen': 543, 'str': 'h', 'lst': [2]} +- postfix_order = (('w', 'w'), ('a', 0)) # no need for OrderedDict +- expected = ['float=0.321', 'gen=543', 'lst=[2]', 'str=h'] +- expected_order = ['w=w', 'a=0', 'float=0.321', 'gen=543', 'lst=[2]', +- 'str=h'] ++ # Test setting postfix string directly ++ with closing(StringIO()) as our_file: ++ with trange(10, file=our_file, desc='pos2 bar', ++ bar_format='{r_bar}', postfix=None) as t5: ++ t5.set_postfix_str("Hello", False) ++ t5.set_postfix_str("World") ++ out5 = our_file.getvalue() ++ ++ assert "Hello" not in out5 ++ out5 = out5[1:-1].split(', ')[3:] ++ assert out5 == ["World"] ++ ++ def test_file_redirection(self): ++ """Test redirection of output""" ++ with closing(StringIO()) as our_file: ++ # Redirect stdout to tqdm.write() ++ with std_out_err_redirect_tqdm(tqdm_file=our_file): ++ for _ in trange(3): ++ print("Such fun") ++ res = our_file.getvalue() ++ assert res.count("Such fun\n") == 3 ++ assert "0/3" in res ++ assert "3/3" in res + +- # Test postfix set at init +- with closing(StringIO()) as our_file: +- with tqdm(total=10, file=our_file, desc='pos0 bar', +- bar_format='{r_bar}', postfix=postfix) as t1: +- t1.refresh() +- out = our_file.getvalue() ++ def test_external_write(self): ++ """Test external write mode""" ++ with closing(StringIO()) as our_file: ++ # Redirect stdout to tqdm.write() ++ for _ in trange(3, file=our_file): ++ del tqdm._lock # classmethod should be able to recreate lock ++ with tqdm.external_write_mode(file=our_file): ++ our_file.write("Such fun\n") ++ res = our_file.getvalue() ++ assert res.count("Such fun\n") == 3 ++ assert "0/3" in res ++ assert "3/3" in res + +- # Test postfix set after init +- with closing(StringIO()) as our_file: +- with trange(10, file=our_file, desc='pos1 bar', bar_format='{r_bar}', +- postfix=None) as t2: +- t2.set_postfix(**postfix) +- t2.refresh() +- out2 = our_file.getvalue() +- +- # Order of items in dict may change, so need a loop to check per item +- for res in expected: +- assert res in out +- assert res in out2 +- +- # Test postfix (with ordered dict and no refresh) set after init +- with closing(StringIO()) as our_file: +- with trange(10, file=our_file, desc='pos2 bar', bar_format='{r_bar}', +- postfix=None) as t3: +- t3.set_postfix(postfix_order, False, **postfix) +- t3.refresh() # explicit external refresh +- out3 = our_file.getvalue() +- +- out3 = out3[1:-1].split(', ')[3:] +- assert out3 == expected_order +- +- # Test postfix (with ordered dict and refresh) set after init +- with closing(StringIO()) as our_file: +- with trange(10, file=our_file, desc='pos2 bar', +- bar_format='{r_bar}', postfix=None) as t4: +- t4.set_postfix(postfix_order, True, **postfix) +- t4.refresh() # double refresh +- out4 = our_file.getvalue() +- +- assert out4.count('\r') > out3.count('\r') +- assert out4.count(", ".join(expected_order)) == 2 +- +- # Test setting postfix string directly +- with closing(StringIO()) as our_file: +- with trange(10, file=our_file, desc='pos2 bar', bar_format='{r_bar}', +- postfix=None) as t5: +- t5.set_postfix_str("Hello", False) +- t5.set_postfix_str("World") +- out5 = our_file.getvalue() +- +- assert "Hello" not in out5 +- out5 = out5[1:-1].split(', ')[3:] +- assert out5 == ["World"] +- +- +-def test_postfix_direct(): +- """Test directly assigning non-str objects to postfix""" +- with closing(StringIO()) as our_file: +- with tqdm(total=10, file=our_file, miniters=1, mininterval=0, +- bar_format="{postfix[0][name]} {postfix[1]:>5.2f}", +- postfix=[dict(name="foo"), 42]) as t: +- for i in range(10): +- if i % 2: +- t.postfix[0]["name"] = "abcdefghij"[i] ++ def test_unit_scale(self): ++ """Test numeric `unit_scale`""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(9), unit_scale=9, file=our_file, ++ miniters=1, mininterval=0): ++ pass ++ out = our_file.getvalue() ++ assert '81/81' in out ++ ++ @patch_lock(thread=False) ++ def test_threading(self): ++ """Test multiprocess/thread-realted features""" ++ pass # TODO: test interleaved output #445 ++ ++ def test_bool(self): ++ """Test boolean cast""" ++ def internal(our_file, disable): ++ kwargs = dict(file=our_file, disable=disable) ++ with trange(10, **kwargs) as t: ++ assert t ++ with trange(0, **kwargs) as t: ++ assert not t ++ with tqdm(total=10, **kwargs) as t: ++ assert bool(t) ++ with tqdm(total=0, **kwargs) as t: ++ assert not bool(t) ++ with tqdm([], **kwargs) as t: ++ assert not t ++ with tqdm([0], **kwargs) as t: ++ assert t ++ with tqdm((x for x in []), **kwargs) as t: ++ assert t ++ with tqdm((x for x in [1, 2, 3]), **kwargs) as t: ++ assert t ++ with tqdm(**kwargs) as t: ++ try: ++ print(bool(t)) ++ except TypeError: ++ pass + else: +- t.postfix[1] = i +- t.update() +- res = our_file.getvalue() +- assert "f 6.00" in res +- assert "h 6.00" in res +- assert "h 8.00" in res +- assert "j 8.00" in res ++ raise TypeError("Expected bool(tqdm()) to fail") + ++ # test with and without disable ++ with closing(StringIO()) as our_file: ++ internal(our_file, False) ++ internal(our_file, True) + +-@contextmanager +-def std_out_err_redirect_tqdm(tqdm_file=sys.stderr): +- orig_out_err = sys.stdout, sys.stderr +- try: +- sys.stdout = sys.stderr = DummyTqdmFile(tqdm_file) +- yield orig_out_err[0] +- # Relay exceptions +- except Exception as exc: +- raise exc +- # Always restore sys.stdout/err if necessary +- finally: +- sys.stdout, sys.stderr = orig_out_err ++ def backendCheck(self, module): ++ """Test tqdm-like module fallback""" ++ tn = module.tqdm ++ tr = module.trange + ++ with closing(StringIO()) as our_file: ++ with tn(total=10, file=our_file) as t: ++ assert len(t) == 10 ++ with tr(1337) as t: ++ assert len(t) == 1337 + +-@with_setup(pretest, posttest) +-def test_file_redirection(): +- """Test redirection of output""" +- with closing(StringIO()) as our_file: +- # Redirect stdout to tqdm.write() +- with std_out_err_redirect_tqdm(tqdm_file=our_file): +- for _ in trange(3): +- print("Such fun") +- res = our_file.getvalue() +- assert res.count("Such fun\n") == 3 +- assert "0/3" in res +- assert "3/3" in res +- +- +-@with_setup(pretest, posttest) +-def test_external_write(): +- """Test external write mode""" +- with closing(StringIO()) as our_file: +- # Redirect stdout to tqdm.write() +- for _ in trange(3, file=our_file): +- del tqdm._lock # classmethod should be able to recreate lock +- with tqdm.external_write_mode(file=our_file): +- our_file.write("Such fun\n") +- res = our_file.getvalue() +- 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(9), unit_scale=9, file=our_file, +- miniters=1, mininterval=0): +- pass +- out = our_file.getvalue() +- assert '81/81' in out ++ def test_auto(self): ++ """Test auto fallback""" ++ from tqdm import autonotebook, auto ++ self.backendCheck(autonotebook) ++ self.backendCheck(auto) + ++ def test_wrapattr(self): ++ """Test wrapping file-like objects""" ++ data = "a twenty-char string" + +-def patch_lock(thread=True): +- """decorator replacing tqdm's lock with vanilla threading/multiprocessing""" +- try: +- if thread: +- from threading import RLock +- else: +- from multiprocessing import RLock +- lock = RLock() +- except (ImportError, OSError): +- raise SkipTest ++ with closing(StringIO()) as our_file: ++ with closing(StringIO()) as writer: ++ with tqdm.wrapattr( ++ writer, "write", file=our_file, bytes=True) as wrap: ++ wrap.write(data) ++ res = writer.getvalue() ++ assert data == res ++ res = our_file.getvalue() ++ assert '%.1fB [' % len(data) in res + +- def outer(func): +- """actual decorator""" +- @wraps(func) +- def inner(*args, **kwargs): +- """set & reset lock even if exceptions occur""" +- default_lock = tqdm.get_lock() +- try: +- tqdm.set_lock(lock) +- return func(*args, **kwargs) +- finally: +- tqdm.set_lock(default_lock) +- return inner +- return outer ++ with closing(StringIO()) as our_file: ++ with closing(StringIO()) as writer: ++ with tqdm.wrapattr( ++ writer, "write", file=our_file, bytes=False) as wrap: ++ wrap.write(data) ++ res = our_file.getvalue() ++ assert '%dit [' % len(data) in res + ++ def test_float_progress(self): ++ """Test float totals""" ++ with closing(StringIO()) as our_file: ++ with trange(10, total=9.6, file=our_file) as t: ++ with catch_warnings(record=True) as w: ++ simplefilter("always", category=TqdmWarning) ++ for i in t: ++ if i < 9: ++ assert not w ++ assert w ++ assert "clamping frac" in str(w[-1].message) ++ ++ def test_screen_shape(self): ++ """Test screen shape""" ++ # ncols ++ with closing(StringIO()) as our_file: ++ with trange(10, file=our_file, ncols=50) as t: ++ list(t) ++ ++ res = our_file.getvalue() ++ assert all(len(i) == 50 for i in get_bar(res)) ++ ++ # no second/third bar, leave=False ++ with closing(StringIO()) as our_file: ++ kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, ++ mininterval=0, leave=False) ++ with trange(10, desc="one", **kwargs) as t1: ++ with trange(10, desc="two", **kwargs) as t2: ++ with trange(10, desc="three", **kwargs) as t3: ++ list(t3) ++ list(t2) ++ list(t1) + +-@with_setup(pretest, posttest) +-@patch_lock(thread=False) +-def test_threading(): +- """Test multiprocess/thread-realted features""" +- pass # TODO: test interleaved output #445 +- +- +-@with_setup(pretest, posttest) +-def test_bool(): +- """Test boolean cast""" +- def internal(our_file, disable): +- kwargs = dict(file=our_file, disable=disable) +- with trange(10, **kwargs) as t: +- assert t +- with trange(0, **kwargs) as t: +- assert not t +- with tqdm(total=10, **kwargs) as t: +- assert bool(t) +- with tqdm(total=0, **kwargs) as t: +- assert not bool(t) +- with tqdm([], **kwargs) as t: +- assert not t +- with tqdm([0], **kwargs) as t: +- assert t +- with tqdm((x for x in []), **kwargs) as t: +- assert t +- with tqdm((x for x in [1, 2, 3]), **kwargs) as t: +- assert t +- with tqdm(**kwargs) as t: +- try: +- print(bool(t)) +- except TypeError: +- pass +- else: +- raise TypeError("Expected bool(tqdm()) to fail") +- +- # test with and without disable +- with closing(StringIO()) as our_file: +- internal(our_file, False) +- internal(our_file, True) +- +- +-def backendCheck(module): +- """Test tqdm-like module fallback""" +- tn = module.tqdm +- tr = module.trange +- +- with closing(StringIO()) as our_file: +- with tn(total=10, file=our_file) as t: +- assert len(t) == 10 +- with tr(1337) as t: +- assert len(t) == 1337 +- +- +-@with_setup(pretest, posttest) +-def test_auto(): +- """Test auto fallback""" +- from tqdm import autonotebook, auto +- backendCheck(autonotebook) +- backendCheck(auto) +- +- +-@with_setup(pretest, posttest) +-def test_wrapattr(): +- """Test wrapping file-like objects""" +- data = "a twenty-char string" +- +- with closing(StringIO()) as our_file: +- with closing(StringIO()) as writer: +- with tqdm.wrapattr( +- writer, "write", file=our_file, bytes=True) as wrap: +- wrap.write(data) +- res = writer.getvalue() +- assert data == res +- res = our_file.getvalue() +- assert '%.1fB [' % len(data) in res +- +- with closing(StringIO()) as our_file: +- with closing(StringIO()) as writer: +- with tqdm.wrapattr( +- writer, "write", file=our_file, bytes=False) as wrap: +- wrap.write(data) +- res = our_file.getvalue() +- assert '%dit [' % len(data) in res +- +- +-@with_setup(pretest, posttest) +-def test_float_progress(): +- """Test float totals""" +- with closing(StringIO()) as our_file: +- with trange(10, total=9.6, file=our_file) as t: +- with catch_warnings(record=True) as w: +- simplefilter("always", category=TqdmWarning) +- for i in t: +- if i < 9: +- assert not w +- assert w +- assert "clamping frac" in str(w[-1].message) +- +- +-@with_setup(pretest, posttest) +-def test_screen_shape(): +- """Test screen shape""" +- # ncols +- with closing(StringIO()) as our_file: +- with trange(10, file=our_file, ncols=50) as t: +- list(t) +- +- res = our_file.getvalue() +- assert all(len(i) == 50 for i in get_bar(res)) +- +- # no second/third bar, leave=False +- with closing(StringIO()) as our_file: +- kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, +- mininterval=0, leave=False) +- with trange(10, desc="one", **kwargs) as t1: +- with trange(10, desc="two", **kwargs) as t2: +- with trange(10, desc="three", **kwargs) as t3: +- list(t3) +- list(t2) +- list(t1) +- +- res = our_file.getvalue() +- assert "one" in res +- assert "two" not in res +- assert "three" not in res +- assert "\n\n" not in res +- assert "more hidden" in res +- # double-check ncols +- assert all(len(i) == 50 for i in get_bar(res) +- if i.strip() and "more hidden" not in i) +- +- # all bars, leave=True +- with closing(StringIO()) as our_file: +- kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, +- mininterval=0) +- with trange(10, desc="one", **kwargs) as t1: +- with trange(10, desc="two", **kwargs) as t2: +- assert "two" not in our_file.getvalue() +- with trange(10, desc="three", **kwargs) as t3: +- assert "three" not in our_file.getvalue() +- list(t3) +- list(t2) +- list(t1) +- +- res = our_file.getvalue() +- assert "one" in res +- assert "two" in res +- assert "three" in res +- assert "\n\n" not in res +- assert "more hidden" in res +- # double-check ncols +- assert all(len(i) == 50 for i in get_bar(res) +- if i.strip() and "more hidden" not in i) +- +- # second bar becomes first, leave=False +- with closing(StringIO()) as our_file: +- kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, +- mininterval=0, leave=False) +- t1 = tqdm(total=10, desc="one", **kwargs) +- with tqdm(total=10, desc="two", **kwargs) as t2: +- t1.update() +- t2.update() +- t1.close() + res = our_file.getvalue() + assert "one" in res + assert "two" not in res ++ assert "three" not in res ++ assert "\n\n" not in res + assert "more hidden" in res +- t2.update() ++ # double-check ncols ++ assert all(len(i) == 50 for i in get_bar(res) ++ if i.strip() and "more hidden" not in i) + +- res = our_file.getvalue() +- assert "two" in res ++ # all bars, leave=True ++ with closing(StringIO()) as our_file: ++ kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, ++ mininterval=0) ++ with trange(10, desc="one", **kwargs) as t1: ++ with trange(10, desc="two", **kwargs) as t2: ++ assert "two" not in our_file.getvalue() ++ with trange(10, desc="three", **kwargs) as t3: ++ assert "three" not in our_file.getvalue() ++ list(t3) ++ list(t2) ++ list(t1) + ++ res = our_file.getvalue() ++ assert "one" in res ++ assert "two" in res ++ assert "three" in res ++ assert "\n\n" not in res ++ assert "more hidden" in res ++ # double-check ncols ++ assert all(len(i) == 50 for i in get_bar(res) ++ if i.strip() and "more hidden" not in i) + +-@with_setup(pretest, posttest) +-def test_initial(): +- """Test `initial`""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(9), initial=10, total=19, file=our_file, +- miniters=1, mininterval=0): +- pass +- out = our_file.getvalue() +- assert '10/19' in out +- assert '19/19' in out ++ # second bar becomes first, leave=False ++ with closing(StringIO()) as our_file: ++ kwargs = dict(file=our_file, ncols=50, nrows=2, miniters=0, ++ mininterval=0, leave=False) ++ t1 = tqdm(total=10, desc="one", **kwargs) ++ with tqdm(total=10, desc="two", **kwargs) as t2: ++ t1.update() ++ t2.update() ++ t1.close() ++ res = our_file.getvalue() ++ assert "one" in res ++ assert "two" not in res ++ assert "more hidden" in res ++ t2.update() + ++ res = our_file.getvalue() ++ assert "two" in res + +-@with_setup(pretest, posttest) +-def test_colour(): +- """Test `colour`""" +- with closing(StringIO()) as our_file: +- for _ in tqdm(_range(9), file=our_file, colour="#beefed"): +- pass +- out = our_file.getvalue() +- assert '\x1b[38;2;%d;%d;%dm' % (0xbe, 0xef, 0xed) in out ++ def test_initial(self): ++ """Test `initial`""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(9), initial=10, total=19, file=our_file, ++ miniters=1, mininterval=0): ++ pass ++ out = our_file.getvalue() ++ assert '10/19' in out ++ assert '19/19' in out + +- with catch_warnings(record=True) as w: +- simplefilter("always", category=TqdmWarning) +- with tqdm(total=1, file=our_file, colour="charm") as t: +- assert w +- t.update() +- assert "Unknown colour" in str(w[-1].message) ++ def test_colour(self): ++ """Test `colour`""" ++ with closing(StringIO()) as our_file: ++ for _ in tqdm(_range(9), file=our_file, colour="#beefed"): ++ pass ++ out = our_file.getvalue() ++ assert '\x1b[38;2;%d;%d;%dm' % (0xbe, 0xef, 0xed) in out + +- with closing(StringIO()) as our_file2: +- for _ in tqdm(_range(9), file=our_file2, colour="blue"): +- pass +- out = our_file2.getvalue() +- assert '\x1b[34m' in out ++ with catch_warnings(record=True) as w: ++ simplefilter("always", category=TqdmWarning) ++ with tqdm(total=1, file=our_file, colour="charm") as t: ++ assert w ++ t.update() ++ assert "Unknown colour" in str(w[-1].message) + ++ with closing(StringIO()) as our_file2: ++ for _ in tqdm(_range(9), file=our_file2, colour="blue"): ++ pass ++ out = our_file2.getvalue() ++ assert '\x1b[34m' in out + +-@with_setup(pretest, posttest) +-def test_closed(): +- """Test writing to closed file""" +- with closing(StringIO()) as our_file: +- for i in trange(9, file=our_file, miniters=1, mininterval=0): +- if i == 5: +- our_file.close() ++ def test_closed(self): ++ """Test writing to closed file""" ++ with closing(StringIO()) as our_file: ++ for i in trange(9, file=our_file, miniters=1, mininterval=0): ++ if i == 5: ++ our_file.close() +diff --git a/tqdm/tests/tests_version.py b/tqdm/tests/tests_version.py +index 226b9980..11f57220 100644 +--- a/tqdm/tests/tests_version.py ++++ b/tqdm/tests/tests_version.py +@@ -1,12 +1,13 @@ + import re ++import unittest + +- +-def test_version(): +- """Test version string""" +- from tqdm import __version__ +- version_parts = re.split('[.-]', __version__) +- assert 3 <= len(version_parts) # must have at least Major.minor.patch +- try: +- map(int, version_parts[:3]) +- except ValueError: +- raise TypeError('Version Major.minor.patch must be 3 integers') ++class TestTqdmVersion(unittest.TestCase): ++ def test_version(self): ++ """Test version string""" ++ from tqdm import __version__ ++ version_parts = re.split('[.-]', __version__) ++ assert 3 <= len(version_parts) # must have at least Major.minor.patch ++ try: ++ map(int, version_parts[:3]) ++ except ValueError: ++ raise TypeError('Version Major.minor.patch must be 3 integers') + diff --git a/python-tqdm.changes b/python-tqdm.changes index 3905d92..dff3c25 100644 --- a/python-tqdm.changes +++ b/python-tqdm.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Oct 23 13:47:03 UTC 2020 - pgajdos@suse.com + +- added patches + https://github.com/tqdm/tqdm/pull/1052 + + python-tqdm-remove-nose.patch + ------------------------------------------------------------------- Sat Oct 10 18:46:08 UTC 2020 - Arun Persaud diff --git a/python-tqdm.spec b/python-tqdm.spec index e5e48e6..19db456 100644 --- a/python-tqdm.spec +++ b/python-tqdm.spec @@ -34,6 +34,8 @@ Summary: An extensible progress meter License: MPL-2.0 AND MIT URL: https://github.com/tqdm/tqdm Source: https://files.pythonhosted.org/packages/source/t/tqdm/tqdm-%{version}.tar.gz +# https://github.com/tqdm/tqdm/pull/1052 +Patch0: python-tqdm-remove-nose.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -42,9 +44,9 @@ Requires(postun): update-alternatives BuildArch: noarch %if %{with test} # SECTION test requirements -BuildRequires: %{python_module nose} BuildRequires: %{python_module numpy} BuildRequires: %{python_module pandas} +BuildRequires: %{python_module pytest} BuildRequires: %{python_module tqdm} BuildRequires: python3-ipython BuildRequires: python3-ipywidgets @@ -60,6 +62,7 @@ and does not require ncurses. %prep %setup -q -n tqdm-%{version} +%patch0 -p1 %build %python_build @@ -82,9 +85,12 @@ install -m 644 -D tqdm/completion.sh %{buildroot}%{_datadir}/bash-completion/com %if %{with test} %check -%{python_expand PYTHONPATH=%{$python_sitelib} -nosetests-%%{$python_bin_suffix} --ignore-files="tests_perf\.py" --ignore-files="tests_synchronisation\.py" tqdm/ -} +# test_perf: flaky +# test_synchronisation: hangs +# test_main: todo upstream, TypeError: a bytes-like object is required, not 'str' +# also disabled in https://github.com/tqdm/tqdm/pull/1052 +# and left upstream to solve +%pytest -k "not (tests_perf or tests_synchronisation or test_main)" tqdm/ %endif %if !%{with test}