[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
--- 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