問題のコードをインポートするために、アプリケーションが実行時(またはそれ以降)にスキャンするプラグインディレクトリを持つことができるはずです。これは、zipファイル内に保存されているプラグインでも機能する通常の.pyまたは.pycコードで機能するはずの例です(ユーザーは「plugins」ディレクトリにsomeplugin.zipをドロップするだけで、魔法のように機能させることができます)。
import re, os, sys
class Plugin(object):
"""
The base class from which all plugins are derived. It is used by the
plugin loading functions to find all the installed plugins.
"""
def __init__(self, foo):
self.foo = foo
# Any useful base plugin methods would go in here.
def get_plugins(plugin_dir):
"""Adds plugins to sys.path and returns them as a list"""
registered_plugins = []
#check to see if a plugins directory exists and add any found plugins
# (even if they're zipped)
if os.path.exists(plugin_dir):
plugins = os.listdir(plugin_dir)
pattern = ".py$"
for plugin in plugins:
plugin_path = os.path.join(plugin_dir, plugin)
if os.path.splitext(plugin)[1] == ".zip":
sys.path.append(plugin_path)
(plugin, ext) = os.path.splitext(plugin) # Get rid of the .zip extension
registered_plugins.append(plugin)
elif plugin != "__init__.py":
if re.search(pattern, plugin):
(shortname, ext) = os.path.splitext(plugin)
registered_plugins.append(shortname)
if os.path.isdir(plugin_path):
plugins = os.listdir(plugin_path)
for plugin in plugins:
if plugin != "__init__.py":
if re.search(pattern, plugin):
(shortname, ext) = os.path.splitext(plugin)
sys.path.append(plugin_path)
registered_plugins.append(shortname)
return registered_plugins
def init_plugin_system(cfg):
"""
Initializes the plugin system by appending all plugins into sys.path and
then using load_plugins() to import them.
cfg - A dictionary with two keys:
plugin_path - path to the plugin directory (e.g. 'plugins')
plugins - List of plugin names to import (e.g. ['foo', 'bar'])
"""
if not cfg['plugin_path'] in sys.path:
sys.path.insert(0, cfg['plugin_path'])
load_plugins(cfg['plugins'])
def load_plugins(plugins):
"""
Imports all plugins given a list.
Note: Assumes they're all in sys.path.
"""
for plugin in plugins:
__import__(plugin, None, None, [''])
if plugin not in Plugin.__subclasses__():
# This takes care of importing zipped plugins:
__import__(plugin, None, None, [plugin])
したがって、アプリケーションに新しい機能を追加する「plugins」というディレクトリ(アプリのベースディレクトリにある)に「foo.py」という名前のプラグインがあるとします。内容は次のようになります。
from plugin_stuff import Plugin
class Foo(Plugin):
"""An example plugin."""
self.menu_entry = {'Tools': {'Foo': self.bar}}
def bar(self):
return "foo plugin!"
次のようにアプリを起動すると、プラグインを初期化できます。
plugin_dir = "%s/plugins" % os.getcwd()
plugin_list = get_plugins(plugin_dir)
init_plugin_system({'plugin_path': plugin_dir, 'plugins': plugin_list})
plugins = find_plugins()
plugin_menu_entries = []
for plugin in plugins:
print "Enabling plugin: %s" % plugin.__name__
plugin_menu_entries.append(plugin.menu_entry))
add_menu_entries(plugin_menu_entries) # This is an imaginary function
プラグインが.pyまたは.pycファイルである限り、これは機能するはずです(問題のプラットフォーム用にバイトコンパイルされていると仮定します)。スタンドアロンファイルでも、init .pyのあるディレクトリ内でも、同じルールのzipファイル内でもかまいません。
これが機能することをどうやって知ることができますか?これが私がPyCIにプラグインを実装した方法です。PyCIはWebアプリケーションですが、この方法が通常の古いGUIで機能しない理由はありません。上記の例では、GUIのメニューにプラグインのメソッドを追加するために使用できるPluginオブジェクト変数と組み合わせて架空のadd_menu_entries()関数を使用することを選択しました。
この回答が、独自のプラグインシステムの構築に役立つことを願っています。実装方法を正確に確認したい場合は、PyCIソースコードをダウンロードして、plugins_enabledディレクトリにあるplugin_utils.pyとExampleプラグインを確認することをお勧めします。