Hot-patch in memory plugin classes

This commit is contained in:
ZeroNet 2016-08-10 12:43:35 +02:00
parent 67f75c4bef
commit 122ef02605

View file

@ -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()