

def monkeypatch(target):

def patcher(func):

setattr(target, func.__name__, func)

return func

return patcher



import MyClass



@monkeypatch(MyClass)

def myfunc():

...





def monkeypatch_reduce(target, reducefunc):

"""

A decorator factory that chains monkey patches, in the

case where separate patches may modify the same class.

"""

def patcher(func):

try:

_oldfunc = getattr(target, func.__name__)

except AttributeError:

setattr(target, func.__name__, func)

return func

def inner(*args, **kwargs):

return reducefunc(

_oldfunc(*args, **kwargs),

func(*args, **kwargs))

setattr(target, func.__name__, inner)

return inner

return patcher





from Products.ZenModel import Device



@monkeypatch_reduce(Device, reducefunc=lambda x,y:x and y)

def validate_device(dev):

return dev.fulfills_some_criteria()



@monkeypatch_reduce(Device, reducefunc=lambda x,y:x and y)

def validate_device(dev):

return dev.fulfills_other_criteria()





ZenPack.py:

-----------

def zenpack_list(self):

return []



A ZenPack:

----------

from Products.ZenModel import ZenPack

@monkeypatch_reduce(ZenPack, operator.add)

def zenpack_list(self):

return ["A Pack"]



A different ZenPack:

--------------------

from Products.ZenModel import ZenPack

@monkeypatch_reduce(ZenPack, operator.add)

def zenpack_list(self):

return ["A Different Pack"]







>>> ZenPack.zenpack_list()

["A Pack", "A Different Pack"]

