Saturday 30 December 2006

Live coding / code reloading for Python

Several months ago there was a spate of interest in live coding with Python. Those who experimented with it reasonably took the approach of continually calling the reload method. However this only worked when code was implemented in a stateless way and how to code within this limitation was covered in detail in the blog posts about it.

I commented on one of the later posts (although my link placement was a little mixed-up) gathering together enough information needed which happened to be publically available and could be used to write the support needed for live coding without the mentioned limitation. To recap that information:

But the general interest in this sort of thing died down and no-one stepped forward to do the work needed. I certainly was not going to do it, as while in theory it would work, it wasn't the approach I would take anyway. Tracking instances so that they can be individually updated seems like much more work than should be required.

A more usable solution would be to update the class itself which would transparently make the changes available to all instances. Though actually implementing this is not as straightforward as one might hope. For instance how functions from classes even unbound are indirectly linked to the class they came from is one of the little hidden surprises of how Python is implemented.

And this "more usable solution" is what I have released, combined with the code from the links above to handle the detection of file changes:
Live coding v 1.0
However there was a design decision made which need to be taken into account. Namely that this is only made possible by using a custom importing solution. All the code which is have automatic code reloading has to be under this solution. Why it was used I will cover after I show how to add a directory containing Python scripts to be made available

This will add a directory of code as importable modules:
import livecoding
cm = livecoding.CodeManager()
cm.AddDirectory("c:\app\server", "applib")

Let's say that 'c:\app' contains the following directories and files:
server\services\networking.py
server\objects\session.py
Where 'networking.py' specifies the class NetworkService and 'session.py' specifies the class Session. Under the algorithm used to map directory contents to an importable namespace, NetworkService would be placed as applib.services.NetworkService and Session would be placed as applib.objects.Session.

So these classes could be imported in the normal manner:
from applib.services import NetworkService
from applib.objects import Session

So why use the custom importing solution? Well, it makes updating loaded code with changes easier because as the library controls the importing it knows what to update and how. But why not look into getting a similar system working with the standard module system? Not interested, sorry. My fellow author and I have worked a lot with custom importing systems and found them to be much easier to work with than the standard module system. And over time we have come to prefer the system which this library implements.

I also cobbled together another library filechanges which comes as part of the download or can also be obtained from the SVN archive. This is just a collection of the file change detection code found in the links I originally gathered above. The livecoding library can be told to use this to provide built in file change detection.
import livecoding
cm = livecoding.CodeManager(detectChanges=True)
cm.AddDirectory("c:\app\server", "applib")

Which completes the collection of functionality which makes up the v1.0 release of this library. However there is still work remaining to do. There is the incomplete override feature, clean up needed regarding custom methods of handling the file update events and more methods of file change detection other than the only one which currently works (the cross-platform one).