Archive

Tag Archives: monkey patching

If you are familiar with writing Django applications, you have probably run across the problem of extending the builtin User authentication model. Django does not yet have the hooks necessary for modifying the User object in a nice way, so you more or less have to resort to monkey patching.

Here is the basic monkey patching pattern I have seen:

def user_get_name(self):
    # do something with the user object which is self
    return "%s, %s" % (self.last_name, self.first_name)

User.get_name = user_get_name

Or if it is really just a one liner you can use a lambda, which avoids dirtying up the local namespace of wherever you are performing the monkey patching:

User.get_name = lambda self: "%s, %s" % (self.last_name, self.first_name)

The first monkey patching pattern makes reading the code incredibly painful (at least to me) and the lambda pattern isn't much better either.

Decorator Pattern

You can perform the same operations in a more readable manner using decorators. Here is what it would look like:

def monkeypatch(cls):
    def decorator(f):
        setattr(cls, f.__name__, f)
    return decorator

Now to monkey patch the get_name method of a User object, you would do this:

@monkeypatch(User)
def get_name(self):
    return "%s, %s" % (self.last_name, self.first_name)

I personally think this is a bit more readable. The real advantage to using a monkeypatch decorator though, is that you call out the fact that you are monkey patching. While reading the above code, it is very clear that monkey patching business is going on.

Monkey patching is almost never the best way to accomplish what you're trying to do, but it will often get the job done fast. To remind yourself that you should revisit any monkey patching code later and think of a better way to do it, consider renaming the decorator to XXXmonkeypatch.

Class decorators with python 2.6

If you are using python2.6, you can also use monkey patching decorators on entire classes. Here is an example of such a decorator:

def monkeypatch(cls_to_patch):
    def decorator(cls):
        cls_to_patch.__bases__ += (cls,)
        return cls
    return decorator

You would use this decorator like so:

@monkeypatch(User)
class MyUser:
    def get_name(self):
        return "%s, %s" % (self.last_name, self.first_name)

    def get_initials(self):
        return self.first_name[0]+self.last_name[0]

The main caveat with this method is that MyUser actually becomes a base class to User so if User ever gets a new method of the same name as one of your monkey patch methods, the User version will take precedence. This might be a feature depending on what exactly it is you are doing.

Follow

Get every new post delivered to your Inbox.

Join 76 other followers

%d bloggers like this: