diff --git a/jobmanager/decorators.py b/jobmanager/decorators.py index 51949dd..30a562d 100644 --- a/jobmanager/decorators.py +++ b/jobmanager/decorators.py @@ -5,7 +5,8 @@ from __future__ import division, print_function import multiprocessing as mp -import time +#import time +from inspect import getcallargs from . import progress @@ -15,7 +16,6 @@ from .jobmanager import getCountKwargs, validCountKwargs __all__ = ["ProgressBar"] - class ProgressBar(object): """ A wrapper/decorator with a text-based progress bar. @@ -23,23 +23,44 @@ class ProgressBar(object): - __init__ - __call__ + The idea is to add a status bar for a regular + function just by wrapping the function via + python's decorator syntax. - Examples - -------- + In order to do so, the function needs to provide some + extra information, namely the current state 'count' and + the final state 'max_count'. Simply expand your function + by these two additional keyword arguments (or other pairs + specified in jobmanager.validCountKwargs) and set their + values during the calculation (see example 1 below). In that + manner the decorated function as well as the not decorated + function can simple be called as one would not care about + any status information. + + Alternatively one could explicitly set count and max_count + in the function call, which circumvents the need to change + the value of max_count AFTER instantiation of the progressBar. + + + Example 1 + --------- >>> from jobmanager.decorators import ProgressBar + >>> from jobmanager.decorators.progress import UnsignedIntValue >>> import time >>> >>> @ProgressBar - >>> def my_func(arg, kwarg="1", count=None, max_count=None): + >>> def my_func_1(arg, + >>> kwarg = "1", + >>> count = UnsignedIntValue(val=0), + >>> max_count = UnsignedIntValue(val=1)): + >>> # max_count should as default always be set to a value > 0 >>> maxval = 100 - >>> if max_count is not None: - >>> max_count.value = maxval - >>> + >>> max_count.value = maxval + >>> >>> for i in range(maxval): - >>> if count is not None: - >>> count.value += 1 - >>> time.sleep(0.05) + >>> count.value += 1 + >>> time.sleep(0.02) >>> >>> return arg+kwarg >>> @@ -47,6 +68,22 @@ class ProgressBar(object): # The progress of my_func is monitored on stdout. one argument second argument + Example 2 + --------- + + >>> from jobmanager.decorators import ProgressBar + >>> from jobmanager.decorators.progress import UnsignedIntValue + >>> import time + >>> + >>> @ProgressBar + >>> def my_func(c, m): + >>> for i in range(m.value): + >>> c.value = i + >>> time.sleep(0.02) + >>> + >>> c = progress.UnsignedIntValue(val=0) + >>> m = progress.UnsignedIntValue(val=100) + >>> my_func(c, m) Notes ----- @@ -57,7 +94,7 @@ class ProgressBar(object): >>> result = wrapper("wrapped function", kwarg=" test") """ - def __init__(self, func, *args, **kwargs): + def __init__(self, func, **kwargs): """ Initiates the wrapper objet. A function can be wrapped by decorating it with @@ -72,8 +109,6 @@ class ProgressBar(object): two keyword-arguments `count` and `max_count` (or `c` and `m`). The method `func` increments `count.value` up to `max_count.value` (`c.value`, `m.value`). - *args : list - Arguments for `jobmanager.ProgressBar`. **kwargs : dict Keyword-arguments for `jobmanager.ProgressBar`. @@ -87,16 +122,13 @@ class ProgressBar(object): """ self.__name__ = func.__name__ # act like the function self.func = func - self.args = args self.kwargs = kwargs - # works with Python 2.7 and 3.3 - valid = func.__code__.co_varnames[:func.__code__.co_argcount] # Check arguments self.cm = getCountKwargs(func) if self.cm is None: raise ValueError( "The wrapped function `{}` ".format(func.func_name)+ - "must accept one of the folling pairs of "+ + "must accept one of the following pairs of "+ "keyword arguments:{}".format(validCountKwargs)) @@ -110,18 +142,18 @@ class ProgressBar(object): **kwargs : dict Keyword-arguments for `func`. """ - # check if the kwarg is already given - # (e.g. by a function that is nested. - if not kwargs.has_key(self.cm[0]) or kwargs[self.cm[0]] is None: - # count - kwargs[self.cm[0]] = progress.UnsignedIntValue(0) - if not kwargs.has_key(self.cm[1]) or kwargs[self.cm[1]] is None: - # max_count - kwargs[self.cm[1]] = progress.UnsignedIntValue(1) - with progress.ProgressBar(kwargs[self.cm[0]], kwargs[self.cm[1]], - *self.args, **self.kwargs) as pb: + + # Bind the args and kwds to the argument names of self.func + callargs = getcallargs(self.func, *args, **kwargs) + + 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() - return self.func(*args, **kwargs) + return self.func(**callargs) @@ -157,40 +189,5 @@ def decorate_module_ProgressBar(module, **kwargs): kwargs["verbose"] > 0): print("Jobmanager wrapped {}.{}".format( module.__name__, key)) - - -@ProgressBar -def _my_func_1(arg, kwarg="1", count=None, max_count=None): - maxval = 100 - if max_count is not None: - max_count.value = maxval - - for i in range(maxval): - if count is not None: - count.value += 1 - - time.sleep(0.02) - - return arg+kwarg -def _my_func_2(arg, c, m, kwarg="2"): - maxval = 100 - m.value += maxval - - for i in range(maxval): - c.value += 1 - - time.sleep(0.02) - - return arg+kwarg - - -def _test_ProgressBar(): - result1 = _my_func_1("decorated function", kwarg=" 1") - print(result1) - - wrapper = ProgressBar(_my_func_2, interval=.1) - result2 = wrapper("wrapped function", kwarg=" 2") - print(result2) - diff --git a/test/test_decorators.py b/test/test_decorators.py new file mode 100644 index 0000000..5ce7de8 --- /dev/null +++ b/test/test_decorators.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import division, print_function + +import os +import sys +import time + +from os.path import abspath, dirname, split + +# Add parent directory to beginning of path variable +sys.path = [split(dirname(abspath(__file__)))[0]] + sys.path + +from jobmanager import decorators, progress + +@decorators.ProgressBar +def _my_func_1(arg, + kwarg = "1", + count = decorators.progress.UnsignedIntValue(val=0), + max_count = decorators.progress.UnsignedIntValue(val=1)): + maxval = 100 + max_count.value = maxval + + for i in range(maxval): + count.value += 1 + time.sleep(0.02) + + return arg+kwarg + + +def _my_func_2(arg, + c = decorators.progress.UnsignedIntValue(val=0), + m = decorators.progress.UnsignedIntValue(val=1), + kwarg = "2"): + maxval = 100 + m.value += maxval + + for i in range(maxval): + c.value += 1 + time.sleep(0.02) + + return arg+kwarg + + +def test_ProgressBar(): + result1 = _my_func_1("decorated function", kwarg=" 1") + print(result1) + + wrapper = decorators.ProgressBar(_my_func_2, interval=.1) + result2 = wrapper("wrapped function", kwarg=" 2") + print(result2) + +@decorators.ProgressBar +def my_func(c, m): + for i in range(m.value): + c.value = i + time.sleep(0.02) + +def test_decorator(): + c = progress.UnsignedIntValue(val=0) + m = progress.UnsignedIntValue(val=100) + my_func(c=c, m=m) + my_func(c, m) + +if __name__ == "__main__": + test_ProgressBar() + test_decorator() + +