[Python-Dev] Monkeypatching idioms -- elegant or ugly? (original) (raw)

Robert Brewer fumanchu at aminus.org
Tue Jan 15 19:13:25 CET 2008


Guido van Rossum:

I ran into the need of monkeypatching a large number of classes (for what I think are very good reasons :-) and invented two new recipes. These don't depend on Py3k, and the second one actually works all the way back to Python 2.2. Neither of these allows monkeypatching built-in types like list. If you don't know what monkeypatching is, see see http://en.wikipedia.org/wiki/Monkeypatch.

I think it's useful to share these recipes, if only to to establish whether they have been discovered before, or to decide whether they are worthy of a place in the standard library. I didn't find any relevant hits on the ASPN Python cookbook. First, a decorator to add a single method to an existing class: def monkeypatchmethod(cls): def decorator(func): setattr(cls, func.name, func) return func return decorator To use: from import @monkeypatchmethod() def (self, args): return This adds to

I like it, but my first thought was, "and if that method already exists?" I'd extend monkeypatch_method to store a reference to the old method(s):

def monkeypatch_method(cls):
    """Add the decorated method to the given class; replace as

needed.

    If the named method already exists on the given class, it will
    be replaced, and a reference to the old method appended to a

list at cls.old. If the "old" attribute already exists and is not a list, KeyError is raised. """ def decorator(func): fname = func.name

        old_func = getattr(cls, fname, None)
        if old_func is not None:
            # Add the old func to a list of old funcs.
            old_ref = "_old_%s" % fname
            old_funcs = getattr(cls, old_ref, None)
            if old_funcs is None:
                setattr(cls, old_ref, [])
            elif not isinstance(old_funcs, list):
                raise KeyError("%s.%s already exists." %
                               (cls.__name__, old_ref))
            getattr(cls, old_ref).append(old_func)
        
        setattr(cls, fname, func)
        return func
    return decorator

I chose a renaming scheme somewhat at random. The list allows you (or someone else ;) to call monkeypatch repeatedly on the same cls.method (but it's not thread-safe).

And although it might seem to be making monkeypatches easier to perform, at least it's very explicit about what's going on as long as you keep "monkeypatch" in the name.

Robert Brewer fumanchu at aminus.org



More information about the Python-Dev mailing list