Is it possible monkey patch like this?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Dec 18 19:54:13 EST 2012


On Tue, 18 Dec 2012 02:26:42 -0800, Marc Aymerich wrote:

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



-- 
Steven



More information about the Python-list mailing list