diff piecrust/templating/jinjaengine.py @ 89:e771c202583a

Fixes to the `cache` Jinja tag. * Thread safety, since it stores common data potentially coming from pages baked at the same time. * Correctly capture and restore modifications made to the execution context (e.g. sources used in the captured section).
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 04 Sep 2014 08:13:39 -0700
parents 071cc99b1779
children 28444014ce7d
line wrap: on
line diff
--- a/piecrust/templating/jinjaengine.py	Thu Sep 04 08:11:28 2014 -0700
+++ b/piecrust/templating/jinjaengine.py	Thu Sep 04 08:13:39 2014 -0700
@@ -1,6 +1,7 @@
 import re
 import time
 import logging
+import threading
 import strict_rfc3339
 from jinja2 import Environment, FileSystemLoader, TemplateNotFound
 from jinja2.exceptions import TemplateSyntaxError
@@ -272,6 +273,7 @@
 
     def __init__(self, environment):
         super(PieCrustCacheExtension, self).__init__(environment)
+        self._lock = threading.RLock()
 
         environment.extend(
             piecrust_cache_prefix='',
@@ -301,15 +303,29 @@
     def _cache_support(self, name, caller):
         key = self.environment.piecrust_cache_prefix + name
 
+        exc_stack = self.environment.app.env.exec_info_stack
+        render_ctx = exc_stack.current_page_info.render_ctx
+
         # try to load the block from the cache
         # if there is no fragment in the cache, render it and store
         # it in the cache.
-        rv = self.environment.piecrust_cache.get(key)
-        if rv is not None:
+        pair = self.environment.piecrust_cache.get(key)
+        if pair is not None:
+            render_ctx.used_source_names.update(pair[1])
+            return pair[0]
+
+        with self._lock:
+            pair = self.environment.piecrust_cache.get(key)
+            if pair is not None:
+                render_ctx.used_source_names.update(pair[1])
+                return pair[0]
+
+            prev_used = render_ctx.used_source_names.copy()
+            rv = caller()
+            after_used = render_ctx.used_source_names.copy()
+            used_delta = after_used.difference(prev_used)
+            self.environment.piecrust_cache[key] = (rv, used_delta)
             return rv
-        rv = caller()
-        self.environment.piecrust_cache[key] = rv
-        return rv
 
 
 class PieCrustSpacelessExtension(HtmlCompressor):