Mock Patch check parent class method call - python

I want to unit test this class method update
class EmployeeUpdateSerializer(serializers.ModelSerializer):
def update(self, instance, data):
shift_types = data.pop('shift_types', None)
instance = super().update(instance, data)
self.update_shift_type(instance, shift_types)
return instance
I am doing this
class TestEmployeeUpdateSerializer(TestCase):
def setUp(self):
self.company, self.user, self.header = create_user_session()
self.serializer = EmployeeUpdateSerializer()
def test_update(self):
employee = self.user
with patch.object(self.serializer, 'update') as update:
with patch.object(self.serializer, 'update_shift_type') as update_shift_type:
res = self.serializer.update(employee, dict())
update.assert_called_once_with(employee, dict())
update_shift_type.assert_called_once_with(employee, None)
self.assertEqual(res, employee)
But this gives me an error
Traceback (most recent call last):
File "/Users/shahzadfarukh/my-croft/backend/account/tests/test_employee_serializers.py", line 222, in test_update
update_shift_type.assert_called_once_with(employee, None)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 830, in assert_called_once_with
raise AssertionError(msg)
AssertionError: Expected 'update_shift_type' to be called once. Called 0 times.
Please help me! is something I am doing wrong?

You have mocked update, so its original code won't get called. If you want to test what update calls, you have to call the original version, and mock only functions/methods called within the update. Assuming the base class is imported like import serializers, you could do something like this (untested)
class TestEmployeeUpdateSerializer(TestCase):
def setUp(self):
self.company, self.user, self.header = create_user_session()
self.serializer = EmployeeUpdateSerializer()
#patch('serializers.ModelSerializer.update')
#patch('serializers.ModelSerializer.update_shift_type')
def test_update(self, mocked_update_shift_type, mocked_update):
employee = self.user
res = self.serializer.update(employee, dict())
mocked_update.assert_called_once_with(employee, dict())
mocked_update_shift_type.assert_called_once_with(employee, None)
self.assertEqual(res, employee)

Related

create Django form class with dynamic number of CharFields

I'm having problem of creating a dynamic class form that contains a list of CharFields.
I'm able to create a normal class with dynamic keys values as below. but when I change the values to be a CharFields. it doesn't work. What's wrong with the form?
=========Normal class===========
class DynamicClass(forms.Form):
def __init__ (self, data):
self.data =data
for key in self.data.keys():
self.__setattr__(key, self.data[key])
my_class = DynamicClass({"HOSTNAME": 'Router_Hostname: ', "IP": 'IP_ADDRESS:'})
print (my_class.HOSTNAME)
output --> Router_Hostname:
print (my_class.IP)
output--> IP_ADDRESS:
=========Form class===========
class DynamicForm(forms.Form):
def __init__ (self, data):
self.data =data
for key in self.data.keys():
self.__setattr__(key, forms.CharField(label = self.data[key]))
my_form = DynamicForm ({"HOSTNAME": 'Router_Hostname: ', "IP": 'IP_ADDRESS:'})
print (my_form.as_table)
error output-->
Traceback (most recent call last):
File "", line 1, in
File "~/automation/lib/python3.8/site-packages/django/forms/forms.py", line 137, in repr
if self._errors is None:
AttributeError: 'DynamicForm' object has no attribute '_errors'
I got it solved
firstly, i shouldn't use "label" to set initial value for CharField(). Instead, use 'initial' keyword.
2nd, missing super(DynamicForm, self).__init__(*args, **kwargs)
finally a working solution:
class DynamicForm(forms.Form):
def __init__ (self, data, *args, **kwargs):
super(DynamicForm, self).__init__(*args, **kwargs)
self.data = data
for key in self.data.keys():
self.fields[key] = forms.CharField(initial = self.data[key])

nesting of properties vs setters and getters in python

