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