From 3de4f91f506de2734454ba104bd97d1ef66b8656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 24 Nov 2015 18:33:49 +0100 Subject: [PATCH] Decoupled decorator from PreemptiveCache class Coupling it led to problems (naturally) when there was no PreemptiveCache instance available yet. --- src/octoprint/server/__init__.py | 21 ++++++++-------- src/octoprint/server/util/flask.py | 39 ++++++++++++++++++------------ src/octoprint/server/views.py | 12 +++++---- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 8ac8a503..08f1b3bc 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -42,6 +42,7 @@ loginManager = None pluginManager = None appSessionManager = None pluginLifecycleManager = None +preemptiveCache = None principals = Principal(app) admin_permission = Permission(RoleNeed("admin")) @@ -61,6 +62,7 @@ import octoprint.util import octoprint.filemanager.storage import octoprint.filemanager.analysis import octoprint.slicing +from octoprint.server.util.flask import PreemptiveCache from . import util @@ -142,6 +144,7 @@ class Server(): global pluginManager global appSessionManager global pluginLifecycleManager + global preemptiveCache global debug from tornado.ioloop import IOLoop @@ -185,6 +188,7 @@ class Server(): printer = Printer(fileManager, analysisQueue, printerProfileManager) appSessionManager = util.flask.AppSessionManager() pluginLifecycleManager = LifecycleManager(pluginManager) + preemptiveCache = PreemptiveCache(os.path.join(s.getBaseFolder("data"), "preemptive_cache_config.yaml")) def octoprint_plugin_inject_factory(name, implementation): if not isinstance(implementation, octoprint.plugin.OctoPrintPlugin): @@ -199,7 +203,8 @@ class Server(): printer=printer, app_session_manager=appSessionManager, plugin_lifecycle_manager=pluginLifecycleManager, - data_folder=os.path.join(settings().getBaseFolder("data"), name) + data_folder=os.path.join(settings().getBaseFolder("data"), name), + preemptive_cache=preemptiveCache ) def settings_plugin_inject_factory(name, implementation): @@ -469,7 +474,7 @@ class Server(): # when we are through with that we also run our preemptive cache if settings().getBoolean(["devel", "cache", "preemptive"]): - self._execute_preemptive_flask_caching() + self._execute_preemptive_flask_caching(preemptiveCache) import threading threading.Thread(target=work).start() @@ -622,9 +627,6 @@ class Server(): response.headers.add("X-Clacks-Overhead", "GNU Terry Pratchett") return response - preemptive_cache = octoprint.server.util.flask.PreemptiveCache(os.path.join(settings().getBaseFolder("data"), "preemptive_flask_cache.yaml")) - preemptive_cache.attach_to_app(app) - def _setup_i18n(self, app): global babel global LOCALES @@ -670,19 +672,16 @@ class Server(): self._register_template_plugins() - def _execute_preemptive_flask_caching(self): + def _execute_preemptive_flask_caching(self, preemptive_cache): from werkzeug.test import EnvironBuilder import time - if not hasattr(app, "preemptive_cache"): - return - # we clean up entries from our preemptive cache settings that haven't been # accessed longer than server.preemptiveCache.until days preemptive_cache_timeout = settings().getInt(["server", "preemptiveCache", "until"]) cutoff_timestamp = time.time() + preemptive_cache_timeout * 24 * 60 * 60 - cache_data = app.preemptive_cache.clean_all_data(lambda root, entries: filter(lambda entry: "_timestamp" in entry and entry["_timestamp"] <= cutoff_timestamp, entries)) + cache_data = preemptive_cache.clean_all_data(lambda root, entries: filter(lambda entry: "_timestamp" in entry and entry["_timestamp"] <= cutoff_timestamp, entries)) if not cache_data: return @@ -697,7 +696,7 @@ class Server(): self._logger.info("Preemptively caching {} for {!r}".format(route, kwargs)) builder = EnvironBuilder(**kwargs) - with app.preemptive_cache.disable_timestamp_update(): + with preemptive_cache.disable_timestamp_update(): app(builder.get_environ(), lambda *a, **kw: None) except: self._logger.exception("Error while trying to preemptively cache {} for {!r}".format(route, kwargs)) diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py index 9d83eb39..3b376ebc 100644 --- a/src/octoprint/server/util/flask.py +++ b/src/octoprint/server/util/flask.py @@ -387,21 +387,17 @@ class PreemptiveCache(object): self._logger = logging.getLogger(__name__ + "." + self.__class__.__name__) self._update_timestamp = True - def recorded(self, data, unless=None): - def decorator(f): - @functools.wraps(f) - def decorated_function(*args, **kwargs): - if not (callable(unless) and unless()): - entry_data = data - if callable(entry_data): - entry_data = entry_data() + def record(self, data, unless=None): + if callable(unless) and unless(): + return - if entry_data is not None: - from flask import request - self.add_data(request.path, entry_data) - return f(*args, **kwargs) - return decorated_function - return decorator + entry_data = data + if callable(entry_data): + entry_data = entry_data() + + if entry_data is not None: + from flask import request + self.add_data(request.path, entry_data) @contextlib.contextmanager def disable_timestamp_update(self): @@ -436,6 +432,10 @@ class PreemptiveCache(object): try: with open(self.cachefile, "r") as f: cache_data = yaml.safe_load(f) + except IOError as e: + import errno + if e.errno != errno.ENOENT: + raise except: self._logger.exception("Error while reading {}".format(self.cachefile)) @@ -515,8 +515,15 @@ class PreemptiveCache(object): self.set_data(root, [to_persist] + other) - def attach_to_app(self, app): - app.preemptive_cache = self + +def preemptively_cached(cache, data, unless=None): + def decorator(f): + @functools.wraps(f) + def decorated_function(*args, **kwargs): + cache.record(data, unless=unless) + return f(*args, **kwargs) + return decorated_function + return decorator def add_non_caching_response_headers(response): diff --git a/src/octoprint/server/views.py b/src/octoprint/server/views.py index 9f99c958..c25671e6 100644 --- a/src/octoprint/server/views.py +++ b/src/octoprint/server/views.py @@ -13,7 +13,7 @@ from flask import request, g, url_for, make_response, render_template, send_from import octoprint.plugin from octoprint.server import app, userManager, pluginManager, gettext, \ - debug, LOCALES, VERSION, DISPLAY_VERSION, UI_API_KEY, BRANCH + debug, LOCALES, VERSION, DISPLAY_VERSION, UI_API_KEY, BRANCH, preemptiveCache from octoprint.settings import settings import re @@ -27,8 +27,9 @@ _valid_id_re = re.compile("[a-z_]+") _valid_div_re = re.compile("[a-zA-Z_-]+") @app.route("/") -@app.preemptive_cache.recorded(data=lambda: dict(path=request.path, base_url=request.url_root, query_string="l10n={}".format(g.locale.language)) if g.locale else None, - unless=lambda: request.url_root in settings().get(["server", "preemptiveCache", "exceptions"])) +@util.flask.preemptively_cached(cache=preemptiveCache, + data=lambda: dict(path=request.path, base_url=request.url_root, query_string="l10n={}".format(g.locale.language)) if g.locale else None, + unless=lambda: request.url_root in settings().get(["server", "preemptiveCache", "exceptions"])) @util.flask.cached(timeout=-1, refreshif=lambda: util.flask.cache_check_headers() or "_refresh" in request.values, key=lambda: "view:{}:{}".format(request.base_url, g.locale.language if g.locale else "default"), @@ -403,8 +404,9 @@ def robotsTxt(): @app.route("/i18n//.js") -@app.preemptive_cache.recorded(data=lambda: dict(path=request.path, base_url=request.url_root), - unless=lambda: request.url_root in settings().get(["server", "preemptiveCache", "exceptions"])) +@util.flask.preemptively_cached(cache=preemptiveCache, + data=lambda: dict(path=request.path, base_url=request.url_root), + unless=lambda: request.url_root in settings().get(["server", "preemptiveCache", "exceptions"])) @util.flask.cached(timeout=-1, refreshif=lambda: util.flask.cache_check_headers() or "_refresh" in request.values, key=lambda: "view:{}".format(request.base_url))