Python:お手軽にPluggableにする
Pythonで自分用の小物アプリを結構書き溜めてるんですけど、実はそのほとんどにプラグインシステムみたいなのをつくってたりします。
たとえばファイルの整理自動化みたいなの。ファイルの移動前に処理を追加する、てな部分をプラグインにしてあるわけです。
まぁ機能的にはCPANのClass::Componentっぽいもんですね。ですがそこまで高機能なのはいらないので、シンプルに自分が必要な部分だけまとめてみました。
pluggable.py
PYTHON:
- from inspect import getmembers, ismethod
- import types
- class Pluggable(object):
- def __init__(self):
- self.hooks = {}
- def load(self, *config):
- self.load_config(*config)
- self.load_plugins()
- def load_config(self, *config):
- self.config = []
- for i in config:
- if isinstance(i, ("".__class__, u"".__class__)):
- self.config.append((i, {}))
- else:
- self.config.append((i[0], len(i)> 1 and i[1] or {}))
- def load_plugins(self):
- for klass,config in self.config:
- plugin = None
- try:
- plugin = eval(klass)
- except (NameError, AttributeError),e:
- names = klass.split(".")
- mod = __import__(".".join(names[0:-1]))
- globals()[names[0]] = mod
- for name in names[1:-1]: mod = getattr(mod, name)
- plugin = getattr(mod, names[-1])
- p = plugin(config)
- make_closure = lambda obj,name: lambda *a, **k: getattr(obj,name)(*a, **k)
- for name, method in ((n,m) for n,m in getmembers(p) if ismethod(m)):
- if hasattr(method, "__plugin_method__"):
- setattr(self, name, make_closure(p, name))
- for hook_type in getattr(method, "__hook_types__", []):
- if hook_type not in self.hooks: self.hooks[hook_type] = []
- self.hooks[hook_type].append(getattr(p, name))
- def _run_hooks(self, dct, f=lambda x:x):
- result = {}
- for (name,value) in dct.iteritems():
- if name not in self.hooks: continue
- k,a = {}, value
- if isinstance(value[-1], types.DictType):
- k,a = value[-1],value[0:-1]
- result[name] = [m(*a, **k) for m in f(self.hooks[name])]
- return result
- def run_hooks(self, dct):
- return self._run_hooks(dct)
- def reverse_run_hooks(self, dct):
- return self._run_hooks(dct, reversed)
- class Plugin(object):
- def __init__(self, config):
- self.config = config
- method = lambda f: setattr(f, "__plugin_method__", True) or f
- hook = lambda *args: lambda f: setattr(f, "__hook_types__", args) or f
機能的には
- クラスに対して設定とともにメソッドを追加する(setattrします)
- 任意のhookの登録と実行
ぐらいです。
使い方は
plugins.py
PYTHON:
- import pluggable
- class People(pluggable.Pluggable):
- pass
- class Hacker(pluggable.Plugin):
- @pluggable.method
- def hack(self):
- print self.config["msg"]
- @pluggable.hook("hello")
- def hello(self, msg1, msg2, msg3):
- print "Hello World!!", msg1, msg2, msg3
- class Otaku(pluggable.Plugin):
- @pluggable.method
- def niconico(self, msg):
- print self.config["msg"], msg
- @pluggable.hook("hello")
- def hello(self, msg1, msg2, msg3):
- print u"にぱー", msg1, msg2, msg3
みたいにプラグインクラスを作っておいて
PYTHON:
- p = People()
- p.load(
- ("plugins.Hacker", {"msg": "Happy Hacking!"}),
- ("plugins.Otaku", {"msg": u"ニコ厨ですが"})
- )
- p.hack() #=> Happy Hacking!
- p.niconico(u"なにか?") #=> ニコ厨ですが なにか?
- p.run_hooks({"hello":([1,2],{"msg3":3})})
- #=> Hello World!! 1 2 3
- #=> にぱー 1 2 3
- p.reverse_run_hooks({"hello":[1,2,3]})
- #=> にぱー 1 2 3
- #=> Hello World!! 1 2 3
という感じで使います。あとは設定の部分をyamlなんかに押し出せばオッケーというわけ。
モジュールのインポートは自動でやってくれます。Pythonは地味に動的なimportが面倒だったので、ライブラリにまとめたことでだいぶ手元の小物アプリがすっきりしました。
- 2008/01/27 若干修正を加えました。
About this entry
You’re currently reading “Python:お手軽にPluggableにする,” an entry on inforno
- Published:
- 11.08.07 / 1am




No comments
Jump to comment form | trackback uri [?]