[fix] fix path unicity process in BytesFileSystemStorage.new_fs_path stable
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Mon, 12 Apr 2010 15:21:08 +0200
branchstable
changeset 5218 aebd00a2d316
parent 5217 08e7fa906cdb
child 5219 35d44017c72b
[fix] fix path unicity process in BytesFileSystemStorage.new_fs_path The previous implementation was bugged (prefixing the whole path with '_' instead of the base name). A new version (using number) replace it. * * * Improve BytesFileSystemStorage.new_fs_path to use available metadata This version try to get some hint about how to name the file using metadata. Using the real file name and extension when available. Keeping the extension might be usefull for exemple in the case of processing that use filename extension to detect content-type.
server/sources/storages.py
--- a/server/sources/storages.py	Mon Apr 12 15:15:00 2010 +0200
+++ b/server/sources/storages.py	Mon Apr 12 15:21:08 2010 +0200
@@ -1,6 +1,8 @@
 """custom storages for the system source"""
 from os import unlink, path as osp
 
+from yams.schema import role_name
+
 from cubicweb import Binary
 from cubicweb.server.hook import Operation
 
@@ -54,6 +56,22 @@
 # * better file path attribution
 # * handle backup/restore
 
+def uniquify_path(dirpath, basename):
+    """return a unique file name for `basename` in `dirpath`, or None
+    if all attemps failed.
+
+    XXX subject to race condition.
+    """
+    path = osp.join(dirpath, basename)
+    if not osp.isfile(path):
+        return path
+    base, ext = osp.splitext(path)
+    for i in xrange(1, 256):
+        path = '%s%s%s' % (base, i, ext)
+        if not osp.isfile(path):
+            return path
+    return None
+
 class BytesFileSystemStorage(Storage):
     """store Bytes attribute value on the file system"""
     def __init__(self, defaultdir):
@@ -102,16 +120,27 @@
         DeleteFileOp(entity._cw, filepath=self.current_fs_path(entity, attr))
 
     def new_fs_path(self, entity, attr):
-        fspath = osp.join(self.default_directory, '%s_%s' % (entity.eid, attr))
-        while osp.exists(fspath):
-            fspath = '_' + fspath
+        basename = [str(entity.eid), attr]
+        # We try to get some hint about how to name the file using attributes
+        # metadata. Using the real file name and extension when available.
+        #
+        # Keeping the extension might be usefull for exemple in the case of PIL
+        # processing that use filename extension to detect content-type.
+        name = entity.attr_metadata(attr, 'name')
+        if name is not None:
+            basename.append(name.encode(entity._cw.encoding))
+        fspath = uniquify_path(self.default_directory, '_'.join(basename))
+        if fspath is None:
+            msg = entity._cw._('failed to uniquify path (%s, %s)') % (
+                dirpath, basename)
+            raise ValidationError(entity.eid, {role_name(attr, 'subject'): msg})
         return fspath
 
     def current_fs_path(self, entity, attr):
         sysource = entity._cw.pool.source('system')
         cu = sysource.doexec(entity._cw,
                              'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % (
-                                 attr, entity.__regid__, entity.eid))
+                             attr, entity.__regid__, entity.eid))
         rawvalue = cu.fetchone()[0]
         if rawvalue is None: # no previous value
             return self.new_fs_path(entity, attr)