new progress decorator, 1) (de)activate with kwargs in function call 2) gives access to the progressBar instance -> allows for stop to do clean printing -- and tests for that

This commit is contained in:
Richard Hartmann 2015-01-29 20:58:36 +01:00
parent 3179cc27ec
commit ec23d5d168
4 changed files with 203 additions and 31 deletions

View file

@ -4,11 +4,8 @@
"""
from __future__ import division, print_function
import multiprocessing as mp
#import time
from inspect import getcallargs
from . import progress
from .jobmanager import getCountKwargs, validCountKwargs
@ -148,6 +145,7 @@ class ProgressBar(object):
count = callargs[self.cm[0]]
max_count = callargs[self.cm[1]]
with progress.ProgressBar(count = count,
max_count = max_count,
prepend = "{} ".format(self.__name__),
@ -155,6 +153,71 @@ class ProgressBar(object):
pb.start()
return self.func(**callargs)
class ProgressBarExtended(ProgressBar):
"""
extends the ProgressBar such that
on can turn of the ProgressBar by giving an extra argument,
namely 'progress_bar_off' and set its value to 'True'.
further there will be an additional argument passed to the function
called 'progress_bar' which allows to stop the progress bar from
within the function. note that there will be an function signature error
if the function does not accept the extra argument 'progress_bar'. So a
general **kwargs at the end of the functions arguments will help.
That is also the reason why the extended version comes in an extra class
because it might otherwise break compatibility.
Example
-------
>>> import jobmanager as jm
>>> c = jm.progress.UnsignedIntValue(val=0)
>>> m = jm.progress.UnsignedIntValue(val=20)
>>> @jm.decorators.ProgressBarExtended # choose 'ProgressBarExtended'
>>> def my_func_kwargs(c, m, **kwargs): # simply add '**kwargs' here
>>> for i in range(m.value):
>>> c.value = i+1
>>> time.sleep(0.1)
>>> # same as when using ProgressBar
>>> my_func_kwargs(c, m)
>>> # a simple kwarg will switch the progressBar off
>>> my_func_kwargs(c, m, progress_bar_off=True)
"""
def __call__(self, *args, **kwargs):
# Bind the args and kwds to the argument names of self.func
callargs = getcallargs(self.func, *args, **kwargs)
progress_bar_off = False
try:
progress_bar_off = callargs['progress_bar_off']
except KeyError:
pass
try:
progress_bar_off = callargs['kwargs']['progress_bar_off']
except KeyError:
pass
if progress_bar_off:
return self.func(**callargs)
count = callargs[self.cm[0]]
max_count = callargs[self.cm[1]]
with progress.ProgressBar(count = count,
max_count = max_count,
prepend = "{} ".format(self.__name__),
**self.kwargs) as pb:
pb.start()
callargs['progress_bar'] = pb
return self.func(**callargs)
class ProgressBarOverrideCount(ProgressBar):
def __call__(self, *args, **kwargs):

View file

@ -676,6 +676,7 @@ class Progress(Loop):
self.interval = interval
self.verbose = verbose
self.name = name
self.show_on_exit = False
self.add_args = {}
@ -709,8 +710,9 @@ class Progress(Loop):
show a last progress -> see the full 100% on exit
"""
super(Progress, self).__exit__(*exc_args)
self._show_stat()
print('\n'*(self.len-1))
if self.show_on_exit:
self._show_stat()
print('\n'*(self.len-1))
def _show_stat(self):
"""
@ -877,7 +879,11 @@ class Progress(Loop):
"""
raise NotImplementedError
def stop(self):
def start(self):
super(Progress, self).start()
self.show_on_exit = True
def stop(self, make_sure_its_down = False):
"""
trigger clean up by hand, needs to be done when not using
context management via 'with' statement
@ -885,6 +891,14 @@ class Progress(Loop):
super(Progress, self).stop()
self._show_stat()
print('\n'*(self.len-1))
self.show_on_exit = False
if make_sure_its_down and (self._proc is not None):
check_process_termination(proc = self._proc,
identifier = self._identifier,
timeout = 2*self.interval,
verbose = self.verbose,
auto_kill_on_last_resort = True)
def _reset_all(self):
"""
@ -970,7 +984,6 @@ class ProgressBar(Progress):
l = len_string_without_ESC(s1+s3)
if max_count_value != 0:
l2 = width - l - 1
a = int(l2 * count_value / max_count_value)

View file

