[Zope-dev] A Modest Proposal Concerning Monkey Patches

jlagarde@bigfoot.com jlagarde@bigfoot.com
Tue, 13 Aug 2002 11:10:40 -0700


On Tue Aug 13, 2002, Jim Penny wrote:
> There is a large problem looming with Moneky Patches. The problem is
> that monkey patches are so Highlander. "There can be Only One".
> 
> For example, there are at least five or six products that monkey patch
> manage_main. Each simply replaces whatever manage_main exists at the
> time of instantation, and blows away any previous monkey patch. Some
> coordinated way of dealing with this problem needs to be arrived at.
>

And if you upgrade Zope, the patched version of manage_main will discard
any changes made in the new version of Zope.

> 
> Proposal:
[ideas on how to check for previous changes and what to do about it]

Here is a more surgical option for small changes: Instead of
indiscriminately replacing the whole function or method, decompile and
look for and replace only that part of the code that you want to change.
In that way, as long as that part of the code remains valid between
versions, the change probably remains valid, and you apply it. If you
can't find the part of the code that you want to change, then some other
change you are not aware of happened, so do not apply your change. I use
decompyle and re to do the work. Here is an example:

-- code snippet from my site monkey patch product --
from decompyle import decompyle
import re
import cStringIO

# -------------
# 'Fix' dtml-in so previous-sequence and next-sequence are
# available everywhere in the batch
from DocumentTemplate.DT_In import InClass

tochangeF = cStringIO.StringIO()
decompyle(InClass.renderwb.im_func.func_code,out=tochangeF)
tochange = tochangeF.getvalue()
tochangeF.close()

# The change is to remove the if index == first | last conditions
print 'Changing dtml-in to provide previous and next sequence everywhere
in the batch'
tochange,num =
re.subn(r"if\s+\(index\s*==\s*(first|last)\):\s*pkw\['(previous|next)-se
quence'\]\s*=\s*1",
                  r"pkw['\2-sequence'] = 1\n",tochange)
if num == 2:
    print 'Success'
    # Replacement seemed successful, so go ahead and make the swap
    exec 'def renderwb(self,md):\n    ' + tochange.replace('\n','\n
')[:-4]
    InClass.renderwb = renderwb
else:
    print 'Was expected to make 2 changes, got %s. Check the DT_In code'
% num
-- end code snippet --

Of course, some monkey patches are so incompatible that even that
approach might not work or produce unexpected results.

Cheers,

Jean