From 3d3c93c67dba0d73bd83cc0bf9260b71e6897505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Thu, 1 Dec 2016 13:35:54 +0100 Subject: [PATCH] Fix yet another settings caching issue That api endpoint really is a tough nut. ETag calculation now also takes full settings dump from settings plugins into account, because those might be providing custom keys through custom on_settings_load implementations, for which we will not notice any changes if we are only looking at the effective config. Of course, the more we put into that ETag calculation, the slower it will be and the less sense it will make. Somewhat annoying :/ --- src/octoprint/server/api/settings.py | 55 ++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/octoprint/server/api/settings.py b/src/octoprint/server/api/settings.py index 52ca7c32..9e3d337e 100644 --- a/src/octoprint/server/api/settings.py +++ b/src/octoprint/server/api/settings.py @@ -32,6 +32,13 @@ def _etag(lm=None): connection_options = printer.__class__.get_connection_options() plugins = sorted(octoprint.plugin.plugin_manager().enabled_plugins) + plugin_settings = _get_plugin_settings() + + from collections import OrderedDict + sorted_plugin_settings = OrderedDict() + for key in sorted(plugin_settings.keys()): + sorted_plugin_settings[key] = plugin_settings.get(key, dict()) + if current_user is not None and not current_user.is_anonymous(): roles = sorted(current_user.roles) else: @@ -39,11 +46,26 @@ def _etag(lm=None): import hashlib hash = hashlib.sha1() + + # last modified timestamp hash.update(str(lm)) + + # effective config from config.yaml + overlays hash.update(repr(settings().effective)) + + # might duplicate settings().effective, but plugins might also inject additional keys into the settings + # output that are not stored in config.yaml + hash.update(repr(sorted_plugin_settings)) + + # connection options are also part of the settings hash.update(repr(connection_options)) + + # if the list of plugins changes, the settings structure changes too hash.update(repr(plugins)) + + # and likewise if the role of the user changes hash.update(repr(roles)) + return hash.hexdigest() @api.route("/settings", methods=["GET"]) @@ -51,8 +73,6 @@ def _etag(lm=None): lastmodified_factory=_lastmodified, unless=lambda: request.values.get("force", "false") in valid_boolean_trues) def getSettings(): - logger = logging.getLogger(__name__) - s = settings() connectionOptions = printer.__class__.get_connection_options() @@ -180,32 +200,45 @@ def getSettings(): for name in gcode_scripts: data["scripts"]["gcode"][name] = s.loadScript("gcode", name, source=True) + plugin_settings = _get_plugin_settings() + if len(plugin_settings): + data["plugins"] = plugin_settings + + return jsonify(data) + + +def _get_plugin_settings(): + logger = logging.getLogger(__name__) + + data = dict() + def process_plugin_result(name, result): if result: try: jsonify(test=result) except: logger.exception("Error while jsonifying settings from plugin {}, please contact the plugin author about this".format(name)) - - if not "plugins" in data: - data["plugins"] = dict() - if "__enabled" in result: - del result["__enabled"] - data["plugins"][name] = result + raise + else: + if "__enabled" in result: + del result["__enabled"] + data[name] = result for plugin in octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SettingsPlugin): try: result = plugin.on_settings_load() process_plugin_result(plugin._identifier, result) except TypeError: - logger.warn("Could not load settings for plugin {name} ({version}) since it called super(...)".format(name=plugin._plugin_name, version=plugin._plugin_version)) + logger.warn("Could not load settings for plugin {name} ({version}) since it called super(...)".format(name=plugin._plugin_name, + version=plugin._plugin_version)) logger.warn("in a way which has issues due to OctoPrint's dynamic reloading after plugin operations.") logger.warn("Please contact the plugin's author and ask to update the plugin to use a direct call like") logger.warn("octoprint.plugin.SettingsPlugin.on_settings_load(self) instead.") except: - logger.exception("Could not load settings for plugin {name} ({version})".format(version=plugin._plugin_version, name=plugin._plugin_name)) + logger.exception("Could not load settings for plugin {name} ({version})".format(version=plugin._plugin_version, + name=plugin._plugin_name)) - return jsonify(data) + return data @api.route("/settings", methods=["POST"])