[web] remove PropertySheet._cache
authorJulien Cristau <julien.cristau@logilab.fr>
Mon, 23 Nov 2015 17:58:10 +0100
changeset 10897 7c386161ebd6
parent 10896 ac64eeddd50a
child 10898 c96d67251a9d
[web] remove PropertySheet._cache Instead of remembering that a css file has been processed, we re-process it at every request. This prevents our cache getting out of date with the file system. Closes #6893185 While we're at it, write the file atomically so that we don't risk serving partial data.
web/propertysheet.py
web/test/unittest_propertysheet.py
--- a/web/propertysheet.py	Mon Nov 23 17:47:46 2015 +0100
+++ b/web/propertysheet.py	Mon Nov 23 17:58:10 2015 +0100
@@ -22,6 +22,8 @@
 import re
 import os
 import os.path as osp
+import tempfile
+
 
 TYPE_CHECKS = [('STYLESHEETS', list), ('JAVASCRIPTS', list),
                ('STYLESHEETS_IE', list), ('STYLESHEETS_PRINT', list),
@@ -52,7 +54,6 @@
         self._ordered_propfiles = []
         self._propfile_mtime = {}
         self._sourcefile_mtime = {}
-        self._cache = {}
 
     def load(self, fpath):
         scriptglobals = self.context.copy()
@@ -69,9 +70,6 @@
         self._ordered_propfiles.append(fpath)
 
     def need_reload(self):
-        for rid, (adirectory, rdirectory, mtime) in list(self._cache.items()):
-            if os.stat(osp.join(rdirectory, rid)).st_mtime > mtime:
-                del self._cache[rid]
         for fpath, mtime in self._propfile_mtime.items():
             if os.stat(fpath).st_mtime > mtime:
                 return True
@@ -88,31 +86,29 @@
             self.reload()
 
     def process_resource(self, rdirectory, rid):
+        cachefile = osp.join(self._cache_directory, rid)
+        self.debug('processing %s/%s into %s',
+                   rdirectory, rid, cachefile)
+        rcachedir = osp.dirname(cachefile)
+        if not osp.exists(rcachedir):
+            os.makedirs(rcachedir)
+        sourcefile = osp.join(rdirectory, rid)
+        with open(sourcefile) as f:
+            content = f.read()
+        # XXX replace % not followed by a paren by %% to avoid having to do
+        # this in the source css file ?
         try:
-            return self._cache[rid][0]
-        except KeyError:
-            cachefile = osp.join(self._cache_directory, rid)
-            self.debug('caching processed %s/%s into %s',
-                       rdirectory, rid, cachefile)
-            rcachedir = osp.dirname(cachefile)
-            if not osp.exists(rcachedir):
-                os.makedirs(rcachedir)
-            sourcefile = osp.join(rdirectory, rid)
-            content = open(sourcefile).read()
-            # XXX replace % not followed by a paren by %% to avoid having to do
-            # this in the source css file ?
-            try:
-                content = self.compile(content)
-            except ValueError as ex:
-                self.error("can't process %s/%s: %s", rdirectory, rid, ex)
-                adirectory = rdirectory
-            else:
-                stream = open(cachefile, 'w')
+            content = self.compile(content)
+        except ValueError as ex:
+            self.error("can't process %s/%s: %s", rdirectory, rid, ex)
+            adirectory = rdirectory
+        else:
+            tmpfd, tmpfile = tempfile.mkstemp(dir=rcachedir, prefix=osp.basename(cachefile))
+            with os.fdopen(tmpfd, 'w') as stream:
                 stream.write(content)
-                stream.close()
-                adirectory = self._cache_directory
-            self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile).st_mtime)
-            return adirectory
+            os.rename(tmpfile, cachefile)
+            adirectory = self._cache_directory
+        return adirectory
 
     def compile(self, content):
         return self._percent_rgx.sub('%%', content) % self
--- a/web/test/unittest_propertysheet.py	Mon Nov 23 17:47:46 2015 +0100
+++ b/web/test/unittest_propertysheet.py	Mon Nov 23 17:58:10 2015 +0100
@@ -49,19 +49,14 @@
                           'a {bgcolor: #FFFFFF; size: 1%;}')
         self.assertEqual(ps.process_resource(DATADIR, 'pouet.css'),
                          self.cachedir)
-        self.assertIn('pouet.css', ps._cache)
         self.assertFalse(ps.need_reload())
         os.utime(self.data('sheet1.py'), None)
-        self.assertIn('pouet.css', ps._cache)
         self.assertTrue(ps.need_reload())
-        self.assertIn('pouet.css', ps._cache)
         ps.reload()
-        self.assertNotIn('pouet.css', ps._cache)
         self.assertFalse(ps.need_reload())
         ps.process_resource(DATADIR, 'pouet.css') # put in cache
         os.utime(self.data('pouet.css'), None)
         self.assertFalse(ps.need_reload())
-        self.assertNotIn('pouet.css', ps._cache)
 
 
 if __name__ == '__main__':