Is it possible monkey patch like this?

M

Marc Aymerich

Dear all,
I want to monkey patch a method that has lots of code so I want to avoid copying all the original method for changing just two lines. The thing is that I don't know how to do this kind of monkey patching.

Consider the following code:

class OringinalClass(object):
def origina_method(self, *args, **kwargs):
...
if some_condition(): # This condition should be changed
raise SomeException
...
if some_condition():
...
#if some_condition(local_variable): # This condition should be added
# raise SomeException
...


Is it possible to tell Python to run the original method without stopping when an exception is raised? so I can catch them on a wrapper method and apply my conditional there.

Any other idea on how to monkey patch those two conditionals ?


Thanks!
 
C

Chris Angelico

Any other idea on how to monkey patch those two conditionals ?

Would it be plausible to simply add a parameter to the function that
controls its behaviour? It'd be a lot more readable than fiddling from
the outside ever would.

ChrisA
 
P

Peter Otten

Marc said:
Dear all,
I want to monkey patch a method that has lots of code so I want to avoid
copying all the original method for changing just two lines. The thing is
that I don't know how to do this kind of monkey patching.

Consider the following code:

class OringinalClass(object):
def origina_method(self, *args, **kwargs):
...
if some_condition(): # This condition should be changed
raise SomeException
...
if some_condition():
...
#if some_condition(local_variable): # This condition should be
#added
# raise SomeException
...


Is it possible to tell Python to run the original method without stopping
when an exception is raised?
No.

so I can catch them on a wrapper method and
apply my conditional there.

Any other idea on how to monkey patch those two conditionals ?

One of the cleanest alternatives is to factor out the condition in the
original class and then use a subclass:

class Original:
def method(self, *args, **kw):
self.check_condition(...)
...
def check_condition(self, ...):
if condition:
raise SomeException

class Sub(Original):
def check_condition(self, ...):
pass

If you insist on monkey-patching possible solutions depend on the actual
conditions. If some_condition() is a function, replace that function. If it
is actually an expression tweak the arguments. E. g:
.... def method(self, x):
.... if x < 0: raise ValueError
.... print x * x
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
.... def __lt__(self, other): return False # a blunt lie
....4

This tends to get complex quickly, so in the long run you will not be happy
with that approach...
 
T

Terry Reedy

I want to monkey patch a method that has lots of code so I want to
avoid copying all the original method for changing just two lines.

You omitted the most important piece of information. Can you modify the
original code (or get someone else to do so) or must you leave it as is?
Chris and Peter gave you answers for the former case. If the latter, you
must copy and modify for the change you specified.
 
S

Steven D'Aprano

Dear all,
I want to monkey patch a method that has lots of code so I want to avoid
copying all the original method for changing just two lines. The thing
is that I don't know how to do this kind of monkey patching.

The only types of monkey-patching supported by Python are overriding or
overloading. In the first, you override a function by replacing it with a
new function of your own design; in the second, you save the original
somewhere, then replace it with a new function that wraps the old:

# overloading the len() function
_len = len
def len(obj):
print "calling len on object %r" % obj
print "returning result as a string"
return str(_len(obj))


You cannot monkey-patch in the middle of a function.

Technically, you could hack the byte code of the function, but such a
thing is not supported or documented anywhere. If you want to learn more,
you can google on "python byte code hacks", but let me warn you that this
is advanced, and dark, territory where you will get little or no help
when things go wrong.

Consider the following code:

class OringinalClass(object):
def origina_method(self, *args, **kwargs):
...
if some_condition(): # This condition should be changed
raise SomeException

If some_condition is a function or method call, you can patch the
function or method. That may require subclassing another object. E.g.

if args[2].startswith('spam'): ... # CHANGE ME

instead of passing a string as args[2], you could subclass string to
patch startswith, then pass a subclass instance instead of a string:

result = instance.original_method(x, y, 'spam', z)

becomes:

result = instance.original_method(x, y, MyStr('spam'), z)

You could even patch original_method to automatically MyStr-ify args[2].

But in general, if the condition is an arbitrary expression:

if x < y or z > 2 and spam is not None: # CHANGE ME

there is nothing you can do except replace the entire method.
if some_condition():
...
#if some_condition(local_variable): # This condition should
be added # raise SomeException
...

Pretty much the same applies. If you can move the new condition to the
start or end of the method, you can patch. To insert it in an arbitrary
spot in the middle, forget it.
Is it possible to tell Python to run the original method without
stopping when an exception is raised? so I can catch them on a wrapper
method and apply my conditional there.

No. Your request doesn't even make sense. What are you going to catch if
no exception is raised? What do you expect the method to do if it doesn't
stop? Consider:

def original(self, x, y):
z = x + y
print z
return "hello world"[z]


Suppose you pass x=1, y={} so that x+y fails. What do you expect Python
to do if you tell it not to stop at an exception? What will it print?
What result should it return?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top