Skip to content Skip to sidebar Skip to footer

Should A Plugin Adding New Instance-methods Monkey-patch Or Subclass/mixin And Replace The Parent?

As a simple example take a class Polynomial class Polynomial(object): def __init__(self, coefficients): self.coefficients = coefficients for polynomials of the form

Solution 1:

The one and probably most important property of monkey-patching the function directly to the original class instead of replacing it is that references to / instances of the original class before the plugin was loaded will now also have the new attribute. In the given example, this is most likely desired behaviour and should therefore be used.

There may however be other situations where a monkey-patch modifies the behaviour of an existing method in a way that is not compatible with its original implementation and previous instances of the modified class should use the original implementation. Granted, this is not only rare but also bad design, but you should keep this possibility in mind. For some convoluted reasons code might even rely on the absence of a monkey-patch-added method, though it seems hard to come up with a non-artificial example here.

In summary, with few exceptions monkey-patching the methods into the original class (preferably with a hasattr(...) check before patching) should be the preferred way.


edit My current go: create a subclass (for simpler code completion and patching) and then use the following patch(patching_class, unpatched_class) method:

import logging
from types import FunctionType, MethodType


logger = logging.getLogger(__name__)
applied_patches = []


classPatchingError(Exception):
    passdefpatch(subcls, cls):
    ifnot subcls in applied_patches:
        logger.info("Monkeypatching %s methods into %s", subcls, cls)
        for methodname in subcls.__dict__:
            if methodname.startswith('_'):
                logger.debug('Skipping methodname %s', methodname)
                continue# TODO treat modified initelifhasattr(cls, methodname):
                raise PatchingError(
                    "%s alrady has methodname %s, cannot overwrite!",
                    cls, methodname)
            else:
                method = getattr(subcls, methodname)
                logger.debug("Adding %s %s", type(method), methodname)
                method = get_raw_method(methodname, method)
                setattr(cls, methodname, method)
        applied_patches.append(subcls)


defget_raw_method(methodname, method):
    # The following wouldn't be necessary in Python3...# http://stackoverflow.com/q/18701102/321973iftype(method) == FunctionType:
        logger.debug("Making %s static", methodname)
        method = staticmethod(method)
    else:
        asserttype(method) == MethodType
        logger.debug("Un-bounding %s", methodname)
        method = method.__func__
    return method

The open question is whether the respective subclass' module should directly call patch on import or that should be done manually. I'm also considering writing a decorator or metaclass for such a patching subclass...

Post a Comment for "Should A Plugin Adding New Instance-methods Monkey-patch Or Subclass/mixin And Replace The Parent?"