Dynamically reload class definition in Python

I wrote an IRC bot using Twisted and now I got to the point where I want to dynamically reload the functionality.

In my main program I am doing from bots.google import GoogleBot

and I have looked at how to use reload

to reload modules, but I still cannot figure out how to do dynamic re-import of classes.

So, given a Python class, how do I dynamically reload a class definition?


source to share

7 replies

I got it, here is the code I'm using:

def reimport_class(self, cls):
    Reload and reimport class "cls".  Return the new definition of the class.

    # Get the fully qualified name of the class.
    from twisted.python import reflect
    full_path = reflect.qual(cls)

    # Naively parse the module name and class name.
    # Can be done much better...
    match = re.match(r'(.*)\.([^\.]+)', full_path)
    module_name = match.group(1)
    class_name = match.group(2)

    # This is where the good stuff happens.
    mod = __import__(module_name, fromlist=[class_name])

    # The (reloaded definition of the) class itself is returned.
    return getattr(mod, class_name)




Reboot is unreliable and has many corner cases where it can fail. It is suitable for reloading simple, stand-alone scripts. If you want to dynamically reload your code without restarting, use forkloop instead:




When you execute from ... import ...

, it binds the object to the local namespace, so whatever you need it re-imports. However, since the module is already loaded, it just re-imports the same version of the class so that you also have to reload the module. So this should do it:

from bots.google import GoogleBot
# do stuff
from bots.google import GoogleBot


If for some reason you don't know the name of the module, you can get it from GoogleBot. module .



Better still the plugin subprocess, and then hypervise the subprocess when the files change the plugin process reload.

Edit: cleaned up.



You cannot reload a module using reload(module)

when using a form from X import Y

. In this case, you need to do something like reload(sys.modules['module'])


This may not be the best way to do what you want, but it works!

import bots.google

class BotClass(irc.IRCClient):
    def __init__(self):
        global plugins
        plugins = [bots.google.GoogleBot()]

    def privmsg(self, user, channel, msg):
        global plugins
        parts = msg.split(' ')
        trigger = parts[0]
        if trigger == '!reload':
            plugins = [bots.google.GoogleBot()] 
            print "Successfully reloaded plugins"




You can use sys.modules

to dynamically reload modules based on user input.

Say you have a folder with multiple plugins, for example:



You can use sys.modules

this way to load / reload modules based on userinput:

import sys

if sys.modules['module.' + userinput]:
    reload(sys.modules['module.' + userinput])

    ' Module not loaded. Cannot reload '
        module = __import__("module." + userinput)
        module = sys.modules["module." + userinput]
        ' error when trying to load %s ' % userinput




def reload_class(class_obj):
    module_name = class_obj.__module__
    module = sys.modules[module_name]
    pycfile = module.__file__
    modulepath = string.replace(pycfile, ".pyc", ".py")
    code=open(modulepath, 'rU').read()
    compile(code, module_name, "exec")
    module = reload(module)
    return getattr(module,class_obj.__name__)


There are many mistakes you can make about this, if you use globals you may have to figure out what happens next.



All Articles