Strange classmethod mock behavior

Peter Otten __peter__ at web.de
Tue Oct 25 08:08:08 EDT 2011


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'>





More information about the Python-list mailing list