Never knowingly knowing narwhals

Python Style Plugins Made Easy


Sometimes you need to write code that loads python at runtime. Plugin architectures are a good example of this. Plugins allow extensibility but more importantly (for me at least) they enforce a strict API. Anyway, I've written this code a few times so I thought I'd modularize it.

The specific bit of code I am going to post is the python code to look for and load a series of python plugins. Plugins (in this case) are just classes that are subclasses of the Plugin base class. This plugin base class dictates the API that all plugins must implement. Here is an example plugin abstract base class

class Plugin(object):
    def setup(self):
        """called before the plugin is asked to do anything"""
        raise NotImplementedError
 
    def teardown(self):
        """called to allow the plugin to free anything"""
        raise NotImplementedError
 
    def domagic(self):
        """do whatever it is the plugin does"""
        raise NotImplementedError
 

Here the is code to look for and return a list of classes that can be instantiated:

def find_subclasses(path, cls):
    """
    Find all subclass of cls in py files located below path
    (does look in sub directories)
 
    @param path: the path to the top level folder to walk
    @type path: str
    @param cls: the base class that all subclasses should inherit from
    @type cls: class
    @rtype: list
    @return: a list if classes that are subclasses of cls
    """
 
    subclasses=[]
 
    def look_for_subclass(modulename):
        log.debug("searching %s" % (modulename))
        module=__import__(modulename)
 
        #walk the dictionaries to get to the last one
        d=module.__dict__
        for m in modulename.split('.')[1:]:
            d=d[m].__dict__
 
        #look through this dictionary for things
        #that are subclass of Job
        #but are not Job itself
        for key, entry in d.items():
            if key == cls.__name__:
                continue
 
            try:
                if issubclass(entry, cls):
                    log.debug("Found subclass: "+key)
                    subclasses.append(entry)
            except TypeError:
                #this happens when a non-type is passed in to issubclass. We
                #don't care as it can't be a subclass of Job if it isn't a
                #type
                continue
 
    for root, dirs, files in os.walk(path):
        for name in files:
            if name.endswith(".py") and not name.startswith("__"):
                path = os.path.join(root, name)
                modulename = path.rsplit('.', 1)[0].replace('/', '.')
                look_for_subclass(modulename)
 
    return subclasses
 

and here is how you would call it:

classes = find_subclasses("./pluginsfolder/", Plugin)
 
#lets create an instance of the first class
inst = classes[0]()
 

So there you go, create folder of python files with classes in them that subclass the Plugin class and you are away.

7 Comments to Python Style Plugins Made Easy

  1. May 10, 2008 at 9:09 pm | Permalink

    Good example. However, it there is a slight problem with the directory name that is passed to the “find_subclasses” function. The code works better without “./” from the path to the plugins directory.

    klasses = find_classes(“pluginsfolder”, Plugin)

  2. stargaming's Gravatar stargaming
    June 9, 2008 at 5:24 am | Permalink

    Is there any advantage of the module __dict__ hassle over just using Plugin.__subclasses__()?

  3. June 9, 2008 at 2:54 pm | Permalink

    Hi dazza,

    Armin R. has written an article with the __subclasses__() methods here :

    http://lucumr.pocoo.org/blogarchive/python-plugin-system

    See you.

  4. June 10, 2008 at 3:58 pm | Permalink

    I’ve been looking for something like this, exactly. Thanks!

    I had managed to hack up something using isinstance() and looping through dir(filename)

  5. March 6, 2010 at 11:31 pm | Permalink

    Cool Thanks for the article. I am just starting python and this will help a lot.

2 Trackbacks to Python Style Plugins Made Easy

  1. By on June 9, 2008 at 7:22 pm
  2. By on February 28, 2011 at 4:59 pm

Leave a Reply

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>