🙆
Python で動的な plugin を実装したい
やりたいことは、plugins ディレクトリに作ったファイルを自動的に import してプラグインとして呼び出すということです。世間一般にどうやってプラグインを実装してるのかわからなくて自分で作ってみました。プラグインの追加削除をファイルの有無で動的に行うことを目指しました。
ここでは基底クラスを Parent としています。プラグインは Parent を継承している必要があります。call_method() ですべてのプラグインが持つ同名のメソッドを呼び出します。もしプラグインが存在しなければ Parent の hello() メソッドを呼び出します。ただ、本来は親から各プラグインのメソッドを呼び出すべきなので、このやり方だと無限ループになりそう・・・
TODO:
- methodの有無をチェック
- 有効にするプラグインを選べるようにする
- Parentからプラグインを呼び出せるようにする
import importlib
import os
class Plugin:
"""
Dynamically import Python scripts (*.py) in plugins directory.
"""
PLUGINS_DIR = 'plugins'
def __init__(self, base:type):
self.base_class:type = base
self.base_class_name:str = base.__name__
self.plugins:list = []
self.objs:list = []
def plugin_names(self):
"""
Return list of module names in the plugins directory
"""
file:str
module_names:list = []
for file in os.listdir(self.PLUGINS_DIR):
if file.endswith('.py') and file != '__init__.py':
module_names.append(self.PLUGINS_DIR + '.' + os.path.splitext(file)[0])
return module_names
def import_plugins(self):
"""
Import modules in the plugins directory
"""
module_name:str
for module_name in self.plugin_names():
module = importlib.import_module(module_name)
self.plugins += getattr(module, self.base_class_name).__subclasses__()
def init_objs(self):
"""
Create class instances and return as a list of objects
"""
if len(self.objs) > 0:
return self.objs
if len(self.plugins) == 0:
self.objs = [self.base_class()]
else:
for p in self.plugins:
self.objs.append(p())
return self.objs
def call_method(self, method_name:str):
"""
Call method of plugins (or base class if no plugin loaded)
TODO: Give arguments for the method
"""
for obj in self.init_objs():
method = getattr(obj, method_name)
method()
if __name__ == '__main__':
from parent import Parent
parent_plugin = Plugin(Parent)
parent_plugin.import_plugins()
parent_plugin.call_method('hello')
Discussion