damd
damd

Reputation: 6957

Dynamically reload a class definition in Python

I've written an IRC bot using Twisted and now I've gotten to the point where I want to be able to dynamically reload functionality.

In my main program, I do from bots.google import GoogleBot and I've looked at how to use reload to reload modules, but I still can't figure out how to do dynamic re-importing of classes.

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

Upvotes: 5

Views: 7573

Answers (7)

Sam P
Sam P

Reputation: 721

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 is a lot of error checking you can do on this, if your using global variables you will probably have to figure out what happens then.

Upvotes: 0

damd
damd

Reputation: 6957

I figured it out, here's the code I use:

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])
    reload(mod)

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

Upvotes: 1

aquavitae
aquavitae

Reputation: 19114

When you do a from ... import ... it binds the object into the local namespace, so all you need to is re-import it. However, since the module is already loaded, it will just re-import the same version of the class so you would need to reload the module too. So this should do it:

from bots.google import GoogleBot
...
# do stuff
...
reload(bots.google)
from bots.google import GoogleBot

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

Upvotes: 0

veiset
veiset

Reputation: 2003

You can use the sys.modules to dynamically reload modules based on user-input.

Say that you have a folder with multiple plugins such as:

module/
  cmdtest.py
  urltitle.py
  ...

You can use sys.modules in this way to load/reload modules based on userinput:

import sys

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

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

Upvotes: 0

Mikko Ohtamaa
Mikko Ohtamaa

Reputation: 83328

Reload is unreliable and has many corner cases where it may fail. It is suitable for reloading simple, self-contained, scripts. If you want to dynamically reload your code without restart consider using forkloop instead:

http://opensourcehacker.com/2011/11/08/sauna-reload-the-most-awesomely-named-python-package-ever/

Upvotes: 2

erm
erm

Reputation: 11

You cannot reload the module using reload(module) when using the from X import Y form. You'd have to do something like reload(sys.modules['module']) in that case.

This might not necessarily 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':
            reload(bots.google)
            plugins = [bots.google.GoogleBot()] 
            print "Successfully reloaded plugins"

Upvotes: 1

Jakob Bowyer
Jakob Bowyer

Reputation: 34698

Better yet subprocess the plugins, then hypervise the subprocess, when the files change reload the plugins process.

Edit: cleaned up.

Upvotes: 0

Related Questions