Replacing keyword argument in pytest - python

I am doing some testing and I would like to mock one argument of a function.
For example I got function like:
def foo(arg1, arg2):
'do something'
and call of that function:
foo(1, 2)
I would like to monkeypatch it somehow to use 3 instead of 2. Is that possible?
tried something like:
monkeypatch.setattr('foo', partial(foo, arg2= 3))
But I got type error: foo() got multiple values for keyword argument 'arg2'
Any idea how to solve this?

You can simply alias the function:
old_foo = monkeypatch.foo
def foo(arg1, arg2):
return old_foo(arg1, 3)
monkeypatch.foo = foo

Related

Can I pass a function and a varying number of arguments to another function?

can someone say what the best method to do this would be?
I want to have a function, overall_function that I pass other functions to. But the other functions don't always have the same or type of arguments.
So what would be the correct syntax to do:
def overall_function(function, arguments):
function(arguments)
do other stuff
I'd like arguments to be:
arg1 = 'foo', arg2 = 53, ...
Thank you!
Take variable number of arguments:
def overall_function(function, *positional, **keyword):
function(*positional, **keyword)
Now, you can pass the arguments like:
overall_function(function, 1, 2, foo='bar')
and this will execute the function as:
function(1, 2, foo='bar')
positional would be a tuple: (1, 2) (these two are positional arguments).
keyword would be a dict: {'foo': 'bar'} (this is a keyword argument).
Looking for *args and **kwargs
def overall_function(function, *args, **kwargs):
function(*args, **kwargs)
do other stuff
You might be better off passing in a lambda as opposed to two arguments so you can delay the execution of the function till you need it, and still keeping the parameters contained together
def overall_function(lambda_func):
lambda_func()
overall_function(lambda: function(used,as,normal))

Python unittest check function call args

I am developing unit tests to an existing library, and I would like to test if the arguments a function is called with match certain criteria. In my case the function to test is:
class ...
def function(self):
thing = self.method1(self.THING)
thing_obj = self.method2(thing)
self.method3(thing_obj, 1, 2, 3, 4)
For the unit tests I have patched the methods 1, 2 and 3 in the following way:
import unittest
from mock import patch, Mock
class ...
def setUp(self):
patcher1 = patch("x.x.x.method1")
self.object_method1_mock = patcher1.start()
self.addCleanup(patcher1.stop)
...
def test_funtion(self)
# ???
In the unit test I would like to extract the arguments 1, 2, 3, 4 and compare them e.g. see if the 3rd argument is smaller than the fourth one ( 2 < 3). How would I go on about this with mock or another library?
You can get the most recent call arguments from a mock using the call_args attribute. If you want to compare the arguments of the self.method3() call, then you should be able to do something like this:
def test_function(self):
# Call function under test etc.
...
# Extract the arguments for the last invocation of method3
arg1, arg2, arg3, arg4, arg5 = self.object_method3_mock.call_args[0]
# Perform assertions
self.assertLess(arg3, arg4)
More info here on call_args and also call_args_list.

Odd argument in threading.py [duplicate]