@ -85,10 +85,72 @@ def test_ProgressBarOverrideCount():
my_func_ProgressBarOverrideCount_dec()
print("done!")
def test_extended_PB_get_access_to_progress_bar():
def my_func(c, m, **kwargs):
for i in range(m.value):
c.value = i+1
time.sleep(0.1)
try:
kwargs['progress_bar'].stop(make_sure_its_down=True)
except:
pass
print("let me show you something")
c = progress.UnsignedIntValue(val=0)
m = progress.UnsignedIntValue(val=20)
print("call decorated func")
my_func_dec = decorators.ProgressBarExtended(my_func)
my_func_dec(c=c, m=m)
print("call non decorated func")
my_func(c, m)
def test_extended_PB_progress_bar_off():
c = progress.UnsignedIntValue(val=0)
m = progress.UnsignedIntValue(val=20)
@decorators.ProgressBarExtended
def my_func_kwargs(c, m, **kwargs):
for i in range(m.value):
c.value = i+1
time.sleep(0.1)
@decorators.ProgressBarExtended
def my_func_normal(c, m, progress_bar_off=False, **kwargs):
for i in range(m.value):
c.value = i+1
time.sleep(0.1)
print("call with no kwarg -> normal progressBar")
my_func_kwargs(c, m)
print("call with kwarg 'progress_bar_off = True' -> NO progressBar")
my_func_kwargs(c, m, progress_bar_off = True)
print("call with kwarg 'progress_bar_off = False' -> normal progressBar")
my_func_kwargs(c, m, progress_bar_off = False)
print("call with argument 'progress_bar_off = False' -> normal progressBar")
my_func_normal(c, m, progress_bar_off = False)
print("call with default argument 'progress_bar_off = False' -> normal progressBar")
my_func_normal(c, m)
print("call with argument 'progress_bar_off = True' -> NO progressBar")
my_func_normal(c, m, progress_bar_off = True)
if __name__ == "__main__":
test_ProgressBar()
test_decorator()
test_ProgressBarOverrideCount()
# test_ProgressBar()
# test_decorator()
# test_ProgressBarOverrideCount()
# test_extended_PB_get_access_to_progress_bar()
test_extended_PB_progress_bar_off()

View file

@ -509,25 +509,59 @@ def test_progress_bar_slow_change():
time.sleep(1)
count.value = i
def test_progress_bar_start_stop():
max_count_value = 20
count = progress.UnsignedIntValue(0)
max_count = progress.UnsignedIntValue(max_count_value)
with progress.ProgressBar(count=count,
max_count=max_count,
interval=0.5,
speed_calc_cycles=5,
verbose=1) as sbm:
sbm.start()
for i in range(max_count_value):
time.sleep(0.1)
count.value = i+1
if i == 10:
sbm.stop(make_sure_its_down = True)
print("this will not overwrite the progressbar, because we stopped it explicitly")
sbm.start()
print("this WILL overwrite the progressbar, because we are still inside it's context (still running)")
print()
print("create a progress bar, but do not start")
with progress.ProgressBar(count=count,
max_count=max_count,
interval=0.5,
speed_calc_cycles=5,
verbose=1) as sbm:
pass
print("this is after progress.__exit__, there should be no prints from the progress")
if __name__ == "__main__":
func = [
test_loop_basic,
test_loop_signals,
test_loop_normal_stop,
test_loop_need_sigterm_to_stop,
test_loop_need_sigkill_to_stop,
test_why_with_statement,
test_progress_bar,
test_progress_bar_with_statement,
test_progress_bar_multi,
test_status_counter,
test_status_counter_multi,
test_intermediate_prints_while_running_progess_bar,
test_intermediate_prints_while_running_progess_bar_multi,
test_progress_bar_counter,
test_progress_bar_counter_non_max,
test_progress_bar_counter_hide_bar,
test_progress_bar_slow_change,
# test_loop_basic,
# test_loop_signals,
# test_loop_normal_stop,
# test_loop_need_sigterm_to_stop,
# test_loop_need_sigkill_to_stop,
# test_why_with_statement,
# test_progress_bar,
# test_progress_bar_with_statement,
# test_progress_bar_multi,
# test_status_counter,
# test_status_counter_multi,
# test_intermediate_prints_while_running_progess_bar,
# test_intermediate_prints_while_running_progess_bar_multi,
# test_progress_bar_counter,
# test_progress_bar_counter_non_max,
# test_progress_bar_counter_hide_bar,
# test_progress_bar_slow_change,
test_progress_bar_start_stop,
lambda: print("END")
]