From 370cc874c2dffd6b4fd306213654c355a5ecadf6 Mon Sep 17 00:00:00 2001 From: mathieui Date: Wed, 6 Mar 2013 22:57:41 +0100 Subject: Introduce a new way for plugins to interact with poezio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Try to reduce the use of the “core” object in the plugins - New “api” member for each BasePlugin which is a wrapper around the unique PluginAPI object. (instead of having the methods directly in BasePlugin and then calling the PluginManager) - Documented methods with rst (for sphinx) --- src/plugin.py | 259 +++++++++++++++++++++++++++++++++++++++++++++++--- src/plugin_manager.py | 5 +- 2 files changed, 251 insertions(+), 13 deletions(-) diff --git a/src/plugin.py b/src/plugin.py index 537f7b61..82b96443 100644 --- a/src/plugin.py +++ b/src/plugin.py @@ -4,6 +4,7 @@ These are used in the plugin system added in poezio 0.7.5 (see plugin_manager.py) """ import os +from functools import partial from configparser import RawConfigParser import config import inspect @@ -82,23 +83,256 @@ class SafetyMetaclass(type): class_dict[k] = SafetyMetaclass.safe_func(v) return type.__new__(meta, name, bases, class_dict) +class PluginWrap(object): + """ + A wrapper to implicitly pass the module name to PluginAPI + """ + def __init__(self, api, module): + self.api = api + self.module = module + + def __getattribute__(self, name): + api = object.__getattribute__(self, 'api') + module = object.__getattribute__(self, 'module') + return partial(getattr(api, name), module) + +class PluginAPI(object): + """ + The public API exposed to the plugins. + Its goal is to limit the use of the raw Core object + as much as possible. + """ + + def __init__(self, core, plugin_manager): + self.core = core + self.plugin_manager = plugin_manager + + def __getitem__(self, value): + return PluginWrap(self, value) + + def send_message(self, _, *args, **kwargs): + """ + Send a message to the current tab. + + :param str msg: The message to send. + """ + return self.core.send_message(*args, **kwargs) + + def add_timed_event(self, _, *args, **kwargs): + """ + Add a timed event. + + :param event: The timed event to add. + """ + return self.core.add_timed_event(*args, **kwargs) + + def information(self, _, *args, **kwargs): + """ + Display a new message in the information buffer. + + :param str msg: The message to display. + :param str typ: The message type (e.g. Info, Error…) + """ + return self.core.information(*args, **kwargs) + + def current_tab(self, _): + """ + Get the current Tab. + + :returns: tabs.Tab The current tab. + """ + return self.core.current_tab() + + def run_command(self, _, *args, **kwargs): + """ + Run a command from the current tab. + (a command starts with a /, if not, it’s a message) + + :param str line: The command to run. + """ + return self.core.current_tab().execute_command(line) + + def all_tabs(self, _): + """ + Return a list of all opened tabs + + :returns list: The list of tabs. + """ + return self.core.tabs + + def add_command(self, module, *args, **kwargs): + """ + Add a global command. + + :param str name: The name of the command (/name) + :param function handler: The function called when the command is run. + :param str help: The complete help for that command. + :param str short: A short description of the command. + :param function completion: The completion function for that command + (optional) + :param str usage: A string showing the required and optional args + of the command. Optional args should be surrounded by [] + and mandatory args should be surrounded by <>. + + Example string: " [port]" + + :raises: Exception If the command already exists. + """ + return self.plugin_manager.add_command(module, *args, **kwargs) + + def del_command(self, module, *args, **kwargs): + """ + Remove a global command. + + :param str name: The name of the command to remove. + That command _must_ have been added by the same plugin + """ + return self.plugin_manager.del_command(module, *args, **kwargs) + + def add_key(self, module, *args, **kwargs): + """ + Associate a global binding to a handler. + + :param str key: The curses representation of the binding. + :param function handler: The function called when the binding is pressed. + + :raise Exception: If the binding is already present. + """ + return self.plugin_manager.add_key(module, *args, **kwargs) + + def del_key(self, module, *args, **kwargs): + """ + Remove a global binding. + + :param str key: The binding to remove. + """ + return self.plugin_manager.del_key(module, *args, **kwargs) + + def add_tab_key(self, module, *args, **kwargs): + """ + Associate a binding to a handler, but only for a certain tab type. + + :param Tab tab_type: The type of tab to target. + :param str key: The binding to add. + :param function handler: The function called when the binding is pressed + """ + return self.plugin_manager.add_tab_key(module, *args, **kwargs) + + def del_tab_key(self, module, *args, **kwargs): + """ + Remove a binding added with add_tab_key + + :param tabs.Tab tab_type: The type of tab to target. + :param str key: The binding to remove. + """ + return self.plugin_manager.del_tab_key(module, *args, **kwargs) + + def add_tab_command(self, module, *args, **kwargs): + """ + Add a command to only one type of tab. + + :param tabs.Tab tab_type: The type of Tab to target. + :param str name: The name of the command (/name) + :param function handler: The function called when the command is run. + :param str help: The complete help for that command. + :param str short: A short description of the command. + :param function completion: The completion function for that command + (optional) + :param str usage: A string showing the required and optional args + of the command. Optional args should be surrounded by [] + and mandatory args should be surrounded by <>. + + Example string: " [port]" + + :raise Exception: If the command already exists. + """ + return self.plugin_manager.add_tab_command(module, *args, **kwargs) + + def del_tab_command(self, module, *args, **kwargs): + """ + Remove a tab-specific command. + + :param tabs.Tab tab_type: The type of tab to target. + :param str name: The name of the command to remove. + That command _must_ have been added by the same plugin + """ + return self.plugin_manager.del_tab_command(module, *args, **kwargs) + + def add_event_handler(self, module, *args, **kwargs): + """ + Add an event handler for a poezio event. + + :param str event_name: The event name. + :param function handler: The handler function. + :param int position: The position of that handler in the handler list. + This is useful for plugins like GPG or OTR, which must be the last + function called on the text. + Defaults to 0. + + A complete list of those events can be found at + http://poezio.eu/doc/en/plugins.html#events-list + """ + return self.plugin_manager.add_event_handler(module, *args, **kwargs) + + def del_event_handler(self, module, *args, **kwargs): + """ + Remove a handler for a poezio event. + + :param str event_name: The name of the targeted event. + :param function handler: The function to remove from the handlers. + """ + return self.plugin_manager.del_event_handler(module, *args, **kwargs) + + def add_sleek_event_handler(self, module, event_name, handler): + """ + Add an event handler for a sleekxmpp event. + + :param str event_name: The event name. + :param function handler: The handler function. + + A list of the SleekXMPP events can be found here + http://sleekxmpp.com/event_index.html + """ + self.core.xmmp.add_event_handler(event_name, handler) + + def del_sleek_event_handler(self, module, event_name, handler): + """ + Remove a handler for a SleekXMPP event + + :param str event_name: The name of the targeted event. + :param function handler: The function to remove from the handlers. + """ + self.core.xmpp.del_event_handler(event_name, handler) + class BasePlugin(object, metaclass=SafetyMetaclass): """ Class that all plugins derive from. """ - def __init__(self, plugin_manager, core, plugins_conf_dir): + def __init__(self, plugin_api, core, plugins_conf_dir): self.core = core # More hack; luckily we'll never have more than one core object SafetyMetaclass.core = core - self.plugin_manager = plugin_manager conf = os.path.join(plugins_conf_dir, self.__module__+'.cfg') self.config = PluginConfig(conf, self.__module__) + self._api = plugin_api[self.name] self.init() + @property + def name(self): + """ + Get the name (module name) of the plugin. + """ + return self.__module__ + + @property + def api(self): + return self._api + def init(self): """ Method called at the creation of the plugin. + Do not overwrite __init__ and use this instead. """ pass @@ -106,6 +340,7 @@ class BasePlugin(object, metaclass=SafetyMetaclass): def cleanup(self): """ Called when the plugin is unloaded. + Overwrite this if you want to erase or save things before the plugin is disabled. """ pass @@ -118,7 +353,7 @@ class BasePlugin(object, metaclass=SafetyMetaclass): Add a global command. You cannot overwrite the existing commands. """ - return self.plugin_manager.add_command(self.__module__, name, handler, help, + return self.api.add_command(name, handler, help, completion=completion, short=short, usage=usage) def del_command(self, name): @@ -126,54 +361,54 @@ class BasePlugin(object, metaclass=SafetyMetaclass): Remove a global command. This only works if the command was added by the plugin """ - return self.plugin_manager.del_command(self.__module__, name) + return self.api.del_command(name) def add_key(self, key, handler): """ Add a global keybind """ - return self.plugin_manager.add_key(self.__module__, key, handler) + return self.api.add_key(key, handler) def del_key(self, key): """ Remove a global keybind """ - return self.plugin_manager.del_key(self.__module__, key) + return self.api.del_key(key) def add_tab_key(self, tab_type, key, handler): """ Add a keybind only for a type of tab. """ - return self.plugin_manager.add_tab_key(self.__module__, tab_type, key, handler) + return self.api.add_tab_key(tab_type, key, handler) def del_tab_key(self, tab_type, key): """ Remove a keybind added through add_tab_key. """ - return self.plugin_manager.del_tab_key(self.__module__, tab_type, key) + return self.api.del_tab_key(tab_type, key) def add_tab_command(self, tab_type, name, handler, help, completion=None, short='', usage=''): """ Add a command only for a type of tab. """ - return self.plugin_manager.add_tab_command(self.__module__, tab_type, name, handler, help, + return self.api.add_tab_command(tab_type, name, handler, help, completion=completion, short=short, usage=usage) def del_tab_command(self, tab_type, name): """ Delete a command added through add_tab_command. """ - return self.plugin_manager.del_tab_command(self.__module__, tab_type, name) + return self.api.del_tab_command(tab_type, name) def add_event_handler(self, event_name, handler, position=0): """ Add an event handler to the event event_name. An optional position in the event handler list can be provided. """ - return self.plugin_manager.add_event_handler(self.__module__, event_name, handler, position) + return self.api.add_event_handler(event_name, handler, position) def del_event_handler(self, event_name, handler): """ Remove 'handler' from the event list for 'event_name'. """ - return self.plugin_manager.del_event_handler(self.__module__, event_name, handler) + return self.api.del_event_handler(event_name, handler) diff --git a/src/plugin_manager.py b/src/plugin_manager.py index a8c651e9..afd04330 100644 --- a/src/plugin_manager.py +++ b/src/plugin_manager.py @@ -14,6 +14,7 @@ from gettext import gettext as _ import core import tabs +from plugin import PluginAPI from config import config log = logging.getLogger(__name__) @@ -63,6 +64,8 @@ class PluginManager(object): self.tab_commands = {} #module name -> dict of tab types; tab type -> commands loaded by the module self.keys = {} # module name → dict of keys/handlers loaded for the module self.tab_keys = {} #module name → dict of tab types; tab type → list of keybinds (tuples) + self.roster_elements = {} + self.plugin_api = PluginAPI(core, self) def load(self, name, notify=True): """ @@ -96,7 +99,7 @@ class PluginManager(object): self.tab_keys[name] = {} self.tab_commands[name] = {} self.event_handlers[name] = [] - self.plugins[name] = module.Plugin(self, self.core, plugins_conf_dir) + self.plugins[name] = module.Plugin(self.plugin_api, self.core, plugins_conf_dir) if notify: self.core.information('Plugin %s loaded' % name, 'Info') -- cgit v1.2.3