Skip to main content

Python: Reload source files

When I was writing BUTT - an irc bot, I wanted to create a mini plugin system, to add or modify code without restarting any process. Python allows this natively using the imp module. In the following examples, I’ll show how to use the load_source procedure to load and parse a new source file.

Here a very simple module we’ll import during execution time:

#!/usr/bin/env python

class TestModule:
    def __init__(self):
        pass

    def myfunction(self):
        print "Calling myfunction"

Let’s load module:

#!/usr/bin/env python

import os, shutil
import imp

def load_module(module_name):
    print "Loading module %s" % module_name

    modfile = os.path.join(os.getcwd(), module_name + '.py')

    module = imp.load_source(module_name, modfile)

    print module
    # Returns <module 'test' from '/home/mycroft/imp/test.pyc'>

    print dir(module)
    # Permet de lister les differents attributs de l'objet importé.
    # Returns ['TestModule', '__builtins__', '__doc__', '__file__', '__name__', '__package__']

    for objname in dir(module):
        # Je veux specifiquement les modules suffixés "Module":
        if objname.endswith('Module'):
            modclass = getattr(module, objname)
            print modclass
            # Returns test.TestModule
            print dir(modlass)
            # Returns ['__doc__', '__init__', '__module__', 'myfunction']
            return modclass

    return None

Just testing the whole thing, with reloading a module:

if __name__ == '__main__':

    modclass = load_module('test')
    instance = modclass()
    instance.myfunction()

    # We move away test.py, and copy test_new.py to test.py.

    print

    shutil.move('test.py', 'test_old.py')
    shutil.move('test_new.py', 'test.py')

    modclass = load_module('test')
    instance = modclass()
    instance.myfunction()

    shutil.move('test.py', 'test_new.py')
    shutil.move('test_old.py', 'test.py')

And we’ll get:

$ ./modules.py
Loading module test
<module 'test' from '/home/mycroft/imp/test.py'>
['TestModule', '__builtins__', '__doc__', '__file__', '__name__', '__package__']
test.TestModule
['__doc__', '__init__', '__module__', 'myfunction']
Calling myfunction

Loading module test
<module 'test' from '/home/mycroft/imp/test.py'>
['TestModule', '__builtins__', '__doc__', '__file__', '__name__', '__package__']
test.TestModule
['__doc__', '__init__', '__module__', 'myfunction']
Calling (new) myfunction