class OurAtt():
def __init__(self):
self.Log = False
def setLog(self):
self.Log = True
def clearLog(self):
self.Log = False
class OurClass(object):
def __init__(self):
self.__OurAtt = OurAtt()
#property
def OurAtt(self):
return self.__OurAtt
#OurAtt.setter
def OurAtt(self, val):
raise Exception("can't modify the attribute" )
x = OurClass()
x.OurAtt.setLog()
print x.OurAtt.Log # print True
x.OurAtt.Log = False
print x.OurAtt.Log # sets to False Aim set this through function call x.OurAtt.setLog() I want to restrict the access, something like private variable.
Final aim is Log should be the attribute of OurAttr and should be protected by getter and setters or properties. Its like nesting of properties. and hierarchy should be maintained like object.OurAttr.Log
I researched and got the following link.
Python: multiple properties, one setter/getter
But It is not hitting my aim.
I am actually new to getter, setter and properties. Thanks in advance
I believe you are over-complicating the issue. If you want to prevent access to the attributes of OurAtt, the #property decorator should be used withing OurAtt. Instances of the OurAtt class will implement this protected-access behavior always, including when they are members of OurClass. You don't need to do anything with the #property decorator in OurClass unless you want to prevent modifying members of that class.
This, I think, does what you are trying to accomplish. It runs under 2.7 - if you are using an earlier version your mileage may vary.
class OurAttr(object):
def __init__(self):
self._log = False
#property
def log(self):
return self._log
#log.setter
def log(self, value):
raise AttributeError("Cannot set 'log' attribute directly.")
#log.deleter
def log(self):
raise AttributeError("Cannot delete 'log' attribute directly.")
def setLog(self):
self._log = True
print "Log is", self._log
def clearLog(self):
self._log = False
print "Log is", self._log
class OurClass(object):
def __init__(self):
self.OurAttr = OurAttr()
oc = OurClass()
oc.OurAttr.setLog()
oc.OurAttr.clearLog()
oc.OurAttr.log = False # Raises exception
Output is:
$ python2.7 test.py
Log is True
Log is False
Traceback (most recent call last):
File "test.py", line 33, in <module>
oc.OurAttr.log = False
File "test.py", line 11, in log
raise AttributeError("Cannot set 'log' attribute directly.")
AttributeError: Cannot set 'log' attribute directly.

No attribute error even when the attribute is set

So I have a class that extends two classes deep, here is it's definition and __init__():
class ProspectEventSocketProtocol(ChannelEventSocketProtocol):
def __init__(self, *args, **kwargs):
super(ProspectEventSocketProtocol, self).__init__(*args, **kwargs)
self.channel_info = None
self.rep_uuid = None
self.manual_dial = None
self.datetime_setup = timezone.now()
self.datetime_answered = None
self.defer_until_answered = defer.Deferred()
self.defer_until_originated = defer.Deferred()
self.defer_until_finished = defer.Deferred()
The definition and __init__() for the ChannelEventSocketProtocol is here:
class ChannelEventSocketProtocol(Freeswitch):
def __init__(self, *args, **kwargs):
self.channel_driver = None
self.uuid = kwargs.pop('uuid', str(uuid4()))
self._call_driver = kwargs.pop('call_driver', None)
super(ChannelEventSocketProtocol, self).__init__(*args, **kwargs)
And the definition and __init__() for the Freeswitch class is here:
class Freeswitch(client.EventSocketProtocol, TwistedLoggingMixin):
def __init__(self, *args, **kwargs):
self.jobs = {}
self.defer_until_authenticated = defer.Deferred() # This is the problem
client.EventSocketProtocol.__init__(self, *args, **kwargs)
TwistedLoggingMixin.__init__(self)
Even though I know that this is running and the defer_until_authenticated is being set as well as it's callback and errback, when I call this:
live_call = yield self._create_client_dial_live_call(client_dial.cid, client_dial.campaign)
pchannel = yield self.realm.get_or_create_channel_driver(live_call.uuid, 'prospect')
# ...
client_dial.prospect_channel = pchannel
yield pchannel.freeswitch_protocol.defer_until_authenticated # This is the problem here!
I get the error:
type object 'ProspectEventSocketProtocol' has no attribute 'defer_until_authenticated'
I have no idea why I can't get the attribute again. I know it is being set, but I have no idea where it goes... or what happens to it. I've searched the error and I have no idea what is happening in this spot.
Just for reference, here are the _create_client_dial_live_call() and get_or_create_channel_driver() functions:
def _create_client_dial_live_call():
# ...
p, created = Prospect.objects.get_or_create_client_dial_prospect(campaign, cid_num)
# ...
live_call = LiveCall(prospect=p, campaign=campaign.slug)
live_call.channel_vars_dict = chan_vars
live_call.save()
# ...
def get_or_create_channel_driver()
# The code is kind of confusing with even more context,
# it basically either gets the existing ProspectChannel
# object or creates a new one and then returns it.
Something somewhere is forgetting to instantiate a class.
The error is not telling you that an instance of the class ProspectEventSocketProtocol has no attribute defer_until_authenticated. It's telling you that the class ProspectEventSocketProtocol itself has no such attribute.
In other words, you are quite probably writing something like
pchannel.freeswitch_protocol = ProspectEventSocketProtocol
when you want
pchannel.freeswitch_protocol = ProspectEventSocketProtocol(...)
instead.
Here's a quick demo script that reproduces the error message you are seeing:
#!/usr/bin/env python3
class Test(object):
def __init__(self):
self.arg = "1234"
correct = Test()
print(correct.arg)
wrong = Test
print(wrong.arg)
When I run it, I get the following output:
1234
Traceback (most recent call last):
File "./type_object_error.py", line 12, in <module>
print(wrong.arg)
AttributeError: type object 'Test' has no attribute 'arg'

