Create PluginClassRegistry class to handle class loading/unloading
Implements signals emitted when a plugin class is loaded/unloaded and defers registration of classes until the plugin module is fully loaded.
This commit is contained in:
parent
a5584de41f
commit
bea7d8e8d5
@ -10,6 +10,7 @@ from PySide import QtCore, QtGui
|
||||
from mcedit2.command import SimpleRevisionCommand
|
||||
from mcedit2.editortools import EditorTool
|
||||
from mcedit2.handles.boxhandle import BoxHandle
|
||||
from mcedit2.plugins.registry import PluginClassRegistry
|
||||
from mcedit2.rendering.scenegraph import scenenode
|
||||
from mcedit2.rendering.scenegraph.matrix import Translate
|
||||
from mcedit2.rendering.scenegraph.scenenode import Node
|
||||
@ -134,24 +135,11 @@ class GeneratePlugin(QtCore.QObject):
|
||||
def editorSession(self):
|
||||
return self.generateTool.editorSession
|
||||
|
||||
_pluginClasses = []
|
||||
|
||||
class _GeneratePlugins(PluginClassRegistry):
|
||||
pluginClass = GeneratePlugin
|
||||
|
||||
def registerGeneratePlugin(cls):
|
||||
_pluginClasses.append(cls)
|
||||
_GeneratePlugins.instance.pluginAdded.emit(cls)
|
||||
return cls
|
||||
|
||||
|
||||
def unregisterGeneratePlugin(cls):
|
||||
_pluginClasses[:] = [c for c in _pluginClasses if c != cls]
|
||||
_GeneratePlugins.instance.pluginRemoved.emit(cls)
|
||||
|
||||
class _GeneratePlugins(QtCore.QObject):
|
||||
pluginRemoved = QtCore.Signal(object)
|
||||
pluginAdded = QtCore.Signal(object)
|
||||
|
||||
_GeneratePlugins.instance = _GeneratePlugins()
|
||||
GeneratePlugins = _GeneratePlugins()
|
||||
|
||||
|
||||
class GenerateTool(EditorTool):
|
||||
@ -171,7 +159,7 @@ class GenerateTool(EditorTool):
|
||||
self.toolWidget = toolWidget
|
||||
|
||||
column = []
|
||||
self.generatorTypes = [pluginClass(self) for pluginClass in _pluginClasses]
|
||||
self.generatorTypes = [pluginClass(self) for pluginClass in GeneratePlugins.registeredPlugins]
|
||||
self.currentGenerator = None
|
||||
if len(self.generatorTypes):
|
||||
self.currentGenerator = self.generatorTypes[0]
|
||||
@ -236,8 +224,8 @@ class GenerateTool(EditorTool):
|
||||
# so it can be reselected if it is immediately reloaded
|
||||
self._lastTypeName = None
|
||||
|
||||
_GeneratePlugins.instance.pluginAdded.connect(self.addPlugin)
|
||||
_GeneratePlugins.instance.pluginRemoved.connect(self.removePlugin)
|
||||
GeneratePlugins.pluginAdded.connect(self.addPlugin)
|
||||
GeneratePlugins.pluginRemoved.connect(self.removePlugin)
|
||||
|
||||
def removePlugin(self, cls):
|
||||
log.info("Removing plugin %s", cls.__name__)
|
||||
|
@ -2,11 +2,13 @@
|
||||
plugins
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
import itertools
|
||||
import os
|
||||
|
||||
import imp
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
from collections import defaultdict
|
||||
|
||||
from mcedit2 import editortools
|
||||
from mcedit2.editortools import generate
|
||||
from mcedit2.plugins import command
|
||||
@ -115,9 +117,15 @@ class PluginRef(object):
|
||||
_currentPluginPathname = pathname
|
||||
|
||||
self.pluginModule = imp.load_module(basename, io, pathname, description)
|
||||
self.registerModule()
|
||||
_loadedModules[self.fullpath] = self.pluginModule
|
||||
self.pluginModule.__FOUND_FILENAME__ = self.fullpath
|
||||
|
||||
_currentPluginPathname = None
|
||||
|
||||
# Plugin registration is deferred until the module is fully loaded.
|
||||
for registry in _registries:
|
||||
registry.commitRegistrations()
|
||||
|
||||
if hasattr(self.pluginModule, 'displayName'):
|
||||
self._displayName = self.pluginModule.displayName
|
||||
|
||||
@ -134,19 +142,14 @@ class PluginRef(object):
|
||||
|
||||
return True
|
||||
|
||||
def registerModule(self):
|
||||
module = self.pluginModule
|
||||
|
||||
_loadedModules[self.fullpath] = module
|
||||
module.__FOUND_FILENAME__ = self.fullpath
|
||||
|
||||
def unload(self):
|
||||
if self.pluginModule is None:
|
||||
return
|
||||
module = self.pluginModule
|
||||
|
||||
try:
|
||||
self.unregisterModule()
|
||||
if hasattr(module, "unregister"):
|
||||
module.unregister()
|
||||
|
||||
except Exception as e:
|
||||
self.unloadError = sys.exc_info()
|
||||
@ -156,24 +159,24 @@ class PluginRef(object):
|
||||
self.unloadError = None
|
||||
finally:
|
||||
self.pluginModule = None
|
||||
deadKeys = []
|
||||
for k, v in sys.modules.iteritems():
|
||||
if v is module:
|
||||
deadKeys.append(k)
|
||||
|
||||
for k in deadKeys:
|
||||
sys.modules.pop(k)
|
||||
log.info("Removed module %s from sys.modules", k)
|
||||
|
||||
return True
|
||||
|
||||
def unregisterModule(self):
|
||||
module = self.pluginModule
|
||||
if hasattr(module, "unregister"):
|
||||
module.unregister()
|
||||
|
||||
classes = _pluginClassesByPathname.pop(self.fullpath)
|
||||
if classes:
|
||||
for cls in classes:
|
||||
_unregisterClass(cls)
|
||||
|
||||
_loadedModules.pop(module.__FOUND_FILENAME__)
|
||||
self.fullpath = None
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def isLoaded(self):
|
||||
@ -256,6 +259,10 @@ _loadedModules = {}
|
||||
_pluginClassesByPathname = defaultdict(list)
|
||||
_currentPluginPathname = None
|
||||
|
||||
_registries = [
|
||||
command.CommandPlugins,
|
||||
generate.GeneratePlugins
|
||||
]
|
||||
|
||||
def _registerClass(cls):
|
||||
_pluginClassesByPathname[_currentPluginPathname].append(cls)
|
||||
@ -265,10 +272,11 @@ def _registerClass(cls):
|
||||
def _unregisterClass(cls):
|
||||
load_ui.unregisterCustomWidget(cls)
|
||||
editortools.unregisterToolClass(cls)
|
||||
generate.unregisterGeneratePlugin(cls)
|
||||
inspector.unregisterBlockInspectorWidget(cls)
|
||||
entities.unregisterTileEntityRefClass(cls)
|
||||
command.unregisterPluginCommand(cls)
|
||||
|
||||
generate.GeneratePlugins.unregisterClass(cls)
|
||||
command.CommandPlugins.unregisterClass(cls)
|
||||
|
||||
# --- Registration functions ---
|
||||
|
||||
@ -290,7 +298,7 @@ def registerPluginCommand(cls):
|
||||
cls : Class
|
||||
"""
|
||||
_registerClass(cls)
|
||||
return command.registerPluginCommand(cls)
|
||||
return command.CommandPlugins.registerClass(cls)
|
||||
|
||||
|
||||
def registerCustomWidget(cls):
|
||||
@ -354,7 +362,7 @@ def registerGeneratePlugin(cls):
|
||||
cls : Class
|
||||
"""
|
||||
_registerClass(cls)
|
||||
return generate.registerGeneratePlugin(cls)
|
||||
return generate.GeneratePlugins.registerClass(cls)
|
||||
|
||||
def registerBlockInspectorWidget(cls):
|
||||
"""
|
||||
|
@ -7,6 +7,8 @@ from collections import defaultdict
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
from mcedit2.plugins.registry import PluginClassRegistry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -52,28 +54,10 @@ class PluginCommand(QtCore.QObject):
|
||||
self.editorSession.menuPlugins.addPluginMenuItem(self.__class__, text, func, submenu)
|
||||
|
||||
|
||||
class _CommandPlugins(QtCore.QObject):
|
||||
pluginRemoved = QtCore.Signal(object)
|
||||
pluginAdded = QtCore.Signal(object)
|
||||
class _CommandPlugins(PluginClassRegistry):
|
||||
pluginClass = PluginCommand
|
||||
|
||||
_CommandPlugins.instance = _CommandPlugins()
|
||||
|
||||
_registeredCommands = []
|
||||
|
||||
|
||||
def registerPluginCommand(cls):
|
||||
if issubclass(cls, PluginCommand):
|
||||
_registeredCommands.append(cls)
|
||||
else:
|
||||
raise ValueError("Class %s must inherit from PluginCommand" % cls)
|
||||
|
||||
_CommandPlugins.instance.pluginAdded.emit(cls)
|
||||
return cls
|
||||
|
||||
|
||||
def unregisterPluginCommand(cls):
|
||||
if issubclass(cls, PluginCommand):
|
||||
_CommandPlugins.instance.pluginRemoved.emit(cls)
|
||||
CommandPlugins = _CommandPlugins()
|
||||
|
||||
|
||||
class PluginsMenu(QtGui.QMenu):
|
||||
@ -82,14 +66,14 @@ class PluginsMenu(QtGui.QMenu):
|
||||
self.setTitle(self.tr("Plugins"))
|
||||
self.editorSession = editorSession
|
||||
self.submenus = {}
|
||||
_CommandPlugins.instance.pluginRemoved.connect(self.pluginRemoved)
|
||||
_CommandPlugins.instance.pluginAdded.connect(self.pluginAdded)
|
||||
CommandPlugins.pluginRemoved.connect(self.pluginRemoved)
|
||||
CommandPlugins.pluginAdded.connect(self.pluginAdded)
|
||||
self.plugins = []
|
||||
|
||||
self.actionsByClass = defaultdict(list)
|
||||
|
||||
def loadPlugins(self):
|
||||
for cls in _registeredCommands:
|
||||
for cls in CommandPlugins.registeredPlugins:
|
||||
instance = cls(self.editorSession)
|
||||
self.plugins.append(instance)
|
||||
|
||||
@ -119,5 +103,5 @@ class PluginsMenu(QtGui.QMenu):
|
||||
action = menu.addAction(text, func)
|
||||
|
||||
self.actionsByClass[cls].append(action)
|
||||
log.info("Added action %s")
|
||||
log.info("Added action %s", action)
|
||||
return action
|
||||
|
38
src/mcedit2/plugins/registry.py
Normal file
38
src/mcedit2/plugins/registry.py
Normal file
@ -0,0 +1,38 @@
|
||||
"""
|
||||
registry
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
import logging
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PluginClassRegistry(QtCore.QObject):
|
||||
pluginRemoved = QtCore.Signal(object)
|
||||
pluginAdded = QtCore.Signal(object)
|
||||
|
||||
pluginClass = NotImplemented
|
||||
|
||||
def __init__(self):
|
||||
super(PluginClassRegistry, self).__init__()
|
||||
self.pendingRegistrations = []
|
||||
self.registeredPlugins = []
|
||||
|
||||
def commitRegistrations(self):
|
||||
for cls in self.pendingRegistrations:
|
||||
self.registeredPlugins.append(cls)
|
||||
self.pluginAdded.emit(cls)
|
||||
self.pendingRegistrations[:] = []
|
||||
|
||||
def registerClass(self, cls):
|
||||
if issubclass(cls, self.pluginClass):
|
||||
self.pendingRegistrations.append(cls)
|
||||
else:
|
||||
raise ValueError("Class %s must inherit from PluginCommand" % cls)
|
||||
return cls
|
||||
|
||||
def unregisterClass(self, cls):
|
||||
if issubclass(cls, self.pluginClass):
|
||||
self.pluginRemoved.emit(cls)
|
Reference in New Issue
Block a user