Why are the 2 Magic Mocks instances different in ID? - python

I am trying to use patch() to inject mock into a call after reading this tutorial: https://kimsereylam.com/python/2021/03/19/how-to-use-patch-in-python-unittest.html, but the expect mock ID is different than actual.
Here is snippet of code to illustrate:
my_class.py
class MyClass:
def __init__(self):
self.value = 5
run.py
from my_class import MyClass
def call_first():
o = MyClass()
print(o)
call_second(o)
def call_second(myclass):
print("second is called")
print(myclass)
main.py
from unittest.mock import patch
from my_class import MyClass
from run import call_first, call_second
with patch("run.MyClass") as myclass_mock:
print(myclass_mock)
print(call_first())
with patch("run.call_second") as call_second_mock:
call_second_mock.assert_called_once_with(myclass_mock)
The output is:
<MagicMock name='MyClass' id='140711608367712'>
<MagicMock name='MyClass()' id='140711598743312'>
second is called
<MagicMock name='MyClass()' id='140711598743312'>
None
Traceback (most recent call last):
File "main.py", line 9, in <module>
call_second_mock.assert_called_once_with(myclass_mock)
File "/usr/lib/python3.8/unittest/mock.py", line 924, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'call_second' to be called once. Called 0 times.
What did I miss? I am trying to use patch() so I can mock the MyClass() object that is instantiated inside call_first() and passed to call_second().

with patch("run.MyClass") as myclass_mock:
with patch("run.call_second") as call_second_mock:
#print(myclass_mock)
instance = myclass_mock.return_value
print(instance)
print(call_first())
call_second_mock.assert_called_once_with(instance)
I need to use .return_value to get the MagicMock for the class instance
I should mock the call_second first

Related

patching at TestCase class level without overriding unit_test signatures

I am patching a method for my TestCase class
#patch(
"my_function_to_mock"
side_effect=new_behaviour_function,
)
class MyTestCase(unittests.TestCase):
def test_my_code():
assert True
however unit_test fails because of unexpected number of argument
Traceback (most recent call last):
File "/usr/local/lib/python3.8/unittest/mock.py", line 1325, in patched
return func(*newargs, **newkeywargs)
TypeError: test_my_code() takes 1 positional argument but 2 were given
Everything seems logical, test_my_code was called with my mock as an argument, this failed because my unit test signature do not except a mock.
How can I apply a patch to my TestCase without having to change all my unit tests signatures? I do not need to access mock inside my test method.
I could solve my problem by manually starting a patch at TestCase class setup.
#classmethod
def setUpClass(cls):
my_patch = patch(
"my_function_to_mock",
side_effect=new_behaviour_function,
)
my_patch.start()
cls.addClassCleanup(my_patch.stop)
super(TestCase, cls).setUpClass()

How to define a custom function of an instance using setattr

I'm trying to add a method to a class dynamically, but I keep running into an error where self is not passed to a the new function. For instance:
class Dummy():
def say_hi(self):
print("hi")
def new_method(self):
print("bye")
dummy = Dummy()
setattr(dummy, "say_bye", new_method)
dummy.say_bye()
results in the following error:
Traceback (most recent call last):
File "main.py", line 13, in <module>
dummy.say_bye()
TypeError: new_method() missing 1 required positional argument: 'self'
What am I doing wrong?
Use types.MethodType feature:
from types import MethodType
class Dummy():
def say_hi(self):
print("hi")
def new_method(self):
print("bye")
dummy = Dummy()
dummy.say_bye = MethodType(new_method, dummy)
dummy.say_bye() # bye
You are setting the function new_method as an attribute of the dummy object.
If you do print(dummy.__dict__) you'll see something like this:
{'say_bye': <function new_method at 0x7fcd949af668>}
This means that your dummy object has the function new_method as an attribute, so when you do dummy.say_bye(), you're calling the function you have as an attribute without any argument.
It is not a function of the Dummy class, it is just a function that your dummy object has as an attribute.
You can achieve the functionality you are looking for using RomanPerekhrest's answer.
Hope it helps.
Cheers!

Using a method from the parent class

