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 os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
from Debug import Debug
|
||||
from Config import config
|
||||
|
||||
|
||||
class PluginManager:
|
||||
|
||||
def __init__(self):
|
||||
self.log = logging.getLogger("PluginManager")
|
||||
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
|
||||
|
||||
sys.path.append(self.plugin_path)
|
||||
|
@ -42,10 +43,18 @@ class PluginManager:
|
|||
|
||||
# Reload all plugins
|
||||
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():
|
||||
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:
|
||||
reload(module)
|
||||
except Exception, err:
|
||||
|
@ -53,6 +62,38 @@ class PluginManager:
|
|||
|
||||
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
|
||||
|
||||
|
@ -63,6 +104,7 @@ plugin_manager = PluginManager() # Singletone
|
|||
|
||||
def acceptPlugins(base_class):
|
||||
class_name = base_class.__name__
|
||||
plugin_manager.pluggable[class_name] = base_class
|
||||
if class_name in plugin_manager.plugins: # Has plugins
|
||||
classes = plugin_manager.plugins[class_name][:] # Copy the current plugins
|
||||
classes.reverse()
|
||||
|
|
Loading…
Reference in a new issue