Replacing methods and attributes at runtime. MakeCode programs can be authored in Blocks, Static TypeScript or Static Python.
Monkey-patching is the technique of swapping functions or methods with others in order to change a module, library or class behavior.
There are some people with strong opinions about it. I haven’t, but it comes really useful when testing, to simulate side-effecting functions or to silence expected errors and warnings.
Class methods monkey patching in Python is really easy, as you can freely assign function to class method names:
This way all the instances of the target class will have the method monkey-patched and there is no problem with arguments, bindings… Everything really straight-forward.
We can also call the old existing method, to handle only some cases or to add some functionality while not repeating code (DRY):
But what if we wanted to do the same, patching just a single instance?
To recap, the requirements are:
- we want just the current instance to be patched;
- we want to build something on top of the existing method, not to replace it entirely;
- we want each monkey-patch not to rollback all the previous ones (so no
super()
or class method call); - we want to be able to do so also from inside a method.
The trick is to save and use the existing method as we did above, and then bind the new function to the instance with types.MethodType
before assigning it to the method name.
The binding is the magic that causes the instance to be passed as first argument (self
) each time the method is called. See thesetwo StackOverflow questions to get an idea.
And here we go!
A practical example
You can see this technique being used in youtube-dl to silence expected warnings in this commit.
The monkey-patching of the instance is done on itself by a method of a testing subclass of the downloader.
Today one of my coworkers came and got me so that I could explain some weird Python code they’d found. It dealt with cmake, but since it was internal code, I won’t be able to show it here. Instead, I wrote up something that has the same issues so you can see what I consider bad, or at the very least, very obtuse code:
When I first saw this, I was struck by how it was using attributes that weren’t initialized and I wondered how this could work. Then I realized they must be doing some kind of monkey patching. Monkey patching is where you write code to dynamically modify a class or module at run time. So I looked farther down the script and found some code that created an instance of the class and did something like this:
Python Monkey Patch Static Method Tutorial
So basically whenever you create an instance of the class, you need to patch it so that the attributes exist before you call the Run method.
While I think it’s pretty cool that Python can do this sort of thing, it is extremely confusing to someone who is not familiar with Python, especially when the code was as poorly documented as this was. As you can see, there are no comments or docstrings, so it took a bit of digging to figure out what was going on. Fortunately, all the code was in the same file. Otherwise this could have gotten really tricky to figure out.
Iron On Patches
I personally thought this was a good example of bad coding. If I had written it, I would have created an __init__ method and initialized all those attributes there. Then there would have been no confusion about how the class worked. I am also a big believer in writing good docstrings and useful comments.
Anyway, I hope you found this interesting. I also thought my readers should be aware of some of the oddball pieces of code you’ll see in the wild.