Merge lp:~jderose/filestore/purge_tmp into lp:filestore

Proposed by Jason Gerard DeRose
Status: Merged
Approved by: James Raymond
Approved revision: 248
Merged at revision: 243
Proposed branch: lp:~jderose/filestore/purge_tmp
Merge into: lp:filestore
Diff against target: 159 lines (+106/-1)
3 files modified
doc/filestore.rst (+12/-1)
filestore.py (+22/-0)
test_filestore.py (+72/-0)
To merge this branch: bzr merge lp:~jderose/filestore/purge_tmp
Reviewer Review Type Date Requested Status
James Raymond Approve
Review via email: mp+110676@code.launchpad.net

Description of the change

Adds FileStore.purge_tmp() method, its test, and its documentation.

For more info, see the bug:

https://bugs.launchpad.net/filestore/+bug/1014214

To post a comment you must log in.
Revision history for this message
James Raymond (jamesmr) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'doc/filestore.rst'
--- doc/filestore.rst 2012-03-22 17:07:54 +0000
+++ doc/filestore.rst 2012-06-17 07:47:20 +0000
@@ -539,7 +539,7 @@
539 *batch* must be a :class:`Batch` namedtuple as returned by539 *batch* must be a :class:`Batch` namedtuple as returned by
540 :func:`scandir()`.540 :func:`scandir()`.
541541
542 You also must supply one of more *filestores*, which must be542 You also must supply one or more *filestores*, which must be
543 :class:`FileStore` instances.543 :class:`FileStore` instances.
544544
545 This function is a Python generator. For each non-empty file in *batch*, it545 This function is a Python generator. For each non-empty file in *batch*, it
@@ -761,6 +761,17 @@
761 .. method:: remove(_id)761 .. method:: remove(_id)
762762
763 Remove the file with *_id*.763 Remove the file with *_id*.
764
765 .. method:: purge_tmp()
766
767 Remove all files in .dmedia/tmp/
768
769 This method is for periodically purging stale temporary files. The
770 return value is a list with a :class:`File` namedtuple for each file
771 removed.
772
773 Symlinks are ignored and directories are not traversed, neither of which
774 should ever be created by the :class:`FileStore` itself.
764775
765776
766 .. method:: verify(_id, return_fp=False)777 .. method:: verify(_id, return_fp=False)
767778
=== modified file 'filestore.py'
--- filestore.py 2012-06-16 02:11:24 +0000
+++ filestore.py 2012-06-17 07:47:20 +0000
@@ -1252,6 +1252,28 @@
1252 log.info('Removing %r from %r', _id, self)1252 log.info('Removing %r from %r', _id, self)
1253 os.remove(filename)1253 os.remove(filename)
12541254
1255 def purge_tmp(self):
1256 """
1257 Remove all files in .dmedia/tmp/
1258
1259 This method is for periodically purging stale temporary files. The
1260 return value is a list with a `File` namedtuple for each file removed.
1261
1262 Symlinks are ignored and directories are not traversed, neither of which
1263 should ever be created by the `FileStore` itself.
1264 """
1265 purged = []
1266 for name in sorted(os.listdir(self.tmp)):
1267 fullname = path.join(self.tmp, name)
1268 st = os.lstat(fullname)
1269 if stat.S_ISREG(st.st_mode):
1270 purged.append(File(fullname, st.st_size, st.st_mtime))
1271 log.info('Removing stale temporary file %r', fullname)
1272 os.remove(fullname)
1273 else:
1274 log.info('Unexpected non-regular file %r', fullname)
1275 return purged
1276
1255 def allocate_tmp(self, size=None):1277 def allocate_tmp(self, size=None):
1256 """1278 """
1257 Allocate randomly named temporary file for an import or render.1279 Allocate randomly named temporary file for an import or render.
12581280
=== modified file 'test_filestore.py'
--- test_filestore.py 2012-06-16 02:13:09 +0000
+++ test_filestore.py 2012-06-17 07:47:20 +0000
@@ -32,12 +32,14 @@
32from subprocess import check_call32from subprocess import check_call
33import tempfile33import tempfile
34import shutil34import shutil
35from random import SystemRandom
3536
36from skein import skein51237from skein import skein512
3738
38import filestore39import filestore
3940
4041
42random = SystemRandom()
41TYPE_ERROR = '{}: need a {!r}; got a {!r}: {!r}'43TYPE_ERROR = '{}: need a {!r}; got a {!r}: {!r}'
4244
43B32NAMES = []45B32NAMES = []
@@ -2137,6 +2139,76 @@
2137 fs.remove(_id)2139 fs.remove(_id)
2138 self.assertFalse(path.exists(canonical))2140 self.assertFalse(path.exists(canonical))
21392141
2142 def test_purge_tmp(self):
2143 tmp = TempDir()
2144 t = tmp.join('.dmedia', 'tmp')
2145 fs = filestore.FileStore(tmp.dir)
2146 self.assertTrue(path.isdir(t))
2147
2148 # Test when nothing is in tmp:
2149 self.assertEqual(os.listdir(t), [])
2150 self.assertEqual(fs.purge_tmp(), [])
2151 self.assertEqual(os.listdir(t), [])
2152
2153 # Test when only regular files are in tmp:
2154 purged = []
2155 for i in range(20):
2156 size = random.randint(1, 1024 * 1024)
2157 fp = fs.allocate_tmp(size)
2158 purged.append(
2159 filestore.File(fp.name, size, path.getmtime(fp.name))
2160 )
2161 purged.sort(key=lambda f: f.name)
2162 self.assertEqual(fs.purge_tmp(), purged)
2163 self.assertEqual(os.listdir(t), [])
2164
2165 # Test that directories aren't traversed:
2166 nope = []
2167 for d in ['aye', 'bee', 'see']:
2168 for n in 'abcd':
2169 nope.append(tmp.touch('.dmedia', 'tmp', d, n))
2170 self.assertEqual(sorted(os.listdir(t)), ['aye', 'bee', 'see'])
2171 self.assertEqual(fs.purge_tmp(), [])
2172 self.assertEqual(sorted(os.listdir(t)), ['aye', 'bee', 'see'])
2173 for f in nope:
2174 self.assertTrue(path.isfile(f))
2175
2176 # Test that symlinks are ignored:
2177 tmp2 = TempDir()
2178 fs2 = filestore.FileStore(tmp2.dir)
2179 names = ['aye', 'bee', 'see']
2180 for i in range(10):
2181 size = random.randint(1, 1024 * 1024)
2182 fp = fs2.allocate_tmp(size)
2183 name = path.basename(fp.name)
2184 link = path.join(t, name)
2185 os.symlink(fp.name, link)
2186 names.append(name)
2187 nope.append(link)
2188 names.sort()
2189 self.assertEqual(sorted(os.listdir(t)), names)
2190 for f in nope:
2191 self.assertTrue(path.isfile(f))
2192
2193 # Add some regular files, including zero-sized files:
2194 purged = []
2195 for i in range(20):
2196 size = random.randint(1, 1024 * 1024)
2197 fp = fs.allocate_tmp(size)
2198 purged.append(
2199 filestore.File(fp.name, size, path.getmtime(fp.name))
2200 )
2201 for i in range(10):
2202 fp = fs.allocate_tmp()
2203 purged.append(
2204 filestore.File(fp.name, 0, path.getmtime(fp.name))
2205 )
2206 purged.sort(key=lambda f: f.name)
2207 self.assertEqual(fs.purge_tmp(), purged)
2208 self.assertEqual(sorted(os.listdir(t)), names)
2209 for f in nope:
2210 self.assertTrue(path.isfile(f))
2211
2140 def test_allocate_tmp(self):2212 def test_allocate_tmp(self):
2141 tmp = TempDir()2213 tmp = TempDir()
2142 t = tmp.join('.dmedia', 'tmp')2214 t = tmp.join('.dmedia', 'tmp')

Subscribers

People subscribed via source and target branches