Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #35044 > unrolled thread

Is it possible monkey patch like this?

Started byMarc Aymerich <glicerinu@gmail.com>
First post2012-12-18 02:26 -0800
Last post2012-12-19 00:54 +0000
Articles 5 — 5 participants

Back to article view | Back to comp.lang.python


Contents

  Is it possible monkey patch like this? Marc Aymerich <glicerinu@gmail.com> - 2012-12-18 02:26 -0800
    Re: Is it possible monkey patch like this? Chris Angelico <rosuav@gmail.com> - 2012-12-18 21:34 +1100
    Re: Is it possible monkey patch like this? Peter Otten <__peter__@web.de> - 2012-12-18 13:35 +0100
    Re: Is it possible monkey patch like this? Terry Reedy <tjreedy@udel.edu> - 2012-12-18 11:49 -0500
    Re: Is it possible monkey patch like this? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-12-19 00:54 +0000

#35044 — Is it possible monkey patch like this?

FromMarc Aymerich <glicerinu@gmail.com>
Date2012-12-18 02:26 -0800
SubjectIs it possible monkey patch like this?
Message-ID<bbfb8154-e283-4961-9680-9d819bf00d01@googlegroups.com>
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!

[toc] | [next] | [standalone]


#35048

FromChris Angelico <rosuav@gmail.com>
Date2012-12-18 21:34 +1100
Message-ID<mailman.1014.1355828213.29569.python-list@python.org>
In reply to#35044
On Tue, Dec 18, 2012 at 9:26 PM, Marc Aymerich <glicerinu@gmail.com> wrote:
> 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

[toc] | [prev] | [next] | [standalone]


#35052

FromPeter Otten <__peter__@web.de>
Date2012-12-18 13:35 +0100
Message-ID<mailman.1017.1355834069.29569.python-list@python.org>
In reply to#35044
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.
> 
> 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:

>>> class Original:
...     def method(self, x):
...             if x < 0: raise ValueError
...             print x * x
... 
>>> Original().method(-2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in method
ValueError
>>> class Int(int):
...     def __lt__(self, other): return False # a blunt lie
... 
>>> Original().method(Int(-2))
4

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

[toc] | [prev] | [next] | [standalone]


#35063

FromTerry Reedy <tjreedy@udel.edu>
Date2012-12-18 11:49 -0500
Message-ID<mailman.1023.1355849362.29569.python-list@python.org>
In reply to#35044
On 12/18/2012 5:26 AM, Marc Aymerich wrote:

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

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#35085

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-12-19 00:54 +0000
Message-ID<50d11034$0$29991$c3e8da3$5496439d@news.astraweb.com>
In reply to#35044
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

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web