What does a bare asterisk in the parameters of a function do?
When I looked at the pickle module, I see this:
pickle.dump(obj, file, protocol=None, *, fix_imports=True)
I know about a single and double asterisks preceding parameters (for variable number of parameters), but this precedes nothing. And I'm pretty sure this has nothing to do with pickle. That's probably just an example of this happening. I only learned its name when I sent this to the interpreter:
>>> def func(*):
... pass
...
File "<stdin>", line 1
SyntaxError: named arguments must follow bare *
If it matters, I'm on python 3.3.0.
Bare * is used to force the caller to use named arguments - so you cannot define a function with * as an argument when you have no following keyword arguments.
See this answer or Python 3 documentation for more details.
While the original answer answers the question completely, just adding a bit of related information. The behaviour for the single asterisk derives from PEP-3102. Quoting the related section:
The second syntactical change is to allow the argument name to
be omitted for a varargs argument. The meaning of this is to
allow for keyword-only arguments for functions that would not
otherwise take a varargs argument:
def compare(a, b, *, key=None):
...
In simple english, it means that to pass the value for key, you will need to explicitly pass it as key="value".
def func(*, a, b):
print(a)
print(b)
func("gg") # TypeError: func() takes 0 positional arguments but 1 was given
func(a="gg") # TypeError: func() missing 1 required keyword-only argument: 'b'
func(a="aa", b="bb", c="cc") # TypeError: func() got an unexpected keyword argument 'c'
func(a="aa", b="bb", "cc") # SyntaxError: positional argument follows keyword argument
func(a="aa", b="bb") # aa, bb
the above example with **kwargs
def func(*, a, b, **kwargs):
print(a)
print(b)
print(kwargs)
func(a="aa",b="bb", c="cc") # aa, bb, {'c': 'cc'}
Semantically, it means the arguments following it are keyword-only, so you will get an error if you try to provide an argument without specifying its name. For example:
>>> def f(a, *, b):
... return a + b
...
>>> f(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given
>>> f(1, b=2)
3
Pragmatically, it means you have to call the function with a keyword argument. It's usually done when it would be hard to understand the purpose of the argument without the hint given by the argument's name.
Compare e.g. sorted(nums, reverse=True) vs. if you wrote sorted(nums, True). The latter would be much less readable, so the Python developers chose to make you to write it the former way.
Suppose you have function:
def sum(a,key=5):
return a + key
You can call this function in 2 ways:
sum(1,2) or sum(1,key=2)
Suppose you want function sum to be called only using keyword arguments.
You add * to the function parameter list to mark the end of positional arguments.
So function defined as:
def sum(a,*,key=5):
return a + key
may be called only using sum(1,key=2)
I've found the following link to be very helpful explaining *, *args and **kwargs:
https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/
Essentially, in addition to the answers above, I've learned from the site above (credit: https://pythontips.com/author/yasoob008/) the following:
With the demonstration function defined first below, there are two examples, one with *args and one with **kwargs
def test_args_kwargs(arg1, arg2, arg3):
print "arg1:", arg1
print "arg2:", arg2
print "arg3:", arg3
# first with *args
>>> args = ("two", 3,5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
# now with **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two","arg1":5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
So *args allows you to dynamically build a list of arguments that will be taken in the order in which they are fed, whereas **kwargs can enable the passing of NAMED arguments, and can be processed by NAME accordingly (irrespective of the order in which they are fed).
The site continues, noting that the correct ordering of arguments should be:
some_func(fargs,*args,**kwargs)

How work pre-defined descriptors in functions?

Python functions have a descriptors. I believe that in most cases I shouldn't use this directly but I want to know how works this feature? I tried a couple of manipulations with such an objects:
def a():
return 'x'
a.__get__.__doc__
'descr.__get__(obj[, type]) -> value'
What is the obj and what is the type?
>>> a.__get__()
TypeError: expected at least 1 arguments, got 0
>>> a.__get__('s')
<bound method ?.a of 's'>
>>> a.__get__('s')()
TypeError: a() takes no arguments (1 given)
Sure that I can't do this trick with functions which take no arguments. Is it required just only to call functions with arguments?
>>> def d(arg1, arg2, arg3):
return arg1, arg2, arg3
>>> d.__get__('s')('x', 'a')
('s', 'x', 'a')
Why the first argument taken directly by __get__, and everything else by returned object?
a.__get__ is a way to bind a function to an object. Thus:
class C(object):
pass
def a(s):
return 12
a = a.__get__(C)
is the rough equivalent of
class C(object):
def a(self):
return 12
(Though it's not a good idea to do it this way. For one thing, C won't know that it has a bound method called a, which you can confirm by doing dir(C). Basically, the __get__ does just one part of the process of binding).
That's why you can't do this for a function that takes no arguments- it must take that first argument (traditionally self) that passes the specific instance.

decorating a function and adding functionalities preserving the number of argument

I'd like to decorate a function, using a pattern like this:
def deco(func):
def wrap(*a,**kw):
print "do something"
return func(*a,**kw)
return wrap
The problem is that if the function decorated has a prototype like that:
def function(a,b,c): return
When decorated, the prototype is destroyed by the varargs, for example, calling function(1,2,3,4) wouldn't result in an exception. Is that a way to avoid that?
How can define the wrap function with the same prototype as the decorated (func) one?
There's something conceptually wrong?
EDIT
My perverse idea was to lighten the "calling of the parent method" without modifying the signature. Something like
def __init__(self, something)
super(ClassName, self).__init__(something)
to:
#extended
def __init__(self, something):
...
I was figuring out if this was possible and if this makes sense.
EDIT
As Alex pointed out, the following code doesn't give an exception:
function(1,2,3,4)
You're wrong when you state that "calling function(1,2,3,4) wouldn't result in an exception". Check it out:
>>> def deco(f):
... def w(*a, **k):
... print 'do something'
... return f(*a, **k)
... return w
...
>>> def f(a, b, c): return
...
>>> f(1, 2, 3, 4)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: f() takes exactly 3 arguments (4 given)
>>> decorated = deco(f)
>>> decorated(1, 2, 3, 4)
do something
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 4, in w
TypeError: f() takes exactly 3 arguments (4 given)
As you see, you get exactly the same exception as when similarly calling the undecorated f (albeit after the print you've added).
For preserving the wrapped function's metadata (name, docstring, use functools.wraps. Predicting that the wrapped function will raise when called (to avoid doing other work before calling it) is always hard ("impossible" in general, as it's equivalent to the halting problem; "just hard" in specific, when you only care about raising a type-error for argument name and number mismatches and want to treat that specialized exception case differently from any other exception case -- a peculiar requirement;-0).
If you're adamant that you absolutely need it (and can perhaps explain why?) I'll be glad (well, not glad, but will grit my teeth and do it, after all I've got a long weekend in front of me;-) to take you down that labyrinthine path.
The decorator module helps you create a decorator that preserves the function signature.
As a result, you will get the exception you expect when calling the function, and inspect.getargspec will give you the correct signature.
It works by dynamically building a function definition and using exec. Unfortunately, there isn't an easier built-in way to do this.
Here is a trick that involves fetching the original argument specification from the decorated function, then creating a lambda by evaling a string with the same arguments. The decorator is then wrapped in this lambda thus to the outside world it has the same parameter names and default values:
import inspect, time
import functools
def decorator_wrapper(old_function, new_function):
args, arglist, kw, default = inspect.getargspec(old_function)
args = list(args)
if arglist:
args.append(arglist)
if kw:
args.append(kw)
callstring = inspect.formatargspec(args, arglist, kw, default, formatvalue=lambda value: "")
argstring = inspect.formatargspec(args, arglist, kw, default)[1:-1]
unique_name = "_func" + str(int(time.time()))
codestring = "lambda " + argstring + " : " + unique_name + callstring
decorated_function = eval(codestring, {unique_name: new_function})
return functools.wraps(old_function)(decorated_function)
By calling result=func(*a,**kw) first, you get the TypeError before printing "do something".
def deco(func):
def wrap(*a,**kw):
result=func(*a,**kw)
print "do something"
return result
return wrap
#deco
def function(a,b,c): return
function(1,2,3)
# do something
function(1,2,3,4)
# TypeError: function() takes exactly 3 arguments (4 given)

Categories

Resources