pluginbase が便利そう。
@mitsuhiko 先生の新作の pluginbase が便利そうというお話。
Python は他の Python なファイルをモジュールとして動的に読み込む場合は import があるから少し面倒。
例えば今作ってる Flask を使ったアプリケーションで、WSGI ミドルウェアを読み込む時には以下の様にしている。
def configure_middlewares(app): middlewares = app.config['MIDDLEWARES'] if not isinstance(middlewares, tuple): middlewares = (middlewares,) for middleware in middlewares: target = middleware.split('.') module = '.'.join(target[0:-1]) name = target[-1] klass = getattr(__import__(module, fromlist=[name]), name) app.wsgi_app = klass(app)
app.config['MIDDLEWARES'] には、WSGI ミドルウェア*1を格納しているパスを与えて、ファイル名からを使って、__import__() と getattr() を使っている。
klass(app) としている通り、クラスという制約を課して読み込んでいる*2。
こういう風に、自分で頑張る必要がある。
現状これで困ってないけど、もっとシンプルにプラグインを読み出すフレームワークが pluginbase。
ディレクトリ構成はこんな感じ。
. ├── app.py └── plugins ├── __init__.py ├── bar.py └── foo.py
app.py がエントリポイントで、plugins は以下のような感じのファイル。
foo.py
# -*- coding: utf-8 -*- def say(s): return 'hello {0}'.format(s)
bar.py
# -*- coding: utf-8 -*- class Bar(object): def __init__(self): print('init') def say(self, name): return 'say {0}'.format(name)
foo.py は関数定義、bar.py はクラスの定義をした。
app.py はこんな感じ。
#!/usr/bin/env python # -*- coding: utf-8 -*- import os from functools import partial from pluginbase import PluginBase here = os.path.dirname(os.path.abspath(__file__)) get_path = partial(os.path.join, here) if __name__ == '__main__': plugin_base = PluginBase(package='plugins') plugin_source = plugin_base.make_plugin_source(searchpath=[ get_path('plugins') ]) my_plugin = plugin_source.load_plugin('foo') ret = my_plugin.say('hoo') print(ret) my_plugin_class = plugin_source.load_plugin('bar') ret = my_plugin_class.Bar().say('fooo') print(ret)
Plugin となるディレクトリのパスとディレクトリ名を設定して、ファイル名を呼び出すとモジュールがロードされる。
関数の場合はそのまま、関数オブジェクトが呼べる。
クラスの場合は、クラス名が呼べるので、インスタンスを生成すれば行ける。
実行結果
$ python app.py hello hoo init say fooo
お手軽!!