Method for dry runs? - python

at the moment my python code often looks like this:
...
if not dry_run:
result = shutil.copyfile(...)
else:
print " DRY-RUN: shutil.copyfile(...) "
...
I now think about writting something like a dry runner method:
def dry_runner(cmd, dry_run, message, before="", after=""):
if dry_run:
print before + "DRY-RUN: " + message + after
# return execute(cmd)
But the cmd will be executed first and the result is given to dry_runner method.
How can I code such a method the pythonic way?

You could use this generic wrapper function:
def execute(func, *args):
print 'before', func
if not dry:
func(*args)
print 'after', func
>>> execute(shutil.copyfile, 'src', 'dst')

This isn't perfect in its display, but the functionality works. Hopefully this is clear enough:
dry = True
def dryrun(f):
def wrapper(*args, **kwargs):
if dry:
print "DRY RUN: %s(%s)" % (f.__name__,
','.join(list(args) + ["%s=%s" % (k, v) for (k, v) in kwargs.iteritems()]))
else:
f(*args, **kwargs)
return wrapper
import shutil
copyfile = dryrun(shutil.copyfile)
copyfile('a', 'b')

Related

How to print the list of args and kwargs

In my code, I have many places where I pass a function and its arguments to another function. For debugging purposes, I want to print the name of the function and the list of arguments. For example
def process(f, *args, **kwargs):
print("processing " + print_func(f, args, kwargs))
The expected output for
process(myfunc, 1,2, a="A",b=0) should be "processing myfunc(1,2,a="A", b=0)"
I made some progress printing the args as follows:
def print_func(f, *args, **kwarg):
func_str = f.__name__ + "("
if len(args) > 0:
func_str = func_str + (', '.join(['%.2f']*len(x)) % args)
func_str = func_str + ")"
which in the example above will produce the output processing myfunc(1,2)
The problem I have is how to print the kwargs. I can't figure out a similar solution using a dynamic formatting string for printing it as a sequence of k=v pairs separated by ",".
Any suggestion would be appreciated.
To format args and kwargs, you can simply iterate over them and create your string representation
def process(my_func, *args, **kwargs):
#Iterate over all args, convert them to str, and join them
args_str = ','.join(map(str,args))
#Iterater over all kwargs, convert them into k=v and join them
kwargs_str = ','.join('{}={}'.format(k,v) for k,v in kwargs.items())
#Or using f-strings
#kwargs_str = ','.join(f'{k}={v}' for k,v in kwargs.items()
#Form the final representation by adding func name
return "processing {}({})".format(my_func.__name__, ','.join([args_str,kwargs_str]))
#Or using f-strings
#return f"processing {my_func.__name__}({','.join([args_str,kwargs_str])})"
print(process(my_func, 1,2, a="A",b=0))
The output will be
processing my_func(1,2,a=A,b=0)
You can do it by creating an annotation like this
def printArgs(my_func):
def wrapper(*arg, **kwargs):
print("{}({})".format(my_func.__name__, ','.join([str(arg),str(kwargs)])))
return my_func(*arg, **kwargs)
return wrapper
#printArgs
def a_function_where_you_want_to_print_args(arg1, arg2, arg3="arg3"):
pass
a_function_where_you_want_to_print_args(1,2, arg3="b")
This will print "a_function_where_you_want_to_print_args((1, 2),{'arg3': 'b'})"

Timeit module for qgis plugin

