minor change to make python 3.4 compatible. Changed default behavior of progressBar decorator. The functions to be decorated NEEDS (c,m) in their definition. made it also possible to call the decorated functions with c or/and m as positional agruments. Added test for that, moved existing tests to test/test_decorators.py. This closes #11

This commit is contained in:
cimatosa 2015-01-21 14:27:42 +01:00
parent 0301665936
commit 3594d67c12
2 changed files with 130 additions and 64 deletions

View file

@ -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)

69
test/test_decorators.py Normal file
View file

@ -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()