Strange classmethod mock behavior
Fabio Zadrozny
fabiofz at gmail.com
Tue Oct 25 18:59:26 EDT 2011
On Tue, Oct 25, 2011 at 10:08 AM, Peter Otten <__peter__ at web.de> wrote:
> Fabio Zadrozny wrote:
>>> I'm trying to mock a classmethod in a superclass but when restoring it
>> a strange behavior happens in subclasses (tested on Python 2.5)
>>>> Anyone knows why this happens? (see test case below for details)
>> Is there any way to restore that original method to have the original
>> behavior?
>>>> import unittest
>>>> class Test(unittest.TestCase):
>>>> def testClassmethod(self):
>> class Super():
>> @classmethod
>> def GetCls(cls):
>> return cls
>>>> class Sub(Super):
>> pass
>>>> self.assertEqual(Sub.GetCls(), Sub)
>>>> original = Super.GetCls
>> #Mock Super.GetCls, and do tests...
>> Super.GetCls = original #Restore the original classmethod
>> self.assertEqual(Sub.GetCls(), Sub) #The call to the
>> classmethod subclass returns the cls as Super and not Sub as expected!
>>>> if __name__ == '__main__':
>> unittest.main()
>> [Not related to your problem] When working with descriptors it's a good idea
> to use newstyle classes, i. e. have Super inherit from object.
>> The Super.GetCls attribute access is roughly equivalent to
>> Super.__dict___["GetCls"].__get__(classmethod_instance, None, Super)
>> and returns an object that knows about its class. So when you think you are
> restoring the original method you are actually setting the GetCls attribute
> to something else. You can avoid the problem by accessing the attribute
> explicitly:
>>>>> class Super(object):
> ... @classmethod
> ... def m(cls): return cls
> ...
>>>> bad = Super.m
>>>> good = Super.__dict__["m"]
>>>> class Sub(Super): pass
> ...
>>>> Sub.m()
> <class '__main__.Sub'>
>>>> Super.m = bad
>>>> Sub.m()
> <class '__main__.Super'>
>>>> Super.m = good
>>>> Sub.m()
> <class '__main__.Sub'>
>
Hi Peter, thanks for the explanation.
Printing it helped me understand it even better...
print(Super.__dict__['GetCls'])
print(Super.GetCls)
<classmethod object at 0x022AF210>
<bound method type.GetCls of <class '__main__.Super'>>
Cheers,
Fabio
More information about the Python-list
mailing list