summaryrefslogtreecommitdiff
path: root/poezio/plugin_manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'poezio/plugin_manager.py')
-rw-r--r--poezio/plugin_manager.py99
1 files changed, 79 insertions, 20 deletions
diff --git a/poezio/plugin_manager.py b/poezio/plugin_manager.py
index 89849747..17673a9e 100644
--- a/poezio/plugin_manager.py
+++ b/poezio/plugin_manager.py
@@ -5,10 +5,13 @@ the API together. Defines also a bunch of variables related to the
plugin env.
"""
+import logging
import os
-from os import path
+from typing import Dict, Set
+from importlib import import_module, machinery
from pathlib import Path
-import logging
+from os import path
+import pkg_resources
from poezio import tabs, xdg
from poezio.core.structs import Command, Completion
@@ -25,6 +28,8 @@ class PluginManager:
And keeps track of everything the plugin has done through the API.
"""
+ rdeps: Dict[str, Set[str]] = {}
+
def __init__(self, core):
self.core = core
# module name -> module object
@@ -44,7 +49,6 @@ class PluginManager:
self.tab_keys = {}
self.roster_elements = {}
- from importlib import machinery
self.finder = machinery.PathFinder()
self.initial_set_plugins_dir()
@@ -57,21 +61,56 @@ class PluginManager:
for plugin in set(self.plugins.keys()):
self.unload(plugin, notify=False)
- def load(self, name, notify=True):
+ def set_rdeps(self, name):
+ """
+ Runs through plugin dependencies to build the reverse dependencies table.
+ """
+
+ if name not in self.rdeps:
+ self.rdeps[name] = set()
+ for dep in self.plugins[name].dependencies:
+ if dep not in self.rdeps:
+ self.rdeps[dep] = {name}
+ else:
+ self.rdeps[dep].add(name)
+
+ def load(self, name: str, notify=True, unload_first=True):
"""
Load a plugin.
"""
+ if not unload_first and name in self.plugins:
+ return None
if name in self.plugins:
self.unload(name)
try:
module = None
loader = self.finder.find_module(name, self.load_path)
- if not loader:
+ if loader:
+ log.debug('Found candidate loader for plugin %s: %r', name, loader)
+ module = loader.load_module()
+ if module is None:
+ log.debug('Failed to load plugin %s from loader', name)
+ else:
+ try:
+ module = import_module('poezio_plugins.%s' % name)
+ except ModuleNotFoundError:
+ pass
+ for entry in pkg_resources.iter_entry_points('poezio_plugins'):
+ if entry.name == name:
+ log.debug('Found candidate entry for plugin %s: %r', name, entry)
+ try:
+ module = entry.load()
+ except Exception as exn:
+ log.debug('Failed to import plugin: %s\n%r', name,
+ exn, exc_info=True)
+ finally:
+ break
+ if not module:
self.core.information('Could not find plugin: %s' % name,
'Error')
return
- module = loader.load_module()
+ log.debug('Plugin %s loaded from "%s"', name, module.__file__)
except Exception as e:
log.debug("Could not load plugin %s", name, exc_info=True)
self.core.information("Could not load plugin %s: %s" % (name, e),
@@ -88,8 +127,22 @@ class PluginManager:
self.event_handlers[name] = []
try:
self.plugins[name] = None
- self.plugins[name] = module.Plugin(self.plugin_api, self.core,
+
+ for dep in module.Plugin.dependencies:
+ self.load(dep, unload_first=False)
+ if dep not in self.plugins:
+ log.debug(
+ 'Plugin %s couldn\'t load because of dependency %s',
+ name, dep
+ )
+ return None
+ # Add reference of the dep to the plugin's usage
+ module.Plugin.refs[dep] = self.plugins[dep]
+
+ self.plugins[name] = module.Plugin(name, self.plugin_api, self.core,
self.plugins_conf_dir)
+ self.set_rdeps(name)
+
except Exception as e:
log.error('Error while loading the plugin %s', name, exc_info=True)
if notify:
@@ -100,9 +153,22 @@ class PluginManager:
if notify:
self.core.information('Plugin %s loaded' % name, 'Info')
- def unload(self, name, notify=True):
+ def unload(self, name: str, notify=True):
+ """
+ Unloads plugin as well as plugins depending on it.
+ """
+
if name in self.plugins:
try:
+ if self.plugins[name] is not None:
+ self.plugins[name]._unloading = True # Prevents loops
+ for rdep in self.rdeps[name].copy():
+ if rdep in self.plugins and not self.plugins[rdep]._unloading:
+ self.unload(rdep)
+ if rdep in self.plugins:
+ log.debug('Failed to unload reverse dependency %s first.', rdep)
+ return None
+
for command in self.commands[name].keys():
del self.core.commands[command]
for key in self.keys[name].keys():
@@ -122,6 +188,7 @@ class PluginManager:
if self.plugins[name] is not None:
self.plugins[name].unload()
del self.plugins[name]
+ del self.rdeps[name]
del self.commands[name]
del self.keys[name]
del self.tab_commands[name]
@@ -253,7 +320,7 @@ class PluginManager:
if key in self.core.key_func:
del self.core.commands[key]
- def add_event_handler(self, module_name, event_name, handler, position=0):
+ def add_event_handler(self, module_name, event_name, handler, *args, **kwargs):
"""
Add an event handler. If event_name isn’t in the event list, assume
it is a slixmpp event.
@@ -261,7 +328,7 @@ class PluginManager:
eh = self.event_handlers[module_name]
eh.append((event_name, handler))
if event_name in self.core.events.events:
- self.core.events.add_event_handler(event_name, handler, position)
+ self.core.events.add_event_handler(event_name, handler, *args, **kwargs)
else:
self.core.xmpp.add_event_handler(event_name, handler)
@@ -326,7 +393,7 @@ class PluginManager:
"""
Create the plugins_conf_dir
"""
- plugins_conf_dir = config.get('plugins_conf_dir')
+ plugins_conf_dir = config.getstr('plugins_conf_dir')
self.plugins_conf_dir = Path(plugins_conf_dir).expanduser(
) if plugins_conf_dir else xdg.CONFIG_HOME / 'plugins'
self.check_create_plugins_conf_dir()
@@ -351,7 +418,7 @@ class PluginManager:
"""
Set the plugins_dir on start
"""
- plugins_dir = config.get('plugins_dir')
+ plugins_dir = config.getstr('plugins_dir')
self.plugins_dir = Path(plugins_dir).expanduser(
) if plugins_dir else xdg.DATA_HOME / 'plugins'
self.check_create_plugins_dir()
@@ -387,11 +454,3 @@ class PluginManager:
if os.access(str(self.plugins_dir), os.R_OK | os.X_OK):
self.load_path.append(str(self.plugins_dir))
-
- try:
- import poezio_plugins
- except:
- pass
- else:
- if poezio_plugins.__path__:
- self.load_path.append(list(poezio_plugins.__path__)[0])