自己动手实现python插件框架(python plugin framework 附源码)

python做为一个动态语言,可以很方便的调用,在程序需要的时候去调用,而且是动态调用的。这为程序开发带来了很大的方便。很多程序都采用了插件式开发,因为方便扩展。在python里,有什么好方法实现插件了,我实现了一个简单的插件:
1. 定义一个插件目录,所有插件都放在这个目录里面。
2. 定义插件要实现的基类,主要是为了插件管理分类方便,python作为动态语言,基类,接口没有太大的意义,因为随时可以扩展。
3. 定义插件管理器,用插件管理器去load 插件
4. 测试调用插件
整个程序目录结构如下(我用pydev做的):

iPlugin 是所有插件必须实现的接口,而且里面的name 属性必须定义,插件管理器,根据这个name 去调用插件.
程序代码 程序代码

#coding:utf-8

class Plugin(object):
    """ 定义一个接口,其他 插件必须实现这个接口,name 属性必须赋值 """
    name = ''
    description = ''
    version = ''
    
    def __init__(self):
        pass
    
    def executeFun(self):
        pass


插件管理器的实现
因为可能有很多种方式管理插件,不同的插件管理器,实现方式不一样,所以定义个插件管理器的基类,各种具体的插件管理器是实现这个基类,比如这里实现了一个 基于目录结构去查找插件的 DirectoryPluginManager
程序代码 程序代码

#coding:utf-8

from iPlugin import Plugin
from imp import find_module,load_module,acquire_lock,release_lock
import os
import sys

class PluginManager(object):
    """Base class for plugin managers. Does not implement loadPlugins, so it
    may only be used with a static list of plugins.
    """
    name = "base"

    def __init__(self, plugins=(), config={}):
        self.__plugins = []
        if plugins:
            self.addPlugins(plugins)

    def __iter__(self):
        return iter(self.plugins)

    def addPlugin(self, plug):
        print 'PluginManager add plugin:',plug
        self.__plugins.append(plug)

    def addPlugins(self, plugins):
        for plug in plugins:
            self.addPlugin(plug)

    def delPlugin(self, plug):
        if plug in self.__plugins:
            self.__plugins.remove(plug)

    def delPlugins(self, plugins):
        for plug in plugins:
            self.delPlugin(plug)

    def getPlugins(self,name=None):
        plugins = []
        print 'self.__plugins:',self.plugins
        for plugin in self.__plugins:
            print 'plugin.name',plugin.name
            if (name is None or plugin.name == name):
                plugins.append(plugin)
        return plugins

    def _loadPlugin(self, plug):      
        loaded = False
        print '******PluginManager  _loadPlugin ,',self.plugins
        for p in self.plugins:
            if p.name == plug.name:
                loaded = True
                break
        if not loaded:
            self.addPlugin(plug)
            print "%s: loaded plugin %s " % (self.name, plug.name)

    def loadPlugins(self):
        pass

    def _get_plugins(self):
        return self.__plugins

    def _set_plugins(self, plugins):
        self.__plugins = []
        self.addPlugins(plugins)

    plugins = property(_get_plugins, _set_plugins, None,
                       """Access the list of plugins managed by
                       this plugin manager""")
    
    
class DirectoryPluginManager(PluginManager):
    """Plugin manager that loads plugins from plugin directories.
    """
    name = "directory"

    def __init__(self, plugins=(), config={}):
        default_directory = os.path.join(os.path.dirname(__file__),"plugins")
        self.directories = config.get("directories", (default_directory,))
        print '========DirectoryPlugManager========',plugins
        PluginManager.__init__(self, plugins, config)

    def loadPlugins(self):
        """Load plugins by iterating files in plugin directories.
        """
        plugins = []
        print '********Directory directories:',self.directories
        for dir in self.directories:
            try:
                for f in os.listdir(dir):
                    if f.endswith(".py") and f != "__init__.py":
                        plugins.append((f[:-3], dir))
            except OSError:
                print "Failed to access: %s" % dir
                continue

        fh = None
        mod = None
        print '********Directory all plugins:',plugins
        for (name, dir) in plugins:
            try:
                acquire_lock()
                fh, filename, desc = find_module(name, [dir])
                print '********Directory fh,filename,desc:',fh,filename,desc,name
                old = sys.modules.get(name)
                if old is not None:
                    # make sure we get a fresh copy of anything we are trying
                    # to load from a new path
                    del sys.modules[name]
                mod = load_module(name, fh, filename, desc)
            finally:
                if fh:
                    fh.close()
                release_lock()
            if hasattr(mod, "__all__"):
                print '********Directory mod  __all__:',mod.__all__
                attrs = [getattr(mod, x) for x in mod.__all__]
                print '********Directory attrs:',attrs
                for plug in attrs:
                    if not issubclass(plug, Plugin):
                        continue
                    self._loadPlugin(plug())


在plugin 目录下 写自己的插件,注意必须实现Plugin 接口,并且必须写name属性
程序代码 程序代码

#coding:utf-8
import sys
sys.path.append('../')

from iPlugin import Plugin

__all__ = ["FirstPlugin"]

class FirstPlugin(Plugin):
  
    name = "firstPlugin"
    version = '0.0.1'

    def __init__(self):
        Plugin.__init__(self)

    def scan(self, config={}):
        return "first plugin"
    
    def execFun(self):
        return "exec function"


调用插件
通过插件管理器去调用插件,用你自己需要的插件管理器去调用,我这里只实现了一个基于目录结构的。
程序代码 程序代码

#coding:utf-8

from pluginManager import DirectoryPluginManager
import os

if __name__=='__main__':
    plugin_manager = DirectoryPluginManager()
    plugin_manager.loadPlugins()
    plugins = plugin_manager.getPlugins("firstPlugin")
    
    print "**"*30
    print plugins[0].execFun()

你会发现,DirectoryPluginManager 找到了名字为 firstPlugin 插件,并成功执行了调用,值得注意的是,在所有的 实现的插件里面,我都加入了一个 __all__属性 暴露公开的类,同时在  pluginManager 里面也用到这个属性。

源代码下载:
下载文件 点击下载此文件


除非申明,文章均为一号门原创,转载请注明本文地址,谢谢!
[本日志由 轻舞肥羊 于 2012-11-24 02:00 PM 编辑]
文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags: python
相关日志:
评论: 0 | 引用: 0 | 查看次数: -
发表评论
昵 称:
密 码: 游客发言不需要密码.
内 容:
验证码: 验证码
选 项:
虽然发表评论不用注册,但是为了保护您的发言权,建议您注册帐号.