On Tue, Oct 25, 2011 at 10:08 AM, Peter Otten <__pete...@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 -- http://mail.python.org/mailman/listinfo/python-list