diff --git a/src/octoprint/plugin/__init__.py b/src/octoprint/plugin/__init__.py index 447e4791..1df93a9b 100644 --- a/src/octoprint/plugin/__init__.py +++ b/src/octoprint/plugin/__init__.py @@ -152,7 +152,7 @@ def plugin_settings(plugin_key, defaults=None, get_preprocessors=None, set_prepr return PluginSettings(settings(), plugin_key, defaults=defaults, get_preprocessors=get_preprocessors, set_preprocessors=set_preprocessors) -def call_plugin(types, method, args=None, kwargs=None, callback=None, error_callback=None): +def call_plugin(types, method, args=None, kwargs=None, callback=None, error_callback=None, sorting_context=None): """ Helper method to invoke the indicated ``method`` on all registered plugin implementations implementing the indicated ``types``. Allows providing method arguments and registering callbacks to call in case of success @@ -198,7 +198,7 @@ def call_plugin(types, method, args=None, kwargs=None, callback=None, error_call if kwargs is None: kwargs = dict() - plugins = plugin_manager().get_implementations(*types) + plugins = plugin_manager().get_implementations(*types, sorting_context=sorting_context) for plugin in plugins: if hasattr(plugin, method): try: diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index 599eb01d..87d39a55 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -1046,7 +1046,7 @@ class PluginManager(object): result[h[0]] = h[1] return result - def get_implementations(self, *types): + def get_implementations(self, *types, **kwargs): """ Get all mixin implementations that implement *all* of the provided ``types``. @@ -1057,6 +1057,8 @@ class PluginManager(object): list: A list of all found implementations """ + sorting_context = kwargs.get("sorting_context", None) + result = None for t in types: @@ -1067,10 +1069,26 @@ class PluginManager(object): result = result.intersection(implementations) if result is None: - return dict() - return [impl[1] for impl in result] + return [] - def get_filtered_implementations(self, f, *types): + found_implementations = [impl[1] for impl in result] + + if sorting_context is not None: + def sort_func(impl): + sorting_value = None + if isinstance(impl, SorteablePlugin): + try: + sorting_value = impl.get_sorting_key(sorting_context) + except: + self.logger.exception("Error while trying to retrieve sorting order for plugin {}".format(impl._identifier)) + + return sorting_value is None, sorting_value + + found_implementations = sorted(found_implementations, key=sort_func) + + return found_implementations + + def get_filtered_implementations(self, f, sorting_context=None, *types): """ Get all mixin implementation that implementat *all* of the provided ``types`` and match the provided filter `f`. @@ -1083,7 +1101,7 @@ class PluginManager(object): """ assert callable(f) - implementations = self.get_implementations(*types) + implementations = self.get_implementations(*types, sorting_context=sorting_context) return filter(f, implementations) def get_helpers(self, name, *helpers): @@ -1242,6 +1260,10 @@ class Plugin(object): class RestartNeedingPlugin(Plugin): pass +class SorteablePlugin(Plugin): + def get_sorting_key(self, context=None): + return None + class PluginNeedsRestart(Exception): def __init__(self, name): Exception.__init__(self) diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index 252c04e2..ea11406a 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -18,7 +18,7 @@ __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agp __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License" -from .core import (Plugin, RestartNeedingPlugin) +from .core import (Plugin, RestartNeedingPlugin, SorteablePlugin) class OctoPrintPlugin(Plugin): @@ -99,7 +99,7 @@ class OctoPrintPlugin(Plugin): class ReloadNeedingPlugin(Plugin): pass -class StartupPlugin(OctoPrintPlugin): +class StartupPlugin(OctoPrintPlugin, SorteablePlugin): """ The ``StartupPlugin`` allows hooking into the startup of OctoPrint. It can be used to start up additional services on or just after the startup of the server. @@ -127,7 +127,7 @@ class StartupPlugin(OctoPrintPlugin): pass -class ShutdownPlugin(OctoPrintPlugin): +class ShutdownPlugin(OctoPrintPlugin, SorteablePlugin): """ The ``ShutdownPlugin`` allows hooking into the shutdown of OctoPrint. It's usually used in conjunction with the :class:`StartupPlugin` mixin, to cleanly shut down additional services again that where started by the :class:`StartupPlugin` diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 5dd13f00..a64b5109 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -417,7 +417,8 @@ class Server(object): # run our startup plugins octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, "on_startup", - args=(self._host, self._port)) + args=(self._host, self._port), + sorting_context="StartupPlugin.on_startup") def call_on_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) @@ -437,7 +438,8 @@ class Server(object): # control to the ioloop def work(): octoprint.plugin.call_plugin(octoprint.plugin.StartupPlugin, - "on_after_startup") + "on_after_startup", + sorting_context="StartupPlugin.on_after_startup") def call_on_after_startup(name, plugin): implementation = plugin.get_implementation(octoprint.plugin.StartupPlugin) @@ -458,7 +460,8 @@ class Server(object): observer.stop() observer.join() octoprint.plugin.call_plugin(octoprint.plugin.ShutdownPlugin, - "on_shutdown") + "on_shutdown", + sorting_context="ShutdownPlugin.on_shutdown") self._logger.info("Goodbye!") atexit.register(on_shutdown)