Decorator to invoke instance method

I have a class A with method do_something(self,a,b,c) and another instance method that validates the input and check permissions named can_do_something(self,a,b,c).
This is a common pattern in my code and I want to write a decorator that accepts a validation function name and perform the test.
def validate_input(validation_fn_name):
def validation_decorator(func):
def validate_input_action(self,*args):
error = getattr(self,validation_fn_name)(*args)
if not error == True:
raise error
else:
return func(*args)
return validate_input_action
return validation_decorator
Invoking the functions as follows
#validate_input('can_do_something')
def do_something(self,a,b,c):
return a + b + c
Problem is that i'm not sure how to maintain self through out the validation function. I've used the validation fn name with getattr so the fn could be ran in the context of the instance but i cannot do that for func(*args).
So what is the proper way to achieve this ?
Thanks.
EDIT
So following #André Laszlo answer I realized that self is just the first argument so there is no need to use getattr at all but just pass on the *args.
def validate_input(validation_fn):
def validation_decorator(func):
def validate_input_action(*args):
error = validation_fn(*args)
if not error == True:
raise error
else:
return func(*args)
return validate_input_action
return validation_decorator
Much more elegant and it also supports static methods as well.
Adding a static method to #André Laszlo example proves the decorator is working :
class Foo(object):
#staticmethod
def validate_baz(a,b,c):
if a > b:
return ValueError('a gt b')
#staticmethod
#validate_input(Foo.validate_baz)
def baz(a,b,c):
print a,b,c
>>> Foo.baz(1,2,3)
1 2 3
>>> Foo.baz(2,1,3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in validate_input_action
ValueError: a gt b
But, when i'm trying to do them same thing in a django model:
from django.db import models
from django.conf import settings
settings.configure()
class Dummy(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10)
def can_say_name(self):
if name is None:
return Exception('Does not have a name')
#validate_input(can_say_name)
def say_name(self):
print self.name
#staticmethod
def can_create_dummy(name):
if name == 'noname':
return Exception('No name is not a name !')
#staticmethod
#validate_input(Dummy.can_create_dummy)
def create_dummy(name):
return Dummy.objects.create(name=name)
I get the following :
NameError: name 'Dummy' is not defined
So what is the different between a django model and an Object in relation to this issue ?
I think this does what you want:
def validate_input(validation_fn_name):
def validation_decorator(func):
def validate_input_action(self, *args):
error = getattr(self, validation_fn_name)(*args)
if error is not None:
raise error
else:
arglist = [self] + list(args)
return func(*arglist)
return validate_input_action
return validation_decorator
class Foo(object):
def validate_length(self, arg1):
if len(arg1) < 3:
return ValueError('%r is too short' % arg1)
#validate_input('validate_length')
def bar(self, arg1):
print "Arg1 is %r" % arg1
if __name__ == "__main__":
f = Foo()
f.bar('hello')
f.bar('')
Output is:
Arg1 is 'hello'
Traceback (most recent call last):
File "validator.py", line 27, in <module>
f.bar('')
File "validator.py", line 6, in validate_input_action
raise error
ValueError: '' is too short
Updated answer
The error (NameError: name 'Dummy' is not defined) occurs because the Dummy class is not defined yet when the validate_input decorator gets Dummy as an argument. I guess this could have been implemented differently, but for now that's the way Python works. The easiest solution that I see is to stick to using getattr, which will work because it looks up the method at run time.

Overriding decorated subclass methods

I'm fiddling around with inheritance and found a behavior that seems strange to me---namely, that some times I can override a parent decorator function (used for validation), but sometimes I cannot, and I cannot understand why or what the difference is.
A quick walkthrough in words---I have a person object I'd like subclass to a more particular person object. The more particular one will have an additional field, "Dance," and will have different validation rules on a previous field, "name."
Here's my base case which works:
# Define the validation wrapper
def ensure(name, validate, doc=None):
def decorator(Class):
privateName = "__" + name
def getter(self):
return getattr(self, privateName)
def setter(self, value):
validate(name, value)
setattr(self, privateName, value)
setattr(Class, name, property(getter, setter, doc=doc))
return Class
return decorator
# Define the not string validation
def is_not_str(name, value):
if isinstance(value, str):
raise ValueError("{} cannot be a string.".format(name))
# Chosen to be exact opposite of above---demonstrating it's possible to reverse.
def is_str(name, value):
if not isinstance(value, str):
raise ValueError("{} must be a string.".format(name))
#ensure("name", is_str)
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = s.get('name',{})
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_not_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s) # idiom to inherit init
self.dance = s.get('dance') # add new param.
bill = Person({"name":"bill",
"url":"http://www.example.com"})
fred = Crazyperson({"name":1,
"url":"http://www.example.com",
"dance":"Flamenco"})
This works fine. So, the first object, bill, is created in such a way that the validation is_str succeeds. If you try to put a number there, it fails. The second object, likewise, accepts non-strings, so fred is created successfully.
Now, here's the case where it breaks, which I'd like to understand...
def is_Name(name, value):
if not isinstance(value, dict) and not isinstance(value,Name):
raise ValueError("{} must be a valid Name object".format(name))
# new object that will be a non-string type of name.
#ensure("firstname", is_str)
#ensure("lastname", is_str)
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
def __str__(self):
return "Name({{'firstname':'{}','lastname':'{}' }})".format(self.firstname, self.lastname)
def __repr__(self):
return str(self)
#ensure("name", is_Name) # require it as the default for the base class
#ensure("url", is_str)
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
def __str__(self):
return "Person({{'name':'{}','url':'{}'}})".format(self.name, self.url)
def __repr__(self):
return str(self)
#ensure("name", is_str) # require a number rather than a Name() object.
class Crazyperson(Person):
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.name = s.get('name','') # THIS IS THE KEY
self.dance = s.get('dance')
bill = Person({"name":{"firstname":"Bill", "lastname":"billbertson"},
"url":"http://www.example.com"})
fred = Crazyperson({"name":"Fred",
"url":"http://www.example.com",
"dance":"Flamenco"})
In this instance, the Crazyperson fails. The error suggests that the is_Name validation function in the __init__ is still being applied:
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "<stdin>", line 4, in __init__
File "<stdin>", line 5, in __init__
File "<stdin>", line 5, in __init__
AttributeError: 'str' object has no attribute 'get'
It looks like it has called the Name initializer: Name(s.get('name',{})) on the string name "Fred".
But it seems it can't be, because in the previous example, I was able to remove a completely contradictory validation (is_str versus is_not_str). Why is this less opposite but failing more? In the first case it wasn't applying both is_str and is_not_str, why is it /now/ applying both is_Name and is_str with seemingly identical syntax?
My question is: what's different about the first way of doing this that causes it to succeed from the second way? I've tried to isolate variables here, but don't understand why I can undo the wrapped validator inherited from the parent class in Scenario I but cannot do what seems similar in Scenario II. It seems the only meaningful difference is that it's an object instead of a string.
(I understand that the better architectural way to do this would be to have a third more abstract parent class, with no validation rules that need changing---and both kinds of person would inherit from that. But I also understand I am supposed to be able to change methods in subclasses, so I'd like to at least understand the difference between why one is succeeding and the other failing here.)
In your second setup, the is_Name function is not applied. You are creating Name object, regardless, in the __init__ method:
class Person(object):
def __init__(self,s):
self.name = Name(s.get('name',{}))
self.url = s.get('url','')
Note the self.name = Name(...) line there.
In Crazyperson.__init__() you call the parent method:
def __init__(self,s):
super(Crazyperson,self).__init__(s)
self.dance = s.get('dance')
passing on s to Person.__init__() which creates a Name() object.
So when you create fred with fred = Crazyperson({"name":"Fred", ...}) you are passing name set to the string 'Fred' to Name.__init__(), which expected a dictionary instead:
class Name(object):
def __init__(self,s):
self.firstname = s.get('firstname','')
self.lastname = s.get('lastname')
and this is where your code fails:
>>> 'Fred'.get('firstname', '')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'get'
Only set name on Person if no self.name has been set yet:
class Person(object):
def __init__(self,s):
if not hasattr(self, 'name')
self.name = Name(s.get('name', {}))
self.url = s.get('url','')
and set name first in Crazyperson:
def __init__(self,s):
self.name = s.get('name', 0)
self.dance = s.get('dance')
super(Crazyperson,self).__init__(s)

Categories