[Tutor] monkey patching question

Dave Angel davea at davea.name
Tue Feb 17 23:50:04 CET 2015


On 02/17/2015 04:53 PM, Albert-Jan Roskam wrote:
> Hi,
>
> I would like to monkey patch a function 'decode' that is defined inside a class. It is defined there because it is a logical place, next to its counterpart *method* 'encode'. I can successfully monkey patch meth1, but when I call meth2, it does not use the patched decorator. How can this be done? In this example, I would like to effectively "turn off" @decode. I am hoping for a solution that works on Python 2.7 and 3.3+.
>
>
> import inspect, functools
> class Foo(object):
>
>      def decode(func):
>          @functools.wraps(func)
>          def wrapped(*args, **kwargs):
>              print "original decorator was called"
>              return func(*args, **kwargs).decode("utf-8")
>          return wrapped
>
>      def encode(self):
>          """this is just here to show why decode() is defined
>          within Foo"""
>          pass
>
>      def meth1(self):
>          return "original method was called"
>
>      @decode
>      def meth2(self):
>          return b"python rocks"
>

I assume the monkey patching happens in some other file which will 
import this one.

So by the time the import is finished, the meth2() method has already 
been decorated by the decode function.

>
> # ---- works -----
> f = Foo()
> print f.meth1()
> Foo.meth1 = lambda self: "new method was called"
> print f.meth1()
> print "-------------------"
>
>
> # ---- does not work -----
> def patched_decode(func):
>      @functools.wraps(func)
>      def wrapped(*args, **kwargs):
>          print "patched decorator was called"
>          return func(*args, **kwargs)
>      return wrapped
>
> f = Foo()
> print 'ORIGINAL'
> print inspect.getsource(Foo.decode)  # shows source code of regular decode (as expected)
> result = f.meth2()
> print repr(result), type(result)
>
> #setattr(Foo, "decode", patched_decode)
> Foo.decode = patched_decode
>
> print 'PATCHED'
> print inspect.getsource(f.decode)  # shows source code of patched_decode (as expected)
> result = f.meth2()
> print repr(result), type(result)   # not patched at all! it's still unicode!
>
>
>
> ##### output:
> In [1]: %run monkey_patch.py
> original method was called
> new method was called
> -------------------
> ORIGINAL
> def decode(func):
>             @functools.wraps(func)
>      def wrapped(*args, **kwargs):
>          print "original decorator was called"
>          return func(*args, **kwargs).decode("utf-8")
>      return wrapped
>
> original decorator was called
> u'python rocks' <type 'unicode'>
> PATCHED
> def patched_decode(func):
>         @functools.wraps(func)
>      def wrapped(*args, **kwargs):
>          print "patched decorator was called"
>          return func(*args, **kwargs)
>      return wrapped
>
> original decorator was called
> u'python rocks' <type 'unicode'>


I think you're going to have to patch meth2().  Patching decode won't 
help, since it's already done its work.


-- 
DaveA


More information about the Tutor mailing list