Add support for dynamic reloading of plugins
* rename the remove_plugin function to uninstall_plugin because it deletes files on the disc and uninstall is the label of the button which triggers this function * added a new remove_plugin function, which deactivates the plugin (if needed), removes it from the list of managed plugins and deletes the contents of sys.modules
This commit is contained in:
parent
379a4b03d7
commit
49bfc1f226
|
@ -252,7 +252,7 @@ class PluginsWindow(object):
|
||||||
if iter:
|
if iter:
|
||||||
plugin = model.get_value(iter, Column.PLUGIN)
|
plugin = model.get_value(iter, Column.PLUGIN)
|
||||||
try:
|
try:
|
||||||
app.plugin_manager.remove_plugin(plugin)
|
app.plugin_manager.uninstall_plugin(plugin)
|
||||||
except PluginsystemError as e:
|
except PluginsystemError as e:
|
||||||
WarningDialog(_('Unable to properly remove the plugin'),
|
WarningDialog(_('Unable to properly remove the plugin'),
|
||||||
str(e), self.window)
|
str(e), self.window)
|
||||||
|
|
|
@ -149,6 +149,32 @@ class PluginManager(metaclass=Singleton):
|
||||||
' short name: %s). Plugin already loaded.' % (plugin.name,
|
' short name: %s). Plugin already loaded.' % (plugin.name,
|
||||||
plugin.version, plugin.__module__, plugin.short_name))
|
plugin.version, plugin.__module__, plugin.short_name))
|
||||||
|
|
||||||
|
@log_calls('PluginManager')
|
||||||
|
def remove_plugin(self, plugin):
|
||||||
|
'''
|
||||||
|
removes the plugin from the plugin list and deletes all loaded modules
|
||||||
|
from sys. This way we will have a fresh start when the plugin gets added
|
||||||
|
agin.
|
||||||
|
'''
|
||||||
|
if plugin.active:
|
||||||
|
self.deactivate_plugin(plugin)
|
||||||
|
|
||||||
|
self.plugins.remove(plugin)
|
||||||
|
|
||||||
|
# remove modules from cache
|
||||||
|
base_package = plugin.__module__.split('.')[0]
|
||||||
|
# get the subpackages/-modules of the base_package. Add a dot to the
|
||||||
|
# name to avoid name problems (removing module_abc if base_package is
|
||||||
|
# module_ab)
|
||||||
|
modules_to_remove = [module for module in sys.modules
|
||||||
|
if module.startswith('{}.'.format(base_package))]
|
||||||
|
# remove the base_package itself
|
||||||
|
if base_package in sys.modules:
|
||||||
|
modules_to_remove.append(base_package)
|
||||||
|
|
||||||
|
for module_to_remove in modules_to_remove:
|
||||||
|
del sys.modules[module_to_remove]
|
||||||
|
|
||||||
@log_calls('PluginManager')
|
@log_calls('PluginManager')
|
||||||
def add_plugins(self, plugin_classes):
|
def add_plugins(self, plugin_classes):
|
||||||
for plugin_class in plugin_classes:
|
for plugin_class in plugin_classes:
|
||||||
|
@ -532,20 +558,9 @@ class PluginManager(metaclass=Singleton):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
module = None
|
module = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if module_name in sys.modules:
|
log.info('Loading %s', module_name)
|
||||||
if path == configpaths.get('PLUGINS_BASE'):
|
module = __import__(module_name)
|
||||||
# Only reload plugins from Gajim base dir when they
|
|
||||||
# dont exist. This means plugins in the user path are
|
|
||||||
# always preferred.
|
|
||||||
continue
|
|
||||||
from imp import reload
|
|
||||||
log.info('Reloading %s', module_name)
|
|
||||||
module = reload(sys.modules[module_name])
|
|
||||||
else:
|
|
||||||
log.info('Loading %s', module_name)
|
|
||||||
module = __import__(module_name)
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
log.warning(
|
log.warning(
|
||||||
"While trying to load {plugin}, exception occurred".format(plugin=elem_name),
|
"While trying to load {plugin}, exception occurred".format(plugin=elem_name),
|
||||||
|
@ -633,14 +648,14 @@ class PluginManager(metaclass=Singleton):
|
||||||
if len(dirs) > 1:
|
if len(dirs) > 1:
|
||||||
raise PluginsystemError(_('Archive is malformed'))
|
raise PluginsystemError(_('Archive is malformed'))
|
||||||
|
|
||||||
base_dir, user_dir = configpaths.get('PLUGINS_DIRS')
|
user_dir = configpaths.get('PLUGINS_USER')
|
||||||
plugin_dir = os.path.join(user_dir, dirs[0])
|
plugin_dir = os.path.join(user_dir, dirs[0])
|
||||||
|
|
||||||
if os.path.isdir(plugin_dir):
|
if os.path.isdir(plugin_dir):
|
||||||
# Plugin dir already exists
|
# Plugin dir already exists
|
||||||
if not owerwrite:
|
if not owerwrite:
|
||||||
raise PluginsystemError(_('Plugin already exists'))
|
raise PluginsystemError(_('Plugin already exists'))
|
||||||
self.remove_plugin(self.get_plugin_by_path(plugin_dir))
|
self.uninstall_plugin(self.get_plugin_by_path(plugin_dir))
|
||||||
|
|
||||||
zip_file.extractall(user_dir)
|
zip_file.extractall(user_dir)
|
||||||
zip_file.close()
|
zip_file.close()
|
||||||
|
@ -652,7 +667,7 @@ class PluginManager(metaclass=Singleton):
|
||||||
plugin = self.plugins[-1]
|
plugin = self.plugins[-1]
|
||||||
return plugin
|
return plugin
|
||||||
|
|
||||||
def remove_plugin(self, plugin):
|
def uninstall_plugin(self, plugin):
|
||||||
'''
|
'''
|
||||||
Deactivate and remove plugin from `plugins` list
|
Deactivate and remove plugin from `plugins` list
|
||||||
'''
|
'''
|
||||||
|
@ -665,15 +680,10 @@ class PluginManager(metaclass=Singleton):
|
||||||
raise PluginsystemError(error[1][1])
|
raise PluginsystemError(error[1][1])
|
||||||
|
|
||||||
if plugin:
|
if plugin:
|
||||||
if plugin.active:
|
self.remove_plugin(plugin)
|
||||||
self.deactivate_plugin(plugin)
|
|
||||||
rmtree(plugin.__path__, False, on_error)
|
rmtree(plugin.__path__, False, on_error)
|
||||||
self.plugins.remove(plugin)
|
|
||||||
if self._plugin_has_entry_in_global_config(plugin):
|
if self._plugin_has_entry_in_global_config(plugin):
|
||||||
self._remove_plugin_entry_in_global_config(plugin)
|
self._remove_plugin_entry_in_global_config(plugin)
|
||||||
del sys.modules[plugin.__module__.split('.')[0]]
|
|
||||||
del plugin.__module__.split('.')[-1]
|
|
||||||
del plugin
|
|
||||||
|
|
||||||
def get_plugin_by_path(self, plugin_dir):
|
def get_plugin_by_path(self, plugin_dir):
|
||||||
for plugin in self.plugins:
|
for plugin in self.plugins:
|
||||||
|
|
Loading…
Reference in New Issue