How to wrap (monkey patch) @classmethod

class-method, decorator, monkeypatching, python, python-decorators

Solution

When you access a method on a class, it is wrapped at that moment; methods act as descriptors here.

You may want to unwrap the method again, returning a wrapped wrapper:

def foo_wrapper(wrapped_func):
    wrapped_func = wrapped_func.__func__
    def _w(*args, **kwargs):
        print '<foo_wrap>'
        wrapped_func(*args, **kwargs)
        print '</foo_wrap>'
    return classmethod(_w)

Now the returned decorator is itself a class method, and wrapping works:

>>> class A:
...     @classmethod
...     def foo(kls, param):
...         print 'A.foo called, param is ' + param
... 
>>> def foo_wrapper(wrapped_func):
...     wrapped_func = wrapped_func.__func__
...     def _w(*args, **kwargs):
...         print '<foo_wrap>'
...         wrapped_func(*args, **kwargs)
...         print '</foo_wrap>'
...     return classmethod(_w)
... 
>>> A.foo = foo_wrapper(A.foo)
>>> A.foo('bar')
<foo_wrap>
A.foo called, param is bar
</foo_wrap>

Problem

I want to monkey patch one single classmethod, keeping old functionality. Consider my code to get the idea. Here is my code (pretty synthetic example). ``` #!/usr/bin/env python class A: @classmethod def foo(kls, param): print 'A.foo called, param is ' + param def bar(self, param): print 'A.bar called, param is ' + param a = A() a.foo('param_foo') a.bar('param_bar') # Patching things def bar_wrapper(wrapped_func): def _w(*args, **kwargs): print '<bar_wrap>' wrapped_func(*args, **kwargs) print '</bar_wrap>' return _w def foo_wrapper(wrapped_func): # Something missing here? def _w(*args, **kwargs): print '<foo_wrap>' wrapped_func(*args, **kwargs) print '</foo_wrap>' return _w # Everything is pretty ok A.bar = bar_wrapper(A.bar) a.bar('is_is_wrapped?') # Failed to wrap @classmethod A.foo = foo_wrapper(A.foo) A.foo('another_wrap_test') ``` This is what I expect to output: ``` A.foo called, param is param_foo A.bar called, param is param_bar <bar_wrap> A.bar called, param is is_is_wrapped? </bar_wrap> <foo_wrap> A.foo called, param is another_wrap_test </foo_wrap> ``` And this is what I get: ``` A.foo called, param is param_foo A.bar called, param is param_bar <bar_wrap> A.bar called, param is is_is_wrapped? </bar_wrap> Traceback (most recent call last): File "./pytest.py", line 39, in <module> A.foo('another_wrap_test') TypeError: unbound method _w() must be called with A instance as first argument (got str instance instead) ``` Seems like one parameter (class-param) was lost during wrapping. Or I just don't the idea of decorating functions? Thanks in advance.

Original source