From f27556747896aeb891ce71cfdd0ac349d68c5b3d Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 24 Sep 2011 22:26:31 +0200 Subject: [PATCH] [teisenbe] Use the imp module to import modules. Also add a simple translator module --- plugins/screen_detach.py | 1 + plugins/test.py | 4 +++ plugins/translate.py | 33 +++++++++++++++++ src/plugin.py | 26 ++++++-------- src/plugin_manager.py | 77 +++++++++++++++++++++++++++++++++++----- 5 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 plugins/translate.py diff --git a/plugins/screen_detach.py b/plugins/screen_detach.py index 6ebc77f2..6ee96896 100644 --- a/plugins/screen_detach.py +++ b/plugins/screen_detach.py @@ -1,3 +1,4 @@ +from plugin import BasePlugin import os import stat import pyinotify diff --git a/plugins/test.py b/plugins/test.py index fc7aedc6..13ba1e9c 100644 --- a/plugins/test.py +++ b/plugins/test.py @@ -1,5 +1,9 @@ +from plugin import BasePlugin + class Plugin(BasePlugin): def init(self): + self.add_command('plugintest', self.command_plugintest, 'Test command') + self.add_event_handler('message', self.on_message) self.core.information("Plugin loaded") def cleanup(self): diff --git a/plugins/translate.py b/plugins/translate.py new file mode 100644 index 00000000..625d78e7 --- /dev/null +++ b/plugins/translate.py @@ -0,0 +1,33 @@ +from plugin import BasePlugin +import urllib.request +from urllib.parse import urlencode +import xhtml +import json + +TARGET_LANG = 'en' + +def translate(s, target=TARGET_LANG, source=''): + f = urllib.request.urlopen('http://ajax.googleapis.com/ajax/services/language/translate', urlencode({ 'v': '1.0', 'q': s, 'langpair': '%s|%s' % (source, target) })) + response = json.loads(str(f.read(), 'utf-8'))['responseData'] + return (response['translatedText'], response['detectedSourceLanguage']) + +class Plugin(BasePlugin): + def init(self): + self.add_event_handler('groupchat_message', self.on_groupchat_message) + + def on_groupchat_message(self, message): + try: + room_from = message.getMucroom() + if message['type'] == 'error': + return + + if room_from == 'poezio@kikoo.louiz.org': + nick_from = message['mucnick'] + body = xhtml.get_body_from_message_stanza(message) + room = self.core.get_room_by_name(room_from) + text, lang = translate(body) + if lang != TARGET_LANG: + room.add_message(text, nickname=nick_from) + except Exception as e: + import traceback + self.core.information("Exception in translator! %s" % (traceback.format_exc(),)) diff --git a/src/plugin.py b/src/plugin.py index e8386d16..728dfe21 100644 --- a/src/plugin.py +++ b/src/plugin.py @@ -1,5 +1,3 @@ -import inspect - class BasePlugin(object): """ Class that all plugins derive from. Any methods beginning with command_ @@ -7,14 +5,9 @@ class BasePlugin(object): event handlers """ - def __init__(self, core): + def __init__(self, plugin_manager, core): self.core = core - for k, v in inspect.getmembers(self, inspect.ismethod): - if k.startswith('on_'): - core.xmpp.add_event_handler(k[3:], v) - elif k.startswith('command_'): - command = k[len('command_'):] - core.commands[command] = (v, v.__doc__, None) + self.plugin_manager = plugin_manager self.init() def init(self): @@ -24,10 +17,13 @@ class BasePlugin(object): pass def unload(self): - for k, v in inspect.getmembers(self, inspect.ismethod): - if k.startswith('on_'): - self.core.xmpp.del_event_handler(k[3:], v) - elif k.startswith('command_'): - command = k[len('command_'):] - del self.core.commands[command] self.cleanup() + + def add_command(self, name, handler, help, completion=None): + return self.plugin_manager.add_command(self.__module__, name, handler, help, completion) + + def add_event_handler(self, event_name, handler): + return self.plugin_manager.add_event_handler(self.__module__, event_name, handler) + + def del_event_handler(self, event_name, handler): + return self.plugin_manager.del_event_handler(self.__module__, event_name, handler) diff --git a/src/plugin_manager.py b/src/plugin_manager.py index 3f900e39..8301f5f8 100644 --- a/src/plugin_manager.py +++ b/src/plugin_manager.py @@ -1,25 +1,86 @@ +import imp +import os +import sys +from config import config +from gettext import gettext as _ + +plugins_dir = config.get('plugins_dir', '') +plugins_dir = plugins_dir or\ + os.path.join(os.environ.get('XDG_DATA_HOME') or\ + os.path.join(os.environ.get('HOME'), '.local', 'share'), + 'poezio', 'plugins') +try: + os.makedirs(plugins_dir) +except OSError: + pass + +sys.path.append(plugins_dir) + class PluginManager(object): def __init__(self, core): self.core = core - self.plugins = {} + self.modules = {} # module name -> module object + self.plugins = {} # module name -> plugin object + self.commands = {} # module name -> dict of commands loaded for the module + self.event_handlers = {} # module name -> list of event_name/handler pairs loaded for the module def load(self, name): if name in self.plugins: self.plugins[name].unload() try: - code = compile(open(name).read(), name, 'exec') - from plugin import BasePlugin - globals = { 'BasePlugin' : BasePlugin } - exec(code, globals) - self.plugins[name] = globals['Plugin'](self.core) + if name in self.modules: + imp.acquire_lock() + module = imp.reload(self.modules[name]) + imp.release_lock() + else: + file, filename, info = imp.find_module(name, [plugins_dir]) + imp.acquire_lock() + module = imp.load_module(name, file, filename, info) + imp.release_lock() except Exception as e: - self.core.information("Could not load plugin: %s" % (e,)) + import traceback + self.core.information(_("Could not load plugin: ") + traceback.format_exc()) + return + finally: + if imp.lock_held(): + imp.release_lock() + + self.modules[name] = module + self.commands[name] = {} + self.event_handlers[name] = [] + self.plugins[name] = module.Plugin(self, self.core) def unload(self, name): if name in self.plugins: try: + for command in self.commands[name].keys(): + del self.core.commands[command] + for event_name, handler in self.event_handlers[name]: + self.core.xmpp.del_event_handler(event_name, handler) + self.plugins[name].unload() del self.plugins[name] + del self.commands[name] + del self.event_handlers[name] except Exception as e: - self.core.information("Could not unload plugin (may not be safe to try again): %s" % (e,)) + import traceback + self.core.information(_("Could not unload plugin (may not be safe to try again): ") + traceback.format_exc()) + + def add_command(self, module_name, name, handler, help, completion=None): + if name in self.core.commands: + raise Exception(_("Command '%s' already exists") % (name,)) + + commands = self.commands[module_name] + commands[name] = (handler, help, completion) + self.core.commands[name] = (handler, help, completion) + + def add_event_handler(self, module_name, event_name, handler): + eh = self.event_handlers[module_name] + eh.append((event_name, handler)) + self.core.xmpp.add_event_handler(event_name, handler) + + def del_event_handler(self, module_name, event_name, handler): + self.core.xmpp.del_event_handler(event_name, handler) + eh = self.event_handlers[module_name] + eh = list(filter(lambda e : e != (event_name, handler), eh))