[web/staticcontrollers] create a unique temporary file for concat handling
authorJulien Cristau <julien.cristau@logilab.fr>
Wed, 05 Feb 2014 09:23:36 +0100
changeset 9497 7310738fafe5
parent 9496 e699fbcc9a61
child 9498 a76ac18f09c4
[web/staticcontrollers] create a unique temporary file for concat handling Predictable names means different processes can race against each other. Let's avoid that and use mkstemp to get a real unique temporary file, and atomically rename it when we're done. Closes #3524182
web/views/staticcontrollers.py
--- a/web/views/staticcontrollers.py	Wed Jan 29 15:50:19 2014 +0100
+++ b/web/views/staticcontrollers.py	Wed Feb 05 09:23:36 2014 +0100
@@ -27,6 +27,7 @@
 import hashlib
 import mimetypes
 import threading
+import tempfile
 from time import mktime
 from datetime import datetime, timedelta
 from logging import getLogger
@@ -145,32 +146,34 @@
     def concat_cached_filepath(self, paths):
         filepath = self.build_filepath(paths)
         if not self._up_to_date(filepath, paths):
-            tmpfile = filepath + '.tmp'
-            try:
-                with self.lock:
-                    if self._up_to_date(filepath, paths):
-                        # first check could have raced with some other thread
-                        # updating the file
-                        return filepath
-                    with open(tmpfile, 'wb') as f:
-                        for path in paths:
-                            dirpath, rid = self._resource(path)
-                            if rid is None:
-                                # In production mode log an error, do not return a 404
-                                # XXX the erroneous content is cached anyway
-                                self.logger.error('concatenated data url error: %r file '
-                                                  'does not exist', path)
-                                if self.config.debugmode:
-                                    raise NotFound(path)
-                            else:
-                                with open(osp.join(dirpath, rid), 'rb') as source:
-                                    for line in source:
-                                        f.write(line)
-                                f.write('\n')
+            with self.lock:
+                if self._up_to_date(filepath, paths):
+                    # first check could have raced with some other thread
+                    # updating the file
+                    return filepath
+                fd, tmpfile = tempfile.mkstemp(dir=os.path.dirname(filepath))
+                try:
+                    f = os.fdopen(fd, 'wb')
+                    for path in paths:
+                        dirpath, rid = self._resource(path)
+                        if rid is None:
+                            # In production mode log an error, do not return a 404
+                            # XXX the erroneous content is cached anyway
+                            self.logger.error('concatenated data url error: %r file '
+                                              'does not exist', path)
+                            if self.config.debugmode:
+                                raise NotFound(path)
+                        else:
+                            with open(osp.join(dirpath, rid), 'rb') as source:
+                                for line in source:
+                                    f.write(line)
+                            f.write('\n')
+                    f.close()
+                except:
+                    os.remove(tmpfile)
+                    raise
+                else:
                     os.rename(tmpfile, filepath)
-            except:
-                os.remove(tmpfile)
-                raise
         return filepath