Re: [Tutor] monkey patching question

2015-02-18 Thread Albert-Jan Roskam


- Original Message -

> From: Dave Angel 
> To: tutor@python.org
> Cc: 
> Sent: Tuesday, February 17, 2015 11:50 PM
> Subject: Re: [Tutor] monkey patching question
> 
> 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.
> 
> 





Hi Dave, Danny, 


aha, now I see why it does not work. The code is patched, but it's too little, 
too late. 


>> 
>>  original decorator was called
>>  u'python rocks' 
> 
> 
> I think you're going to have to patch meth2().  Patching decode won't 
> help, since it's already done its work.


But in reality there is meth2 through methn, where n is at least a dozen. I 
considered monkey patching because it seemed easy to experiment with and the 
use case for which this would be needed was not very common. And it's fun to 
experiment with it of course. But I will have to refactor my decode decorator. 
Thanks!!

Albert-Jan
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] monkey patching question

2015-02-17 Thread Danny Yoo
Apologies, but the answer might be unsatisfactory.  The syntactic use
of decorators is confusing the situation.  Let's simplify.  Your
question is equivalent to the following scenario:


def logged(g):
def wrapped(x):
print "call"
return g(x)
return wrapped

square = logged(lambda x: x * x)
#

where we can only interact with the resulting environment afterwards.

The function that you want to mock out has *already* been called by
the time you have control.

Monkey patching as a technique works only under late binding, when
there's a name that you can use to swap out an original binding with a
new one.  But in the situation above, that's not applicable at all.
The function value is not being referred to by some name that's
externally accessible, so there's no handle to monkey-patch.


Can you do something else instead besides trying to monkey-patch?
Even if it were possible to do, if you can correct the original code,
that might be preferable and use less magic.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] monkey patching question

2015-02-17 Thread Dave Angel

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



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



--
DaveA
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


[Tutor] monkey patching question

2015-02-17 Thread Albert-Jan Roskam
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"


#  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' 
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' 
 
Regards,

Albert-Jan




~~

All right, but apart from the sanitation, the medicine, education, wine, public 
order, irrigation, roads, a 

fresh water system, and public health, what have the Romans ever done for us?

~~ 
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor