Introduce a new way for plugins to interact with poezio
- 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)
This commit is contained in:
parent
cbf563583c
commit
370cc874c2
2 changed files with 251 additions and 13 deletions
259
src/plugin.py
259
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: "<server> [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: "<server> [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)
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
Loading…
Reference in a new issue