Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

Pluginシステムの実装

ちょっとPlugin的なものを実装しようとしてたら行き着いた。
http://lucumr.pocoo.org/2006/7/3/python-plugin-system
importする仕組みはよくあるのでわかるんだけど。
読み込んだモジュールからPluginクラスを抽出するスマートな方法が書いてあったので紹介しておく。

class Plugin(object):
    pass

class MyPlugin1(Plugin):
    pass

class MyPlugin2(Plugin):
    pass

Plugin.__subclasses__()

__subclasses__()で一発でとれる。
baseのtuple(スーパークラスのリスト)はよく使うんだけどsubclassも取れるのは忘れてた。

で自分でも書いてみた。

plugin.py
import sys
import os
import os.path as path

default_plugin_path = path.join(path.abspath(path.curdir), 'plugins')

class Plugin(object):
    pass

def default_plugin():
    default = path.join(path.dirname(__file__), 'plugins')
    return list_module(default)

def list_module(dir):
    if path.isdir(dir):
        return [path.splitext(file)[0] for file in os.listdir(dir) if not file.startswith('__')]
    return []

def init_plugins(cfg):
    sys.path.insert(0, path.join(path.dirname(path.abspath(__file__)), 'plugins'))
    plugin_path = cfg.get('plugin_path', default_plugin_path)
    plugins = cfg.get('plugins', list_module(plugin_path))
    if not plugin_path in sys.path:
        sys.path.insert(0, plugin_path)
    plugins.extend(default_plugin()) 
    import_plugins(plugins)

def import_plugins(plugins):
    for plugin in plugins:
        __import__(plugin, None, None, [''])

def find_plugins():
    return Plugin.__subclasses__()

def load_plugins(cfg={}):
    init_plugins(cfg)
    return find_plugins()

自分自身がおいてあるディレクトリ内の'plugins'ディレクトリと実行時のカレントディレクトリ内の'plugins'を読み込む。
(configで一部置き換えれる)

使うときはload_pluginsでクラスのリストを受け取ってインスタンス化する

for plugin in load_plugins():
    p = plugin()

まあホントはインスタンス化、カテゴリ化、シングルトンもpluginモジュールでやってもいいんだけど手抜きで。