2010-11-06 06:37:31 +08:00
|
|
|
"""
|
2015-04-22 19:31:46 +08:00
|
|
|
PluginManager, basic initialization and tracing.
|
2010-11-06 06:37:31 +08:00
|
|
|
"""
|
2014-08-01 06:13:40 +08:00
|
|
|
import os
|
2013-10-02 20:32:40 +08:00
|
|
|
import sys
|
2010-10-13 17:12:27 +08:00
|
|
|
import inspect
|
2010-10-12 18:54:32 +08:00
|
|
|
import py
|
2010-10-13 03:59:15 +08:00
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
py3 = sys.version_info > (3,0)
|
|
|
|
|
2010-11-06 06:37:31 +08:00
|
|
|
class TagTracer:
|
2011-07-15 01:11:50 +08:00
|
|
|
def __init__(self):
|
2010-11-06 06:37:31 +08:00
|
|
|
self._tag2proc = {}
|
|
|
|
self.writer = None
|
2010-11-06 16:05:17 +08:00
|
|
|
self.indent = 0
|
2010-11-06 06:37:31 +08:00
|
|
|
|
|
|
|
def get(self, name):
|
|
|
|
return TagTracerSub(self, (name,))
|
|
|
|
|
2012-11-29 17:04:39 +08:00
|
|
|
def format_message(self, tags, args):
|
|
|
|
if isinstance(args[-1], dict):
|
|
|
|
extra = args[-1]
|
|
|
|
args = args[:-1]
|
|
|
|
else:
|
|
|
|
extra = {}
|
|
|
|
|
|
|
|
content = " ".join(map(str, args))
|
|
|
|
indent = " " * self.indent
|
2013-02-04 23:07:51 +08:00
|
|
|
|
2012-11-29 17:04:39 +08:00
|
|
|
lines = [
|
|
|
|
"%s%s [%s]\n" %(indent, content, ":".join(tags))
|
|
|
|
]
|
|
|
|
|
|
|
|
for name, value in extra.items():
|
|
|
|
lines.append("%s %s: %s\n" % (indent, name, value))
|
|
|
|
return lines
|
|
|
|
|
2010-11-06 06:37:31 +08:00
|
|
|
def processmessage(self, tags, args):
|
2012-11-29 17:04:39 +08:00
|
|
|
if self.writer is not None and args:
|
|
|
|
lines = self.format_message(tags, args)
|
|
|
|
self.writer(''.join(lines))
|
2010-11-06 06:37:31 +08:00
|
|
|
try:
|
|
|
|
self._tag2proc[tags](tags, args)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def setwriter(self, writer):
|
|
|
|
self.writer = writer
|
|
|
|
|
|
|
|
def setprocessor(self, tags, processor):
|
|
|
|
if isinstance(tags, str):
|
|
|
|
tags = tuple(tags.split(":"))
|
|
|
|
else:
|
|
|
|
assert isinstance(tags, tuple)
|
|
|
|
self._tag2proc[tags] = processor
|
|
|
|
|
|
|
|
class TagTracerSub:
|
|
|
|
def __init__(self, root, tags):
|
|
|
|
self.root = root
|
|
|
|
self.tags = tags
|
|
|
|
def __call__(self, *args):
|
|
|
|
self.root.processmessage(self.tags, args)
|
|
|
|
def setmyprocessor(self, processor):
|
|
|
|
self.root.setprocessor(self.tags, processor)
|
|
|
|
def get(self, name):
|
|
|
|
return self.__class__(self.root, self.tags + (name,))
|
|
|
|
|
2014-10-07 22:16:47 +08:00
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
def add_method_wrapper(cls, wrapper_func):
|
|
|
|
""" Substitute the function named "wrapperfunc.__name__" at class
|
|
|
|
"cls" with a function that wraps the call to the original function.
|
|
|
|
Return an undo function which can be called to reset the class to use
|
|
|
|
the old method again.
|
2014-10-07 22:16:47 +08:00
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
wrapper_func is called with the same arguments as the method
|
|
|
|
it wraps and its result is used as a wrap_controller for
|
|
|
|
calling the original function.
|
2014-10-07 22:16:47 +08:00
|
|
|
"""
|
2014-10-08 17:27:14 +08:00
|
|
|
name = wrapper_func.__name__
|
2014-10-04 21:49:31 +08:00
|
|
|
oldcall = getattr(cls, name)
|
|
|
|
def wrap_exec(*args, **kwargs):
|
2014-10-08 17:27:14 +08:00
|
|
|
gen = wrapper_func(*args, **kwargs)
|
|
|
|
return wrapped_call(gen, lambda: oldcall(*args, **kwargs))
|
2014-10-07 22:16:47 +08:00
|
|
|
|
2014-10-04 21:49:31 +08:00
|
|
|
setattr(cls, name, wrap_exec)
|
|
|
|
return lambda: setattr(cls, name, oldcall)
|
|
|
|
|
2014-10-09 18:21:01 +08:00
|
|
|
def raise_wrapfail(wrap_controller, msg):
|
|
|
|
co = wrap_controller.gi_code
|
|
|
|
raise RuntimeError("wrap_controller at %r %s:%d %s" %
|
|
|
|
(co.co_name, co.co_filename, co.co_firstlineno, msg))
|
2014-10-04 21:49:31 +08:00
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
def wrapped_call(wrap_controller, func):
|
2014-10-09 18:21:01 +08:00
|
|
|
""" Wrap calling to a function with a generator which needs to yield
|
|
|
|
exactly once. The yield point will trigger calling the wrapped function
|
|
|
|
and return its CallOutcome to the yield point. The generator then needs
|
|
|
|
to finish (raise StopIteration) in order for the wrapped call to complete.
|
2014-10-08 17:27:14 +08:00
|
|
|
"""
|
2014-10-09 02:23:40 +08:00
|
|
|
try:
|
|
|
|
next(wrap_controller) # first yield
|
|
|
|
except StopIteration:
|
2014-10-09 18:21:01 +08:00
|
|
|
raise_wrapfail(wrap_controller, "did not yield")
|
2014-10-08 17:27:14 +08:00
|
|
|
call_outcome = CallOutcome(func)
|
|
|
|
try:
|
|
|
|
wrap_controller.send(call_outcome)
|
2014-10-09 18:21:01 +08:00
|
|
|
raise_wrapfail(wrap_controller, "has second yield")
|
2014-10-08 17:27:14 +08:00
|
|
|
except StopIteration:
|
|
|
|
pass
|
2014-10-09 02:23:40 +08:00
|
|
|
return call_outcome.get_result()
|
2014-10-08 17:27:14 +08:00
|
|
|
|
|
|
|
|
|
|
|
class CallOutcome:
|
2014-10-09 18:21:01 +08:00
|
|
|
""" Outcome of a function call, either an exception or a proper result.
|
|
|
|
Calling the ``get_result`` method will return the result or reraise
|
|
|
|
the exception raised when the function was called. """
|
2014-10-08 17:27:14 +08:00
|
|
|
excinfo = None
|
|
|
|
def __init__(self, func):
|
|
|
|
try:
|
|
|
|
self.result = func()
|
2015-04-17 17:47:29 +08:00
|
|
|
except BaseException:
|
2014-10-08 17:27:14 +08:00
|
|
|
self.excinfo = sys.exc_info()
|
|
|
|
|
|
|
|
def force_result(self, result):
|
|
|
|
self.result = result
|
|
|
|
self.excinfo = None
|
|
|
|
|
2014-10-09 02:23:40 +08:00
|
|
|
def get_result(self):
|
|
|
|
if self.excinfo is None:
|
|
|
|
return self.result
|
|
|
|
else:
|
|
|
|
ex = self.excinfo
|
|
|
|
if py3:
|
|
|
|
raise ex[1].with_traceback(ex[2])
|
|
|
|
py.builtin._reraise(*ex)
|
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
class PluginManager(object):
|
2015-04-22 19:31:46 +08:00
|
|
|
def __init__(self, prefix, excludefunc=None):
|
|
|
|
self._prefix = prefix
|
|
|
|
self._excludefunc = excludefunc
|
2010-10-12 18:54:32 +08:00
|
|
|
self._name2plugin = {}
|
2010-10-13 17:12:27 +08:00
|
|
|
self._plugins = []
|
2014-10-09 16:47:32 +08:00
|
|
|
self._plugin2hookcallers = {}
|
2010-11-06 16:05:17 +08:00
|
|
|
self.trace = TagTracer().get("pluginmanage")
|
2013-09-29 04:23:00 +08:00
|
|
|
self._shutdown = []
|
2015-04-22 19:31:46 +08:00
|
|
|
self.hook = HookRelay(pm=self)
|
2013-09-30 19:14:14 +08:00
|
|
|
|
2014-10-02 21:25:42 +08:00
|
|
|
def set_tracing(self, writer):
|
|
|
|
self.trace.root.setwriter(writer)
|
2014-10-04 21:49:31 +08:00
|
|
|
# reconfigure HookCalling to perform tracing
|
|
|
|
assert not hasattr(self, "_wrapping")
|
|
|
|
self._wrapping = True
|
|
|
|
|
|
|
|
def _docall(self, methods, kwargs):
|
2014-10-02 21:25:42 +08:00
|
|
|
trace = self.hookrelay.trace
|
|
|
|
trace.root.indent += 1
|
|
|
|
trace(self.name, kwargs)
|
2014-10-08 17:27:14 +08:00
|
|
|
box = yield
|
|
|
|
if box.excinfo is None:
|
|
|
|
trace("finish", self.name, "-->", box.result)
|
|
|
|
trace.root.indent -= 1
|
2014-10-04 21:49:31 +08:00
|
|
|
|
2014-10-08 17:27:14 +08:00
|
|
|
undo = add_method_wrapper(HookCaller, _docall)
|
2014-10-04 21:49:31 +08:00
|
|
|
self.add_shutdown(undo)
|
2014-10-02 21:25:42 +08:00
|
|
|
|
2013-10-04 17:36:45 +08:00
|
|
|
def do_configure(self, config):
|
|
|
|
# backward compatibility
|
|
|
|
config.do_configure()
|
|
|
|
|
2013-09-30 19:14:14 +08:00
|
|
|
def set_register_callback(self, callback):
|
|
|
|
assert not hasattr(self, "_registercallback")
|
|
|
|
self._registercallback = callback
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2015-04-22 19:31:46 +08:00
|
|
|
def make_hook_caller(self, name, plugins):
|
|
|
|
caller = getattr(self.hook, name)
|
|
|
|
methods = self.listattr(name, plugins=plugins)
|
|
|
|
if methods:
|
|
|
|
return HookCaller(self.hook, caller.name, caller.firstresult,
|
|
|
|
argnames=caller.argnames, methods=methods)
|
|
|
|
return caller
|
|
|
|
|
|
|
|
def _scan_plugin(self, plugin):
|
|
|
|
def fail(msg, *args):
|
|
|
|
name = getattr(plugin, '__name__', plugin)
|
|
|
|
raise PluginValidationError("plugin %r\n%s" %(name, msg % args))
|
|
|
|
|
|
|
|
for name in dir(plugin):
|
|
|
|
if name[0] == "_" or not name.startswith(self._prefix):
|
|
|
|
continue
|
|
|
|
hook = getattr(self.hook, name, None)
|
|
|
|
method = getattr(plugin, name)
|
|
|
|
if hook is None:
|
|
|
|
if self._excludefunc is not None and self._excludefunc(name):
|
|
|
|
continue
|
|
|
|
if getattr(method, 'optionalhook', False):
|
|
|
|
continue
|
|
|
|
fail("found unknown hook: %r", name)
|
|
|
|
for arg in varnames(method):
|
|
|
|
if arg not in hook.argnames:
|
|
|
|
fail("argument %r not available\n"
|
|
|
|
"actual definition: %s\n"
|
|
|
|
"available hookargs: %s",
|
|
|
|
arg, formatdef(method),
|
|
|
|
", ".join(hook.argnames))
|
|
|
|
yield hook
|
|
|
|
|
2015-04-22 19:33:01 +08:00
|
|
|
def register(self, plugin, name=None):
|
2012-06-17 03:29:04 +08:00
|
|
|
if self._name2plugin.get(name, None) == -1:
|
|
|
|
return
|
2010-12-06 23:54:42 +08:00
|
|
|
name = name or getattr(plugin, '__name__', str(id(plugin)))
|
2012-06-17 03:29:04 +08:00
|
|
|
if self.isregistered(plugin, name):
|
2013-09-30 19:14:14 +08:00
|
|
|
raise ValueError("Plugin already registered: %s=%s\n%s" %(
|
|
|
|
name, plugin, self._name2plugin))
|
2010-12-06 23:54:42 +08:00
|
|
|
#self.trace("registering", name, plugin)
|
2013-09-30 19:14:14 +08:00
|
|
|
reg = getattr(self, "_registercallback", None)
|
|
|
|
if reg is not None:
|
2014-10-01 18:20:11 +08:00
|
|
|
reg(plugin, name) # may call addhooks
|
2015-04-22 19:31:46 +08:00
|
|
|
hookcallers = list(self._scan_plugin(plugin))
|
2014-10-09 16:47:32 +08:00
|
|
|
self._plugin2hookcallers[plugin] = hookcallers
|
2014-10-01 18:20:11 +08:00
|
|
|
self._name2plugin[name] = plugin
|
2015-04-22 19:33:01 +08:00
|
|
|
self._plugins.append(plugin)
|
2014-10-09 16:47:32 +08:00
|
|
|
# finally make sure that the methods of the new plugin take part
|
|
|
|
for hookcaller in hookcallers:
|
|
|
|
hookcaller.scan_methods()
|
2010-10-12 18:54:32 +08:00
|
|
|
return True
|
|
|
|
|
2014-10-09 16:47:32 +08:00
|
|
|
def unregister(self, plugin):
|
2015-04-22 19:33:01 +08:00
|
|
|
self._plugins.remove(plugin)
|
2010-10-12 18:54:32 +08:00
|
|
|
for name, value in list(self._name2plugin.items()):
|
|
|
|
if value == plugin:
|
|
|
|
del self._name2plugin[name]
|
2014-10-09 16:47:32 +08:00
|
|
|
hookcallers = self._plugin2hookcallers.pop(plugin)
|
|
|
|
for hookcaller in hookcallers:
|
|
|
|
hookcaller.scan_methods()
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2013-09-29 04:23:00 +08:00
|
|
|
def add_shutdown(self, func):
|
|
|
|
self._shutdown.append(func)
|
|
|
|
|
|
|
|
def ensure_shutdown(self):
|
|
|
|
while self._shutdown:
|
|
|
|
func = self._shutdown.pop()
|
|
|
|
func()
|
2015-04-22 19:33:01 +08:00
|
|
|
self._plugins = []
|
2013-09-29 04:23:00 +08:00
|
|
|
self._name2plugin.clear()
|
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
def isregistered(self, plugin, name=None):
|
2010-12-06 23:54:42 +08:00
|
|
|
if self.getplugin(name) is not None:
|
2010-10-12 18:54:32 +08:00
|
|
|
return True
|
2015-04-22 19:33:01 +08:00
|
|
|
return plugin in self._plugins
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2015-04-22 19:31:46 +08:00
|
|
|
def addhooks(self, module_or_class):
|
|
|
|
isclass = int(inspect.isclass(module_or_class))
|
|
|
|
names = []
|
|
|
|
for name in dir(module_or_class):
|
|
|
|
if name.startswith(self._prefix):
|
|
|
|
method = module_or_class.__dict__[name]
|
|
|
|
firstresult = getattr(method, 'firstresult', False)
|
|
|
|
hc = HookCaller(self.hook, name, firstresult=firstresult,
|
|
|
|
argnames=varnames(method, startindex=isclass))
|
|
|
|
setattr(self.hook, name, hc)
|
|
|
|
names.append(name)
|
|
|
|
if not names:
|
|
|
|
raise ValueError("did not find new %r hooks in %r"
|
|
|
|
%(self._prefix, module_or_class))
|
2010-10-12 18:54:32 +08:00
|
|
|
|
|
|
|
def getplugins(self):
|
2015-04-22 19:33:01 +08:00
|
|
|
return self._plugins
|
2010-10-12 18:54:32 +08:00
|
|
|
|
|
|
|
def hasplugin(self, name):
|
2010-12-07 01:32:04 +08:00
|
|
|
return bool(self.getplugin(name))
|
2008-08-16 23:26:59 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
def getplugin(self, name):
|
2015-04-22 19:31:46 +08:00
|
|
|
return self._name2plugin.get(name)
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2010-10-13 17:12:27 +08:00
|
|
|
def listattr(self, attrname, plugins=None):
|
|
|
|
if plugins is None:
|
2015-04-22 19:33:01 +08:00
|
|
|
plugins = self._plugins
|
2010-10-13 17:12:27 +08:00
|
|
|
l = []
|
2010-11-22 06:17:59 +08:00
|
|
|
last = []
|
2014-03-14 19:49:35 +08:00
|
|
|
wrappers = []
|
2010-10-13 17:12:27 +08:00
|
|
|
for plugin in plugins:
|
|
|
|
try:
|
2010-11-22 06:17:59 +08:00
|
|
|
meth = getattr(plugin, attrname)
|
2010-10-13 17:12:27 +08:00
|
|
|
except AttributeError:
|
|
|
|
continue
|
2014-03-14 19:49:35 +08:00
|
|
|
if hasattr(meth, 'hookwrapper'):
|
|
|
|
wrappers.append(meth)
|
|
|
|
elif hasattr(meth, 'tryfirst'):
|
|
|
|
last.append(meth)
|
|
|
|
elif hasattr(meth, 'trylast'):
|
|
|
|
l.insert(0, meth)
|
|
|
|
else:
|
|
|
|
l.append(meth)
|
2010-11-22 06:17:59 +08:00
|
|
|
l.extend(last)
|
2014-03-14 19:49:35 +08:00
|
|
|
l.extend(wrappers)
|
2010-10-13 17:12:27 +08:00
|
|
|
return l
|
|
|
|
|
|
|
|
def call_plugin(self, plugin, methname, kwargs):
|
|
|
|
return MultiCall(methods=self.listattr(methname, plugins=[plugin]),
|
|
|
|
kwargs=kwargs, firstresult=True).execute()
|
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
|
|
|
|
class MultiCall:
|
2010-10-13 17:12:27 +08:00
|
|
|
""" execute a call into multiple python functions/methods. """
|
2014-03-14 19:49:35 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
def __init__(self, methods, kwargs, firstresult=False):
|
2010-10-13 17:12:27 +08:00
|
|
|
self.methods = list(methods)
|
|
|
|
self.kwargs = kwargs
|
2014-10-01 20:55:54 +08:00
|
|
|
self.kwargs["__multicall__"] = self
|
2010-10-12 18:54:32 +08:00
|
|
|
self.results = []
|
|
|
|
self.firstresult = firstresult
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
status = "%d results, %d meths" % (len(self.results), len(self.methods))
|
|
|
|
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
|
|
|
|
|
|
|
def execute(self):
|
2014-10-08 17:27:14 +08:00
|
|
|
all_kwargs = self.kwargs
|
|
|
|
while self.methods:
|
|
|
|
method = self.methods.pop()
|
|
|
|
args = [all_kwargs[argname] for argname in varnames(method)]
|
|
|
|
if hasattr(method, "hookwrapper"):
|
|
|
|
return wrapped_call(method(*args), self.execute)
|
|
|
|
res = method(*args)
|
|
|
|
if res is not None:
|
|
|
|
self.results.append(res)
|
|
|
|
if self.firstresult:
|
|
|
|
return res
|
|
|
|
if not self.firstresult:
|
|
|
|
return self.results
|
2014-03-14 19:49:35 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2014-10-02 21:25:42 +08:00
|
|
|
def varnames(func, startindex=None):
|
2013-11-19 22:33:52 +08:00
|
|
|
""" return argument name tuple for a function, method, class or callable.
|
|
|
|
|
|
|
|
In case of a class, its "__init__" method is considered.
|
|
|
|
For methods the "self" parameter is not included unless you are passing
|
|
|
|
an unbound method with Python3 (which has no supports for unbound methods)
|
|
|
|
"""
|
|
|
|
cache = getattr(func, "__dict__", {})
|
2011-03-08 01:28:45 +08:00
|
|
|
try:
|
2013-11-19 22:33:52 +08:00
|
|
|
return cache["_varnames"]
|
|
|
|
except KeyError:
|
2011-03-08 01:28:45 +08:00
|
|
|
pass
|
2013-11-19 22:33:52 +08:00
|
|
|
if inspect.isclass(func):
|
|
|
|
try:
|
|
|
|
func = func.__init__
|
|
|
|
except AttributeError:
|
|
|
|
return ()
|
2014-10-02 21:25:42 +08:00
|
|
|
startindex = 1
|
2013-11-19 22:33:52 +08:00
|
|
|
else:
|
|
|
|
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
|
|
|
func = getattr(func, '__call__', func)
|
2014-10-02 21:25:42 +08:00
|
|
|
if startindex is None:
|
|
|
|
startindex = int(inspect.ismethod(func))
|
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
rawcode = py.code.getrawcode(func)
|
|
|
|
try:
|
2014-10-02 21:25:42 +08:00
|
|
|
x = rawcode.co_varnames[startindex:rawcode.co_argcount]
|
2010-10-12 18:54:32 +08:00
|
|
|
except AttributeError:
|
2011-03-08 01:28:45 +08:00
|
|
|
x = ()
|
2014-10-01 20:55:54 +08:00
|
|
|
else:
|
|
|
|
defaults = func.__defaults__
|
|
|
|
if defaults:
|
|
|
|
x = x[:-len(defaults)]
|
2013-11-19 22:33:52 +08:00
|
|
|
try:
|
|
|
|
cache["_varnames"] = x
|
|
|
|
except TypeError:
|
|
|
|
pass
|
2011-03-08 01:28:45 +08:00
|
|
|
return x
|
2010-10-12 18:54:32 +08:00
|
|
|
|
2014-10-01 20:55:54 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
class HookRelay:
|
2015-04-22 19:31:46 +08:00
|
|
|
def __init__(self, pm):
|
2010-10-13 17:12:27 +08:00
|
|
|
self._pm = pm
|
2010-11-06 16:05:17 +08:00
|
|
|
self.trace = pm.trace.root.get("hook")
|
2014-10-01 18:19:11 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
|
|
|
|
class HookCaller:
|
2014-10-09 16:47:32 +08:00
|
|
|
def __init__(self, hookrelay, name, firstresult, argnames, methods=()):
|
2010-10-12 18:54:32 +08:00
|
|
|
self.hookrelay = hookrelay
|
|
|
|
self.name = name
|
|
|
|
self.firstresult = firstresult
|
2014-10-01 19:57:35 +08:00
|
|
|
self.argnames = ["__multicall__"]
|
|
|
|
self.argnames.extend(argnames)
|
2014-10-09 16:47:32 +08:00
|
|
|
assert "self" not in argnames # sanity check
|
|
|
|
self.methods = methods
|
2014-10-01 18:20:11 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
def __repr__(self):
|
|
|
|
return "<HookCaller %r>" %(self.name,)
|
2009-03-06 05:10:18 +08:00
|
|
|
|
2014-10-09 16:47:32 +08:00
|
|
|
def scan_methods(self):
|
|
|
|
self.methods = self.hookrelay._pm.listattr(self.name)
|
2014-10-01 18:20:11 +08:00
|
|
|
|
2010-10-12 18:54:32 +08:00
|
|
|
def __call__(self, **kwargs):
|
2014-10-09 16:47:32 +08:00
|
|
|
return self._docall(self.methods, kwargs)
|
2010-10-07 17:51:58 +08:00
|
|
|
|
2014-10-01 18:20:11 +08:00
|
|
|
def callextra(self, methods, **kwargs):
|
|
|
|
return self._docall(self.methods + methods, kwargs)
|
|
|
|
|
2010-11-07 02:46:24 +08:00
|
|
|
def _docall(self, methods, kwargs):
|
2014-10-02 21:25:42 +08:00
|
|
|
return MultiCall(methods, kwargs,
|
|
|
|
firstresult=self.firstresult).execute()
|
2014-10-01 18:20:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
class PluginValidationError(Exception):
|
|
|
|
""" plugin failed validation. """
|
|
|
|
|
|
|
|
|
|
|
|
def formatdef(func):
|
|
|
|
return "%s%s" % (
|
|
|
|
func.__name__,
|
|
|
|
inspect.formatargspec(*inspect.getargspec(func))
|
|
|
|
)
|