Hot-patch in memory plugin classes
This commit is contained in:
parent
67f75c4bef
commit
122ef02605
1 changed files with 46 additions and 4 deletions
|
@ -1,17 +1,18 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from Debug import Debug
|
from Debug import Debug
|
||||||
from Config import config
|
from Config import config
|
||||||
|
|
||||||
|
|
||||||
class PluginManager:
|
class PluginManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log = logging.getLogger("PluginManager")
|
self.log = logging.getLogger("PluginManager")
|
||||||
self.plugin_path = "plugins" # Plugin directory
|
self.plugin_path = "plugins" # Plugin directory
|
||||||
self.plugins = {} # Registered plugins (key: class name, value: list of plugins for class)
|
self.plugins = defaultdict(list) # Registered plugins (key: class name, value: list of plugins for class)
|
||||||
|
self.pluggable = {}
|
||||||
self.plugin_names = [] # Loaded plugin names
|
self.plugin_names = [] # Loaded plugin names
|
||||||
|
|
||||||
sys.path.append(self.plugin_path)
|
sys.path.append(self.plugin_path)
|
||||||
|
@ -42,10 +43,18 @@ class PluginManager:
|
||||||
|
|
||||||
# Reload all plugins
|
# Reload all plugins
|
||||||
def reloadPlugins(self):
|
def reloadPlugins(self):
|
||||||
self.plugins = {} # Reset registered plugins
|
self.plugins_before = self.plugins
|
||||||
|
self.plugins = defaultdict(list) # Reset registered plugins
|
||||||
for module_name, module in sys.modules.items():
|
for module_name, module in sys.modules.items():
|
||||||
if module and "__file__" in dir(module) and self.plugin_path in module.__file__: # Module file within plugin_path
|
if module and "__file__" in dir(module) and self.plugin_path in module.__file__: # Module file within plugin_path
|
||||||
if "allow_reload" not in dir(module) or module.allow_reload: # Check if reload disabled
|
if "allow_reload" in dir(module) and not module.allow_reload: # Reload disabled
|
||||||
|
# Re-add non-reloadable plugins
|
||||||
|
for class_name, classes in self.plugins_before.iteritems():
|
||||||
|
for c in classes:
|
||||||
|
if c.__module__ != module.__name__:
|
||||||
|
continue
|
||||||
|
self.plugins[class_name].append(c)
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
reload(module)
|
reload(module)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
|
@ -53,6 +62,38 @@ class PluginManager:
|
||||||
|
|
||||||
self.loadPlugins() # Load new plugins
|
self.loadPlugins() # Load new plugins
|
||||||
|
|
||||||
|
# Change current classes in memory
|
||||||
|
import gc
|
||||||
|
patched = {}
|
||||||
|
for class_name, classes in self.plugins.iteritems():
|
||||||
|
classes = classes[:] # Copy the current plugins
|
||||||
|
classes.reverse()
|
||||||
|
base_class = self.pluggable[class_name] # Original class
|
||||||
|
classes.append(base_class) # Add the class itself to end of inherience line
|
||||||
|
plugined_class = type(class_name, tuple(classes), dict()) # Create the plugined class
|
||||||
|
for obj in gc.get_objects():
|
||||||
|
if type(obj).__name__ == class_name:
|
||||||
|
obj.__class__ = plugined_class
|
||||||
|
patched[class_name] = patched.get(class_name, 0) + 1
|
||||||
|
self.log.debug("Patched objects: %s" % patched)
|
||||||
|
|
||||||
|
# Change classes in modules
|
||||||
|
patched = {}
|
||||||
|
for class_name, classes in self.plugins.iteritems():
|
||||||
|
for module_name, module in sys.modules.iteritems():
|
||||||
|
if class_name in dir(module):
|
||||||
|
if "__class__" not in dir(getattr(module, class_name)): # Not a class
|
||||||
|
continue
|
||||||
|
base_class = self.pluggable[class_name]
|
||||||
|
classes = self.plugins[class_name][:]
|
||||||
|
classes.reverse()
|
||||||
|
classes.append(base_class)
|
||||||
|
plugined_class = type(class_name, tuple(classes), dict())
|
||||||
|
setattr(module, class_name, plugined_class)
|
||||||
|
patched[class_name] = patched.get(class_name, 0) + 1
|
||||||
|
|
||||||
|
self.log.debug("Patched modules: %s" % patched)
|
||||||
|
|
||||||
|
|
||||||
plugin_manager = PluginManager() # Singletone
|
plugin_manager = PluginManager() # Singletone
|
||||||
|
|
||||||
|
@ -63,6 +104,7 @@ plugin_manager = PluginManager() # Singletone
|
||||||
|
|
||||||
def acceptPlugins(base_class):
|
def acceptPlugins(base_class):
|
||||||
class_name = base_class.__name__
|
class_name = base_class.__name__
|
||||||
|
plugin_manager.pluggable[class_name] = base_class
|
||||||
if class_name in plugin_manager.plugins: # Has plugins
|
if class_name in plugin_manager.plugins: # Has plugins
|
||||||
classes = plugin_manager.plugins[class_name][:] # Copy the current plugins
|
classes = plugin_manager.plugins[class_name][:] # Copy the current plugins
|
||||||
classes.reverse()
|
classes.reverse()
|
||||||
|
|
Loading…
Reference in a new issue