I'd like to use the python module timeit to time some functions in my QGIS plugin.
Here, I've called the time it function within a function that I call at the end of the last function. It seems, though, that the plugin is taking even longer to run than usual and I am wondering if i'm calling the timer in the wrong place. Is there a better way to set this up?
class myPluginName:
def firstFunction(self):
...
self.secondFunction()
def secondFunction(self):
...
self.timeThings()
def run(self):
self.firstFunction()
def timeThings(self):
QMessageBox.information(None, 'First Function', 'Time : %s' % timeit.timeit(self.firstFunction,number=1)
QMessageBox.information(None, 'Second Function', 'Time : %s' % timeit.timeit(self.secondFunction,number=1)
UPDATE: After following some advice, i've tried to implement the wrapper in the following way. I get however, a TypeError: firstFunction() takes exactly 1 argument (2 given) on ret = func(**args, **kwargs)
def time_func(func):
try:
name = func.__name__
except:
name = func.f__name
def tf_wrapper(*args, **kwargs):
t = time.time()
ret = func(*args, **kwargs)
QMessageLog.logMessage("{}: {}".format(name, time.time() - t))
return ret
return tf_wrapper
class myPlugin:
def initGui(self):
QObject.connect(self.dlg.ui.comboBox,SIGNAL("currentIndexChanged(int)"), self.firstFunction)
#time_func
def firstFunc(self):
registry = QgsMapLayerRegistry.instance()
firstID = str(self.dlg.ui.firstCombo.itemData(self.dlg.ui.firstCombo.currentIndex()))
secondID = str(self.dlg.ui.secondCombo.itemData(self.dlg.ui.secondCombo.currentIndex()))
self.firstLayer = registry.mapLayer(firstID)
self.secondLayer = registry.mapLayer(secondID)
#time_func
def secondFunc(self):
...
self.thirdFunc()
def thirdFunct(self):
...
def run(self):
self.dlg.ui.firstCombo.clear()
self.dlg.ui.secondCombo.clear()
for layer in self.iface.legendInterface().layers():
if layer.type() == QgsMapLayer.VectorLayer:
self.dlg.ui.firstCombo.addItem(layer.name(), layer.id())
self.dlg.ui.secondCombo.addItem(layer.name(), layer.id())
result = self.dlg.exec_()
if result == 1:
self.secondFunction()
OK, I don't know your exact situation, but I'd set it up though decorators:
import time
def time_func(func):
try:
name = func.__name__
except:
name = func.f_name
def tf_wrapper(*args, **kwargs):
t = time.time()
ret = func(*args, **kwargs)
print("{}: {}".format(name, time.time() - t)) # Or use QMessageBox
return ret
return tf_wrapper
class myPluginName:
#time_func
def firstFunction(self):
pass
#time_func
def secondFunction(self):
pass
def run(self):
self.firstFunction()
myPluginName().firstFunction()
With this code, any function wrapped in time_func will have the time taken to execute the function printed when it returns, along with its name. E.g. running it will print:
firstFunction: 1.430511474609375e-06
For your TypeError, you need to change;
def firstFunction(self):
pass
To:
def firstFunction(self, index):
pass

Convert a while loop to something reusable

I often find myself using a pattern like this:
num_repeats = 123
interval = 12
for _ in xrange(num_repeats):
result = ...
if result meets condition:
break
time.sleep(interval)
else:
raise Failed despite multiple attempts
Basically, it repeats code until the correct result is returned, or the counter expires.
Although this works, it looks too verbose to me. Is it possible to "parametrize" this loop to a reusable function or context manager, like for example
with repeat(num_repeats, interval):
code
Or maybe there's something in the standard library that would do the trick?
You can use a generator which sleeps before returning repeated results.
The advantage is that your caller is still a genuine for loop, with
all the break, continue, else semantics still in tact.
def trickle_range(num_repeats, interval):
yield 0
for k in xrange(1, num_repeats):
time.sleep(interval)
yield k
for k in trickle_range(num_repeats, interval):
... do stuff, iterate or break as you like ...
You definately won't be able to use the with statement, as python only supplies hooks before and after the code has run, but not one for invoking it, ie. You can't hide a loop within a with statement.
A nice approach is to use a lambda function:
def repeat(repeats, interval, func):
for i in xrange(repeats):
if func(i):
break
time.sleep(interval)
Which you can then use quite easily:
repeat(123, 12, lambda i: condition(i))
Or something similar
One approach would be to decorate the functions you want to repeat:
def repeats_until(num_repeats, interval, condition):
def deco(f):
def func(*args, **kwargs):
for _ in xrange(num_repeats):
result = f(*args, **kwargs)
if condition(result):
return result
time.sleep(interval)
return func
return deco
And then use it like:
#repeats_until(3, 5, lambda s: s == "hello")
def take_input():
return raw_input("Say hello: ")
Example (although I can't show the wait!)
>>> take_input()
Say hello: foo
Say hello: bar
Say hello: baz
>>> take_input()
Say hello: hello
'hello'
Alternatively, to keep the condition with the called function, something like:
def repeats(num_repeats, interval):
def deco(f):
def func(*args, **kwargs):
for _ in xrange(num_repeats):
result = f(*args, **kwargs)
if result is not None: # or e.g. False if None is valid return
return result
time.sleep(interval)
return func
return deco
#repeats(3, 5)
def take_input(condition):
s = raw_input("Say hello: ")
if condition(s):
return s
ui = take_input(lambda s: s == "hello")
This relies on the decorated function returning a value (in this case the implicit None) that tells the decorator it isn't finished yet.

Using Decorator for Data Driven Tests

I am trying to parametrize a function using decorator. Finally I am able to get it run as expected after lot of hit and trials. But still I am not satisfied as though it is working, it doesn't seem to be right way to do it.
Please help me improve this code.
Here is my code:
def WarmWelcome(fn):
def wrapped(DataProvider):
for name in DataProvider():
print fn(name) + ":)"
return wrapped
def DataProvider():
names=["abc","xyz","def"]
for name in names:
yield name
#WarmWelcome
def hello(name):
return "hello " +name
hello(DataProvider)
Here is the updated code:
def WarmWelcome(DataProvider):
def real_decorator(fn):
def wrapped():
for name in DataProvider():
print fn(name) + ":)"
return wrapped
return real_decorator
def DataProvider():
names=["abc","xyz","def"]
for name in names:
yield name
#WarmWelcome(DataProvider)
def hello(name):
return "hello " +name
hello()
It's also possible to provide dataset right to WarmWelcome decorator:
def WarmWelcome(*data_sets):
def _decorator(func):
def _func():
for ds in data_sets:
func(*ds)
return _func
return _decorator
#WarmWelcome(
("abc", ),
("xyz", ),
("def", ),
)
def hello(name):
return "hello " +name
Original: PHPUnit-like dataProvider implementation for Python unittest

Including a formatted iterable as part of a larger formatted string

In writing a class recently, I initially included a __repr__ method along the following lines:
return "{}({!r}, {!r}, {!r})".format(
self.__class__.__name__,
self.arg1,
self.arg2,
self.arg3)
Repeating the '{!r}' snippet like that feels wrong and would be tedious to maintain if I ever add any more arguments to this class. However, the more robust alternatives that occurred to me aren't going to win any prizes for elegance either.
Building the format string programmatically:
fmt = "{}(%s)" % ", ".join(["{!r}"]*3)
return fmt.format(self.__class__.__name__,
self.arg1,
self.arg2,
self.arg3)
Formatting the arguments separately with str.join:
args = ", ".join(map(repr, [self.arg1, self.arg2, self.arg3]))
return "{}({})".format(self.__class__.__name__, args)
I've currently implemented the class using the last example, but I'm interested in suggestions for alternative approaches (since I'm not particularly happy with any of the above options).
Update:
Inspired by Ned's answer, I've now added the following utility function to a helper module:
def format_iter(iterable, fmt='{!r}', sep=', '):
return sep.join(fmt.format(x) for x in iterable)
>>> format_iter(range(10))
'0, 1, 2, 3, 4, 5, 6, 7, 8, 9'
>>> format_iter(range(10), sep='|')
'0|1|2|3|4|5|6|7|8|9'
>>> format_iter(range(10), fmt='{:04b}', sep='|')
'0000|0001|0010|0011|0100|0101|0110|0111|1000|1001'
>>> format_iter(range(10), fmt='{0.real}+{0.imag}j')
'0+0j, 1+0j, 2+0j, 3+0j, 4+0j, 5+0j, 6+0j, 7+0j, 8+0j, 9+0j'
Update2:
I ended up adding a second utility function, almost identical to the one in agf's answer:
def call_repr(name, *args):
return "{}({})".format(name, format_iter(args))
So the originally offending __repr__ function now looks like:
def __repr__(self):
return call_repr(self.__class__.__name__,
self.arg1,
self.arg2)
(Yes, one of the original constructor arguments went away earlier today.)
If this is a pattern you're going to repeat, I'd probably use:
# This is a static method or module-level function
def argrepr(name, *args):
return '{}({})'.format(name, ', '.join(repr(arg) for arg in args))
def __repr__(self):
return argrepr(self.__name__, self.arg1, self.arg2, self.arg3)
or
# This is a static method or module-level function
def argrepr(*args):
return '(' + ', '.join(repr(arg) for arg in args) + ')'
def __repr__(self):
return repr(self.__name__) + argrepr(self.arg1, self.arg2, self.arg3)
My inclination would be, if you don't like the code, hide it in a function:
def repr_all(*args):
return ", ".join(repr(a) for a in args)
def __repr__(self):
args = repr_all(self.arg1, self.arg2, self.arg3)
return "{}({})".format(self.__class__.__name__, args)
I might do it like:
def __repr__(self):
return "{0}({1})".format(
type(self).__name__,
", ".join(repr(arg) for arg in (self.arg1, self.arg2, self.arg3)))
or:
_init_getter = operator.attrgetter('arg1', 'arg2', 'arg3')
def __repr__(self):
return "{0}({1})".format(
type(self).__name__,
", ".join(repr(arg) for arg in self._init_getter(self)))

Categories