2020-10-07 01:48:05 +00:00
|
|
|
from functools import wraps
|
2018-03-19 20:08:55 +00:00
|
|
|
from threading import Event
|
2020-10-07 01:48:05 +00:00
|
|
|
from time import sleep, time
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2021-01-09 17:00:18 +00:00
|
|
|
from tqdm import TMonitor, tqdm, trange
|
|
|
|
|
|
|
|
from .tests_tqdm import StringIO, closing, importorskip, patch_lock, skip
|
2020-10-24 20:56:26 +00:00
|
|
|
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
class Time(object):
|
|
|
|
"""Fake time class class providing an offset"""
|
|
|
|
offset = 0
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
@classmethod
|
|
|
|
def reset(cls):
|
2020-10-06 17:46:10 +00:00
|
|
|
"""zeroes internal offset"""
|
2020-10-06 15:23:27 +00:00
|
|
|
cls.offset = 0
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
@classmethod
|
|
|
|
def time(cls):
|
2020-10-06 17:46:10 +00:00
|
|
|
"""time.time() + offset"""
|
2020-10-06 15:23:27 +00:00
|
|
|
return time() + cls.offset
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
@staticmethod
|
|
|
|
def sleep(dur):
|
2020-10-06 17:46:10 +00:00
|
|
|
"""identical to time.sleep()"""
|
2020-10-07 01:48:05 +00:00
|
|
|
sleep(dur)
|
2020-10-06 15:11:36 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
@classmethod
|
|
|
|
def fake_sleep(cls, dur):
|
2020-10-06 17:46:10 +00:00
|
|
|
"""adds `dur` to internal offset"""
|
2020-10-06 15:23:27 +00:00
|
|
|
cls.offset += dur
|
2020-10-06 15:11:36 +00:00
|
|
|
sleep(0.000001) # sleep to allow interrupt (instead of pass)
|
2018-03-19 20:18:14 +00:00
|
|
|
|
|
|
|
|
2023-03-03 15:43:42 +00:00
|
|
|
class FakeEvent(Event):
|
2020-10-07 01:48:05 +00:00
|
|
|
"""patched `threading.Event` where `wait()` uses `Time.fake_sleep()`"""
|
2023-03-03 15:43:42 +00:00
|
|
|
def wait(self, timeout=None):
|
2020-10-07 11:39:13 +00:00
|
|
|
"""uses Time.fake_sleep"""
|
2020-10-07 01:48:05 +00:00
|
|
|
if timeout is not None:
|
|
|
|
Time.fake_sleep(timeout)
|
2023-03-03 15:43:42 +00:00
|
|
|
return self.is_set()
|
2020-10-07 01:48:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
def patch_sleep(func):
|
2020-10-07 11:39:13 +00:00
|
|
|
"""Temporarily makes TMonitor use Time.fake_sleep"""
|
2020-10-07 01:48:05 +00:00
|
|
|
@wraps(func)
|
|
|
|
def inner(*args, **kwargs):
|
2020-10-07 11:39:13 +00:00
|
|
|
"""restores TMonitor on completion regardless of Exceptions"""
|
2020-10-07 01:48:05 +00:00
|
|
|
TMonitor._test["time"] = Time.time
|
|
|
|
TMonitor._test["Event"] = FakeEvent
|
|
|
|
if tqdm.monitor:
|
|
|
|
assert not tqdm.monitor.get_instances()
|
|
|
|
tqdm.monitor.exit()
|
|
|
|
del tqdm.monitor
|
|
|
|
tqdm.monitor = None
|
|
|
|
try:
|
|
|
|
return func(*args, **kwargs)
|
|
|
|
finally:
|
|
|
|
# Check that class var monitor is deleted if no instance left
|
|
|
|
tqdm.monitor_interval = 10
|
|
|
|
if tqdm.monitor:
|
|
|
|
assert not tqdm.monitor.get_instances()
|
|
|
|
tqdm.monitor.exit()
|
|
|
|
del tqdm.monitor
|
|
|
|
tqdm.monitor = None
|
|
|
|
TMonitor._test.pop("Event")
|
|
|
|
TMonitor._test.pop("time")
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-07 01:48:05 +00:00
|
|
|
return inner
|
2018-03-19 20:08:55 +00:00
|
|
|
|
2020-10-06 15:11:36 +00:00
|
|
|
|
2020-10-06 15:23:27 +00:00
|
|
|
def cpu_timify(t, timer=Time):
|
|
|
|
"""Force tqdm to use the specified timer instead of system-wide time"""
|
2020-10-06 15:11:36 +00:00
|
|
|
t._time = timer.time
|
|
|
|
t._sleep = timer.fake_sleep
|
|
|
|
t.start_t = t.last_print_t = t._time()
|
|
|
|
return timer
|
|
|
|
|
|
|
|
|
|
|
|
class FakeTqdm(object):
|
2020-10-07 01:48:05 +00:00
|
|
|
_instances = set()
|
2020-10-06 15:11:36 +00:00
|
|
|
get_lock = tqdm.get_lock
|
2018-03-19 20:08:55 +00:00
|
|
|
|
|
|
|
|
2018-04-15 00:05:15 +00:00
|
|
|
def incr(x):
|
|
|
|
return x + 1
|
|
|
|
|
|
|
|
|
2019-11-08 01:34:36 +00:00
|
|
|
def incr_bar(x):
|
|
|
|
with closing(StringIO()) as our_file:
|
2019-11-08 23:20:43 +00:00
|
|
|
for _ in trange(x, lock_args=(False,), file=our_file):
|
|
|
|
pass
|
2019-11-08 01:34:36 +00:00
|
|
|
return incr(x)
|
|
|
|
|
|
|
|
|
2020-10-24 18:36:45 +00:00
|
|
|
@patch_sleep
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
2018-03-19 20:08:55 +00:00
|
|
|
with tqdm(total=total, file=our_file, miniters=500, mininterval=0.1,
|
2020-10-24 18:36:45 +00:00
|
|
|
maxinterval=1E5) as t2:
|
|
|
|
cpu_timify(t1, Time)
|
|
|
|
cpu_timify(t2, Time)
|
2018-03-19 20:08:55 +00:00
|
|
|
# Do a lot of iterations in a small timeframe
|
2020-10-24 18:36:45 +00:00
|
|
|
Time.fake_sleep(maxinterval / 10)
|
|
|
|
t1.update(500)
|
|
|
|
t2.update(500)
|
|
|
|
assert t1.miniters <= 500 # TODO: should really be == 500
|
|
|
|
assert t2.miniters == 500
|
2018-03-19 20:08:55 +00:00
|
|
|
# Then do 1 it after monitor interval, so that monitor kicks in
|
2020-10-07 01:48:05 +00:00
|
|
|
Time.fake_sleep(maxinterval)
|
2020-10-24 18:36:45 +00:00
|
|
|
t1.update(1)
|
|
|
|
t2.update(1)
|
|
|
|
# Wait for the monitor to get out of sleep and update tqdm
|
2020-10-19 12:14:53 +00:00
|
|
|
timeend = Time.time()
|
2020-10-24 18:36:45 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
def test_imap():
|
|
|
|
"""Test multiprocessing.Pool"""
|
|
|
|
try:
|
|
|
|
from multiprocessing import Pool
|
|
|
|
except ImportError as err:
|
|
|
|
skip(str(err))
|
|
|
|
|
|
|
|
pool = Pool()
|
|
|
|
res = list(tqdm(pool.imap(incr, range(100)), disable=True))
|
2021-01-03 22:33:43 +00:00
|
|
|
pool.close()
|
2020-10-24 18:36:45 +00:00
|
|
|
assert res[-1] == 100
|
|
|
|
|
|
|
|
|
|
|
|
@patch_lock(thread=True)
|
|
|
|
def test_threadpool():
|
|
|
|
"""Test concurrent.futures.ThreadPoolExecutor"""
|
2021-01-04 03:26:44 +00:00
|
|
|
ThreadPoolExecutor = importorskip('concurrent.futures').ThreadPoolExecutor
|
2020-10-24 18:36:45 +00:00
|
|
|
|
|
|
|
with ThreadPoolExecutor(8) as pool:
|
2023-03-03 15:43:42 +00:00
|
|
|
res = list(tqdm(pool.map(incr_bar, range(100)), disable=True))
|
2020-10-24 18:36:45 +00:00
|
|
|
assert sum(res) == sum(range(1, 101))
|