I want to call a method from the parent class in a child class.
I use XX.__init__() in my child class and call the press function from the parent class. But it fails when I run the following code:
Func.py
class PC:
def __init__(self):
PCKeyDis = {}
self.PCKeyDis = PCKeyDis
def Press(self,key):
KeyDis = self.PCKeyDis
if len(key)==1 and key.islower():
key = key.upper()
win32api.keybd_event(KeyDis[key],0,0,0)
time.sleep(0.1)
win32api.keybd_event(KeyDis[key],0,win32con.KEYEVENTF_KEYUP,0)
class PCFunc(PC):
def __init__(self):
pass
def Sentence(self,string):
PC.__init__()
strlist = list(string)
for i in xrange(len(strlist)):
if strlist[i] == ' ':
strlist[i] = 'Space'
PC.Press(strlist[i]) #use this function
action.py
import Func
import win32gui
PC = Func.PC()
PCFunc = Func.PCFunc ()
win32gui.SetForegroundWindow(win32gui.FindWindow(winclass,winnm))
PCFunc.Sentence(path)
I get:
unbound method Sentence() must be called with PCFunc instance as first argument (got str instance instead)
If you want to call the constructor of the base class, then you do it on instantiation in the __init__() method, not in the Sentence() method:
def __init__(self):
super(self.__class__, self).__init__()
Since Sentence() is an instance method, you need to call it via an instance of the class (like the error tells you):
pc_func = PCFunc()
pc_func.Sentence(var)
Here you are calling the method with an undefined variable:
PCFunc.Sentence(path)
Instead you need to give a string as parameter, so either write Sentence('path'), or define the variable first:
path = 'my path'
pc_func.Sentence(path)
Do not use the same name as the class name for an instance of the class:
PCFunc = Func.PCFunc ()
Otherwise the variable name storing the instance overwrites the class name.
Apart from that, it is unclear what your code is actually supposed to do. Have a look at the Python code conventions for a first step to making your code more readible. Then do some research about classes and inheritance.
The code you posted does not produce the error you posted. Here is an example that will produce that error:
class Dog:
def do_stuff(self, string):
print string
d = Dog()
d.do_stuff('hello')
Dog.do_stuff(d, 'goodbye')
Dog.do_stuff('goodbye')
--output:--
hello
goodbye
Traceback (most recent call last):
File "1.py", line 9, in <module>
Dog.do_stuff('goodbye')
TypeError: unbound method do_stuff() must be called with Dog instance as first argument (got str instance instead)
An __init__() function can also produce that error:
class Dog:
def __init__(self):
pass
def do_stuff(self, string):
print(string)
Dog.__init__()
--output:--
Traceback (most recent call last):
File "1.py", line 7, in <module>
Dog.__init__()
TypeError: unbound method __init__() must be called with Dog instance as first argument (got nothing instead)
In the line:
d.do_stuff('hello')
the fragment d.do_stuff causes python to create and return a bound method object--which is then immediately executed by the function execution operator () in the fragment ('hello’). The bound method is bound to the instance d, hence the reason it is called a bound method. A bound method automatically passes the instance it contains to the method when the method is executed.
On the other hand, when you write:
Dog.do_stuff(....)
the fragment Dog.do_stuff causes python to create and return an unbound method. An unbound method does not contain an instance, so when an unbound method is executed by the function execution operator (), you must manually pass an instance. (In python3, things changed and you can pass anything as the first argument--an instance of the class isn't required.)

Extending a class in nltk. - python

The aim is to add additional functions to the wordnet class in nltk, e.g.:
from nltk.corpus import wordnet
class WN(wordnet):
def foobar(self):
print 'foobar'
x = WN
WN.foobar()
but it gives an error:
Traceback (most recent call last):
File "/home/alvas/workspace/pybabel-fresh/babelnet/utils/pybabel_WordNet.py", line 5, in <module>
class WN(wordnet):
File "/usr/local/lib/python2.7/dist-packages/nltk/corpus/util.py", line 44, in __init__
assert issubclass(reader_cls, CorpusReader)
TypeError: Error when calling the metaclass bases
issubclass() arg 1 must be a class
So I tried with nltk.corpus.reader.WordNetCorpusReader
(http://www.nltk.org/_modules/nltk/corpus/reader/wordnet.html#WordNetCorpusReader):
from nltk.corpus.reader import WordNetCorpusReader
class WN(WordNetCorpusReader):
def __init__(self):
self = WN.__init__()
def foobar(self):
return "foobar"
x = WN
x.foobar()
Still it seems like if I'm using WordNetCorpusReader, I need to instantiate it, so I got:
Traceback (most recent call last):
File "/home/alvas/workspace/pybabel-fresh/babelnet/utils/pybabel_WordNet.py", line 13, in <module>
x.foobar()
TypeError: unbound method foobar() must be called with WN instance as first argument (got nothing instead)
Then I tried:
from nltk.corpus.reader import WordNetCorpusReader
class WN(WordNetCorpusReader):
def foobar(self):
return "foobar"
x = WN
for i in x.all_synsets():
print i
[out]:
Traceback (most recent call last):
File "/home/alvas/workspace/pybabel-fresh/babelnet/utils/pybabel_WordNet.py", line 10, in <module>
for i in x.all_synsets():
TypeError: unbound method all_synsets() must be called with WN instance as first argument (got nothing instead)
How do I extend the nltk wordnet API with new functions? Note: that the aim is to create a new class with the new functions.
Your second attempt seems closest. The problem there is with your constructor:
class WN(WordNetCorpusReader):
def __init__(self):
self = WN.__init__() # needs an instance as the first argument, recursive, and no need to assign to self
The __init__ method needs an instance as its first argument (here self), and in addition you are calling the __init__ method of the wrong class. This will lead to a RuntimeError: maximum recursion depth exceeded error. Finally, you simply want to call the method; you don't need to assign the results of the method to self.
I think you meant to do this instead:
from nltk.corpus.reader import WordNetCorpusReader
import nltk
class WN(WordNetCorpusReader):
def __init__(self, *args):
WordNetCorpusReader.__init__(self, *args)
def foobar(self):
return "foobar"
The catch is, though, that you will need to pass the required WordNetCorpusReader.__init__ args to your new class. In my version of nltk, that means you will need to pass a root argument as follows:
>>> x = WN(nltk.data.find('corpora/wordnet'))
>>> x.foobar()
'foobar'
>>> x.synsets('run')
[Synset('run.n.01'), Synset('test.n.05'), ...]
A more efficient approach
A much more efficient way to do the same thing is as follows:
class WN(WordNetCorpusReader):
root = nltk.data.find('corpora/wordnet') # make root a class variable, so you only need to load it once
def __init__(self, *args, **kwargs):
WordNetCorpusReader.__init__(self, WN.root, *args, **kwargs) # add root yourself here, so no arguments are required
def foobar(self):
return "foobar"
Now test it:
>>> x = WN()
>>> x.foobar()
'foobar'
>>> x.synsets('run')
[Synset('run.n.01'), Synset('test.n.05'), ...]
By the way, I've enjoyed seeing your work on the nltk tag.

Importing class-type global variables

I'm trying to implement a configuration system contained within a module. The core configuration variable is a class instance and a global variable in this module. It seems that when I import this variable, I cannot use it as a class for some reason.
Consider this minimal example:
foomodule.py:
class FooClass:
number = 5
def bar (self):
return self.number
foo = FooClass
foo.number = 5
main.py
from foomodule import foo
print foo.bar()
Running main.py results in a cryptic error message:
Traceback (most recent call last):
File "main.py", line 2, in <module>
print foo.bar()
TypeError: unbound method bar() must be called with FooClass instance as first argument (got nothing instead)
But I am calling it with a FooClass instance which I'd think should be the self argument like it usually is. What am I doing wrong here?
You only bound foo to the class; you didn't make it an instance:
foo = FooClass # only creates an additional reference
Call the class:
foo = FooClass() # creates an instance of FooClass
In Python you usually don't use accessor methods; just reference foo.number in your main module, rather than use foo.bar() to obtain it.
In your example foo is just an alias for FooClass. I assume that your actual problem is more complicated than your snippet. However, if you really need a class method, you can annotate it with #classmethod decorator.
class FooClass(object):
number = 5
#classmethod
def bar(cls):
return cls.number
To use your the class you could do:
from foomodule import Foo
Foo.bar()
Or you can access the class member directly
Foo.number

Categories

Resources