Allow plugins to define settings overlays
This commit is contained in:
parent
c879ae4099
commit
19ff9ac664
4 changed files with 104 additions and 12 deletions
|
|
@ -201,8 +201,65 @@ def init_logging(settings, use_logging_file=True, logging_file=None, default_con
|
|||
|
||||
def init_pluginsystem(settings):
|
||||
"""Initializes the plugin manager based on the settings."""
|
||||
|
||||
from octoprint.plugin import plugin_manager
|
||||
return plugin_manager(init=True, settings=settings)
|
||||
pm = plugin_manager(init=True, settings=settings)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
settings_overlays = dict()
|
||||
disabled_from_overlays = dict()
|
||||
|
||||
def handle_plugin_loaded(name, plugin):
|
||||
if hasattr(plugin.instance, "__plugin_settings_overlay__"):
|
||||
# plugin has a settings overlay, inject it
|
||||
overlay_definition = getattr(plugin.instance, "__plugin_settings_overlay__")
|
||||
if isinstance(overlay_definition, (tuple, list)):
|
||||
overlay_definition, order = overlay_definition
|
||||
else:
|
||||
order = None
|
||||
|
||||
overlay = settings.load_overlay(overlay_definition)
|
||||
|
||||
if "plugins" in overlay and "_disabled" in overlay["plugins"]:
|
||||
disabled_plugins = overlay["plugins"]["_disabled"]
|
||||
del overlay["plugins"]["_disabled"]
|
||||
disabled_from_overlays[name] = (disabled_plugins, order)
|
||||
|
||||
settings_overlays[name] = overlay
|
||||
logger.debug("Found settings overlay on plugin {}".format(name))
|
||||
|
||||
def handle_plugins_loaded(startup=False, initialize_implementations=True, force_reload=None):
|
||||
if not startup:
|
||||
return
|
||||
|
||||
sorted_disabled_from_overlays = sorted([(key, value[0], value[1]) for key, value in disabled_from_overlays.items()], key=lambda x: (x[2] is None, x[2], x[0]))
|
||||
|
||||
disabled_list = pm.plugin_disabled_list
|
||||
already_processed = []
|
||||
for name, addons, _ in sorted_disabled_from_overlays:
|
||||
if not name in disabled_list and not name.endswith("disabled"):
|
||||
for addon in addons:
|
||||
if addon in disabled_list:
|
||||
continue
|
||||
|
||||
if addon in already_processed:
|
||||
logger.info("Plugin {} wants to disable plugin {}, but that was already processed".format(name, addon))
|
||||
|
||||
if not addon in already_processed and not addon in disabled_list:
|
||||
disabled_list.append(addon)
|
||||
logger.info("Disabling plugin {} as defined by plugin {} through settings overlay".format(addon, name))
|
||||
already_processed.append(name)
|
||||
|
||||
def handle_plugin_enabled(name, plugin):
|
||||
if name in settings_overlays:
|
||||
settings.add_overlay(settings_overlays[name])
|
||||
logger.info("Added settings overlay from plugin {}".format(name))
|
||||
|
||||
pm.on_plugin_loaded = handle_plugin_loaded
|
||||
pm.on_plugins_loaded = handle_plugins_loaded
|
||||
pm.on_plugin_enabled = handle_plugin_enabled
|
||||
pm.reload_plugins(startup=True, initialize_implementations=False)
|
||||
return pm
|
||||
|
||||
#~~ server main method
|
||||
|
||||
|
|
|
|||
|
|
@ -472,6 +472,9 @@ class PluginManager(object):
|
|||
self.on_plugin_disabled = lambda *args, **kwargs: None
|
||||
self.on_plugin_implementations_initialized = lambda *args, **kwargs: None
|
||||
|
||||
self.on_plugins_loaded = lambda *args, **kwargs: None
|
||||
self.on_plugins_enabled = lambda *args, **kwargs: None
|
||||
|
||||
self.registered_clients = []
|
||||
|
||||
self.marked_plugins = defaultdict(list)
|
||||
|
|
@ -480,8 +483,6 @@ class PluginManager(object):
|
|||
self._python_virtual_env = False
|
||||
self._detect_python_environment()
|
||||
|
||||
self.reload_plugins(startup=True, initialize_implementations=False)
|
||||
|
||||
def _detect_python_environment(self):
|
||||
from distutils.command.install import install as cmd_install
|
||||
from distutils.dist import Distribution
|
||||
|
|
@ -667,16 +668,33 @@ class PluginManager(object):
|
|||
plugins = self.find_plugins(existing=dict((k, v) for k, v in self.plugins.items() if not k in force_reload))
|
||||
self.disabled_plugins.update(plugins)
|
||||
|
||||
# 1st pass: loading the plugins
|
||||
for name, plugin in plugins.items():
|
||||
try:
|
||||
self.load_plugin(name, plugin, startup=startup, initialize_implementation=initialize_implementations)
|
||||
if not self._is_plugin_disabled(name):
|
||||
except PluginNeedsRestart:
|
||||
pass
|
||||
except PluginLifecycleException as e:
|
||||
self.logger.info(str(e))
|
||||
|
||||
self.on_plugins_loaded(startup=startup,
|
||||
initialize_implementations=initialize_implementations,
|
||||
force_reload=force_reload)
|
||||
|
||||
# 2nd pass: enabling those plugins that need enabling
|
||||
for name, plugin in plugins.items():
|
||||
try:
|
||||
if plugin.loaded and not self._is_plugin_disabled(name):
|
||||
self.enable_plugin(name, plugin=plugin, initialize_implementation=initialize_implementations, startup=startup)
|
||||
except PluginNeedsRestart:
|
||||
pass
|
||||
except PluginLifecycleException as e:
|
||||
self.logger.info(str(e))
|
||||
|
||||
self.on_plugins_enabled(startup=startup,
|
||||
initialize_implementations=initialize_implementations,
|
||||
force_reload=force_reload)
|
||||
|
||||
if len(self.enabled_plugins) <= 0:
|
||||
self.logger.info("No plugins found")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1165,17 +1165,24 @@ class LifecycleManager(object):
|
|||
self._plugin_lifecycle_callbacks = defaultdict(list)
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
def wrap_plugin_event(lifecycle_event, new_handler):
|
||||
orig_handler = getattr(self._plugin_manager, "on_plugin_" + lifecycle_event)
|
||||
|
||||
def handler(*args, **kwargs):
|
||||
if callable(orig_handler):
|
||||
orig_handler(*args, **kwargs)
|
||||
if callable(new_handler):
|
||||
new_handler(*args, **kwargs)
|
||||
|
||||
return handler
|
||||
|
||||
def on_plugin_event_factory(lifecycle_event):
|
||||
def on_plugin_event(name, plugin):
|
||||
self.on_plugin_event(lifecycle_event, name, plugin)
|
||||
return on_plugin_event
|
||||
|
||||
self._plugin_manager.on_plugin_loaded = on_plugin_event_factory("loaded")
|
||||
self._plugin_manager.on_plugin_unloaded = on_plugin_event_factory("unloaded")
|
||||
self._plugin_manager.on_plugin_activated = on_plugin_event_factory("activated")
|
||||
self._plugin_manager.on_plugin_deactivated = on_plugin_event_factory("deactivated")
|
||||
self._plugin_manager.on_plugin_enabled = on_plugin_event_factory("enabled")
|
||||
self._plugin_manager.on_plugin_disabled = on_plugin_event_factory("disabled")
|
||||
for event in ("loaded", "unloaded", "enabled", "disabled"):
|
||||
wrap_plugin_event(event, on_plugin_event_factory(event))
|
||||
|
||||
def on_plugin_event(self, event, name, plugin):
|
||||
for lifecycle_callback in self._plugin_lifecycle_callbacks[event]:
|
||||
|
|
|
|||
|
|
@ -720,9 +720,16 @@ class Settings(object):
|
|||
if migrate:
|
||||
self._migrate_config()
|
||||
|
||||
def add_overlay(self, overlay, migrate=False):
|
||||
def load_overlay(self, overlay, migrate=True):
|
||||
config = None
|
||||
|
||||
if callable(overlay):
|
||||
try:
|
||||
overlay = overlay()
|
||||
except:
|
||||
self._logger.exception("Error loading overlay from callable")
|
||||
return
|
||||
|
||||
if isinstance(overlay, basestring):
|
||||
if os.path.exists(overlay) and os.path.isfile(overlay):
|
||||
with open(overlay, "r") as f:
|
||||
|
|
@ -737,7 +744,10 @@ class Settings(object):
|
|||
|
||||
if migrate:
|
||||
self._migrate_config(config)
|
||||
self._map.parents.maps.insert(0, config)
|
||||
return config
|
||||
|
||||
def add_overlay(self, overlay):
|
||||
self._map.maps.insert(1, overlay)
|
||||
|
||||
def _migrate_config(self, config=None):
|
||||
if config is None:
|
||||
|
|
|
|||
Loading…
Reference in a new issue