Merge lp:~jderose/filestore/py3.3 into lp:filestore

Proposed by Jason Gerard DeRose
Status: Merged
Merged at revision: 302
Proposed branch: lp:~jderose/filestore/py3.3
Merge into: lp:filestore
Diff against target: 454 lines (+113/-96)
11 files modified
.bzrignore (+2/-2)
_filestore.c (+0/-22)
debian/control (+5/-3)
debian/filestore-crashdb.conf (+0/-4)
debian/python3-filestore.install (+0/-1)
debian/source_filestore.py (+4/-3)
debian/watch (+2/-0)
filestore/__init__.py (+11/-22)
filestore/tests/__init__.py (+11/-13)
filestore/tests/run.py (+70/-0)
setup.py (+8/-26)
To merge this branch: bzr merge lp:~jderose/filestore/py3.3
Reviewer Review Type Date Requested Status
David Jordan Approve
Review via email: mp+149047@code.launchpad.net

Description of the change

For details, see this bug:

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

Changes include:

* Dropped back-ported posix_fadvise() C extension as now we can just use os.posix_fadvise() from the standard lib

* FileStore.allocate_tmp() and FileStore.allocate_partial() now take advantage of new 'xb' mode for open()

* FileStore.random_tmp_path() now uses dbase32.random_id()

* setup.py now enforces python3 >= 3.3

* Updated debian/control to require python3 >= 3.3, build-depend and depend on python3-dbase32

* Removed filestore-crashdb.conf, and updated source_filestore.py to provide the same info as we only need Apport 2.5+ compatibility now

* Added missing debian/watch file

* Added filestore.tests.run entry point for running the tests against the installed package like this:

  python3 -m filestore.tests.run

To post a comment you must log in.
Revision history for this message
David Jordan (dmj726) wrote :

Looks good, approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-08-13 06:47:34 +0000
3+++ .bzrignore 2013-02-18 13:31:28 +0000
4@@ -1,2 +1,2 @@
5-__pycache__
6-doc/_build/*
7+build/
8+doc/_build/
9
10=== modified file '_filestore.c'
11--- _filestore.c 2013-02-16 01:28:23 +0000
12+++ _filestore.c 2013-02-18 13:31:28 +0000
13@@ -22,26 +22,6 @@
14 return 1;
15 }
16
17-static PyObject *
18-filestore_posix_fadvise(PyObject *self, PyObject *args)
19-{
20- off_t len, offset;
21- int res, fd, advice;
22-
23- if (!PyArg_ParseTuple(args, "iO&O&i:posix_fadvise",
24- &fd, _parse_off_t, &offset, _parse_off_t, &len, &advice))
25- return NULL;
26-
27- Py_BEGIN_ALLOW_THREADS
28- res = posix_fadvise(fd, offset, len, advice);
29- Py_END_ALLOW_THREADS
30- if (res != 0) {
31- errno = res;
32- return filestore_error();
33- }
34- Py_RETURN_NONE;
35-}
36-
37
38 /* Our custom fallocate */
39 static PyObject *
40@@ -121,8 +101,6 @@
41 static struct PyMethodDef filestore_functions[] = {
42 {"fallocate", filestore_fallocate, METH_VARARGS,
43 "fallocate(fd, mode, offset, len)"},
44- {"posix_fadvise", filestore_posix_fadvise, METH_VARARGS,
45- "posix_fadvise(fd, offset, len, advice)"},
46 {"fastread", filestore_fastread, METH_VARARGS,
47 "fastread(fd, leaf_index, leaf_size)"},
48 {NULL, NULL, 0, NULL}
49
50=== modified file 'debian/control'
51--- debian/control 2012-11-01 05:37:37 +0000
52+++ debian/control 2013-02-18 13:31:28 +0000
53@@ -3,9 +3,10 @@
54 Priority: optional
55 Maintainer: Jason Gerard DeRose <jderose@novacut.com>
56 Build-Depends: debhelper (>= 9),
57- python3-all-dev (>= 3.2.3),
58+ python3-all-dev (>= 3.3),
59+ python3-sphinx,
60 python3-skein (>= 0.7.1),
61- python3-sphinx (>= 1.1),
62+ python3-dbase32 (>= 0.3),
63 Standards-Version: 3.9.3
64 X-Python3-Version: >= 3.2
65 Homepage: https://launchpad.net/filestore
66@@ -13,7 +14,8 @@
67 Package: python3-filestore
68 Architecture: any
69 Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends},
70- python3-skein (>= 0.7.1)
71+ python3-skein (>= 0.7.1),
72+ python3-dbase32 (>= 0.3),
73 Description: Dmedia hashing protocol and file layout
74 This is the heart of Dmedia. It has been split out of Dmedia to make it easier
75 to review, and in hopes that other apps might use the hashing protocol and
76
77=== removed file 'debian/filestore-crashdb.conf'
78--- debian/filestore-crashdb.conf 2012-09-07 00:29:56 +0000
79+++ debian/filestore-crashdb.conf 1970-01-01 00:00:00 +0000
80@@ -1,4 +0,0 @@
81-filestore = {
82- 'impl': 'launchpad',
83- 'project': 'filestore',
84-}
85
86=== modified file 'debian/python3-filestore.install'
87--- debian/python3-filestore.install 2012-09-07 00:29:56 +0000
88+++ debian/python3-filestore.install 2013-02-18 13:31:28 +0000
89@@ -1,2 +1,1 @@
90-debian/filestore-crashdb.conf etc/apport/crashdb.conf.d/
91 debian/source_filestore.py usr/share/apport/package-hooks/
92
93=== modified file 'debian/source_filestore.py'
94--- debian/source_filestore.py 2012-09-07 00:29:56 +0000
95+++ debian/source_filestore.py 2013-02-18 13:31:28 +0000
96@@ -1,9 +1,10 @@
97-'''apport package hook for filestore.
98+"""
99+Apport package hook for filestore (requires Apport 2.5 or newer).
100
101 (c) 2012 Novacut Inc
102 Author: Jason Gerard DeRose <jderose@novacut.com>
103-'''
104+"""
105
106 def add_info(report):
107- report['CrashDB'] = 'filestore'
108+ report['CrashDB'] = "{'impl': 'launchpad', 'project': 'filestore'}"
109
110
111=== added file 'debian/watch'
112--- debian/watch 1970-01-01 00:00:00 +0000
113+++ debian/watch 2013-02-18 13:31:28 +0000
114@@ -0,0 +1,2 @@
115+version=3
116+https://launchpad.net/filestore/+download https://launchpad.net/filestore/.*/filestore-(.+).tar.gz
117
118=== removed directory 'doc/_build'
119=== modified file 'filestore/__init__.py'
120--- filestore/__init__.py 2013-02-16 04:18:54 +0000
121+++ filestore/__init__.py 2013-02-18 13:31:28 +0000
122@@ -82,17 +82,16 @@
123 from collections import namedtuple
124 import logging
125
126+from dbase32 import random_id
127+
128 from .protocols import VERSION0, VERSION1
129
130 try:
131- from _filestore import fallocate, posix_fadvise, fastread
132+ from _filestore import fallocate, fastread
133 except ImportError:
134 def fallocate(fd, mode, offset, length):
135 os.ftruncate(fd, length)
136
137- def posix_fadvise(fd, offset, length, advice):
138- pass
139-
140 def fastread(fd, index, size):
141 return os.read(fd, size)
142
143@@ -146,10 +145,6 @@
144 https://launchpad.net/dmedia
145 """
146
147-POSIX_FADV_SEQUENTIAL = 2
148-POSIX_FADV_WILLNEED = 3
149-POSIX_FADV_DONTNEED = 4
150-
151
152 ##############################
153 # The Dmedia Hashing Protocol:
154@@ -602,7 +597,7 @@
155 fd = src_fp.fileno()
156 st = os.fstat(fd)
157 if st.st_size > LEAF_SIZE:
158- posix_fadvise(fd, 0, st.st_size, POSIX_FADV_SEQUENTIAL)
159+ os.posix_fadvise(fd, 0, st.st_size, os.POSIX_FADV_SEQUENTIAL)
160 index = 0
161 while True:
162 data = fastread(fd, index, LEAF_SIZE)
163@@ -668,7 +663,7 @@
164 log.info('Opening %r', file)
165 src_fp = open(file.name, 'rb')
166 fd = src_fp.fileno()
167- posix_fadvise(fd, 0, file.size, POSIX_FADV_SEQUENTIAL)
168+ os.posix_fadvise(fd, 0, file.size, os.POSIX_FADV_SEQUENTIAL)
169 index = 0
170 while True:
171 data = fastread(fd, index, LEAF_SIZE)
172@@ -1044,8 +1039,7 @@
173 If this file will be opened by another application, you should try and
174 do the same, using the O_CREAT and O_EXCL flags with OPEN(2).
175 """
176- name = b32encode(os.urandom(15)).decode('utf-8')
177- return path.join(self.tmp, name)
178+ return path.join(self.tmp, random_id())
179
180 def exists(self, _id):
181 """
182@@ -1258,10 +1252,8 @@
183 raise ValueError(
184 'Need size >= 1; got size={}'.format(size)
185 )
186- rand = b32encode(os.urandom(15)).decode('utf-8')
187 filename = self.random_tmp_path()
188- # FIXME: open in mode 'xb' once we support only Python 3.3 and newer:
189- tmp_fp = open(filename, 'wb')
190+ tmp_fp = open(filename, 'xb')
191 fd = tmp_fp.fileno()
192 if size is not None:
193 try:
194@@ -1289,14 +1281,11 @@
195 filename = self.partial_path(_id)
196 try:
197 tmp_fp = open(filename, 'rb+')
198- reopened = True
199+ fd = tmp_fp.fileno()
200+ os.ftruncate(fd, size)
201 except IOError:
202- tmp_fp = open(filename, 'wb')
203- reopened = False
204- fd = tmp_fp.fileno()
205- if reopened:
206- os.ftruncate(fd, size)
207- else:
208+ tmp_fp = open(filename, 'xb')
209+ fd = tmp_fp.fileno()
210 try:
211 fallocate(fd, 0, 0, size)
212 except OSError:
213
214=== modified file 'filestore/tests/__init__.py'
215--- filestore/tests/__init__.py 2013-02-16 02:35:34 +0000
216+++ filestore/tests/__init__.py 2013-02-18 13:31:28 +0000
217@@ -35,6 +35,7 @@
218 from random import SystemRandom
219
220 from skein import skein512
221+from dbase32 import isdb32
222
223 from filestore import protocols
224 from filestore.protocols import PROTOCOLS, VERSION0, VERSION1
225@@ -1811,9 +1812,7 @@
226 self.assertEqual(path.dirname(filename), fs.tmp)
227 name = path.basename(filename)
228 self.assertEqual(len(name), 24)
229- self.assertEqual(name,
230- b32encode(b32decode(name.encode('utf-8'))).decode('utf-8')
231- )
232+ self.assertTrue(isdb32(name))
233 self.assertEqual(path.abspath(filename), filename)
234
235 def test_exists(self):
236@@ -2299,7 +2298,7 @@
237 # Test with size=None
238 fp1 = fs.allocate_tmp()
239 self.assertIsInstance(fp1, io.BufferedWriter)
240- self.assertEqual(fp1.mode, 'wb')
241+ self.assertEqual(fp1.mode, 'xb')
242 self.assertEqual(os.fstat(fp1.fileno()).st_size, 0)
243 self.assertEqual(stat.S_IMODE(os.fstat(fp1.fileno()).st_mode), 0o666)
244 self.assertTrue(fp1.name.startswith(t + os.sep))
245@@ -2307,7 +2306,7 @@
246 # Test with size=2311
247 fp2 = fs.allocate_tmp(2311)
248 self.assertIsInstance(fp2, io.BufferedWriter)
249- self.assertEqual(fp2.mode, 'wb')
250+ self.assertEqual(fp2.mode, 'xb')
251 self.assertEqual(os.fstat(fp2.fileno()).st_size, 2311)
252 self.assertEqual(stat.S_IMODE(os.fstat(fp2.fileno()).st_mode), 0o666)
253 self.assertTrue(fp2.name.startswith(t + os.sep))
254@@ -2332,7 +2331,7 @@
255 # But make sure that size=1 works:
256 fp3 = fs.allocate_tmp(1)
257 self.assertIsInstance(fp3, io.BufferedWriter)
258- self.assertEqual(fp3.mode, 'wb')
259+ self.assertEqual(fp3.mode, 'xb')
260 self.assertEqual(os.fstat(fp3.fileno()).st_size, 1)
261 self.assertEqual(stat.S_IMODE(os.fstat(fp3.fileno()).st_mode), 0o666)
262 self.assertTrue(fp3.name.startswith(t + os.sep))
263@@ -2349,7 +2348,7 @@
264 fp = fs.allocate_partial(2311, _id)
265 self.assertEqual(fp.name, filename)
266 self.assertIsInstance(fp, io.BufferedWriter)
267- self.assertEqual(fp.mode, 'wb')
268+ self.assertEqual(fp.mode, 'xb')
269 self.assertEqual(os.fstat(fp.fileno()).st_size, 2311)
270 self.assertEqual(stat.S_IMODE(os.fstat(fp.fileno()).st_mode), 0o666)
271
272@@ -2361,23 +2360,22 @@
273 fp = fs.allocate_partial(2311, _id)
274 self.assertEqual(fp.name, filename)
275 self.assertIsInstance(fp, io.BufferedRandom)
276- self.assertIn(fp.mode, ('r+b', 'rb+'))
277+ self.assertEqual(fp.mode, 'rb+')
278 self.assertEqual(os.fstat(fp.fileno()).st_size, 2311)
279 self.assertEqual(stat.S_IMODE(os.fstat(fp.fileno()).st_mode), 0o666)
280 fp.close()
281
282- open(filename, 'wb').write(b'a' * 200) # Truncate to smaller than size
283+ open(filename, 'wb+').write(b'a' * 200) # Truncate to smaller than size
284
285 # Test with pre-existing file smaller than size:
286 self.assertEqual(os.stat(filename).st_size, 200)
287 fp = fs.allocate_partial(2311, _id)
288 self.assertEqual(fp.name, filename)
289 self.assertIsInstance(fp, io.BufferedRandom)
290- self.assertIn(fp.mode, ('r+b', 'rb+'))
291+ self.assertEqual(fp.mode, 'rb+')
292 self.assertEqual(os.fstat(fp.fileno()).st_size, 2311)
293 self.assertEqual(stat.S_IMODE(os.fstat(fp.fileno()).st_mode), 0o666)
294
295-
296 _id = random_id() # We'll use a new ID for below
297 filename = tmp.join('.dmedia', 'partial', _id)
298
299@@ -2401,14 +2399,14 @@
300 fp = fs.allocate_partial(1, _id)
301 self.assertEqual(fp.name, filename)
302 self.assertIsInstance(fp, io.BufferedWriter)
303- self.assertEqual(fp.mode, 'wb')
304+ self.assertEqual(fp.mode, 'xb')
305 self.assertEqual(os.fstat(fp.fileno()).st_size, 1)
306 self.assertEqual(stat.S_IMODE(os.fstat(fp.fileno()).st_mode), 0o666)
307 fp.close()
308 fp = fs.allocate_partial(1, _id)
309 self.assertEqual(fp.name, filename)
310 self.assertIsInstance(fp, io.BufferedRandom)
311- self.assertIn(fp.mode, ('r+b', 'rb+'))
312+ self.assertEqual(fp.mode, 'rb+')
313 self.assertEqual(os.fstat(fp.fileno()).st_size, 1)
314 self.assertEqual(stat.S_IMODE(os.fstat(fp.fileno()).st_mode), 0o666)
315
316
317=== added file 'filestore/tests/run.py'
318--- filestore/tests/run.py 1970-01-01 00:00:00 +0000
319+++ filestore/tests/run.py 2013-02-18 13:31:28 +0000
320@@ -0,0 +1,70 @@
321+# filestore: dmedia hashing protocol and file layout
322+# Copyright (C) 2012 Novacut Inc
323+#
324+# This file is part of `filestore`.
325+#
326+# `filestore` is free software: you can redistribute it and/or modify it under
327+# the terms of the GNU Affero General Public License as published by the Free
328+# Software Foundation, either version 3 of the License, or (at your option) any
329+# later version.
330+#
331+# `filestore` is distributed in the hope that it will be useful, but WITHOUT ANY
332+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
333+# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
334+# details.
335+#
336+# You should have received a copy of the GNU Affero General Public License along
337+# with `filestore`. If not, see <http://www.gnu.org/licenses/>.
338+#
339+# Authors:
340+# Jason Gerard DeRose <jderose@novacut.com>
341+
342+"""
343+Run `filestore` unit tests.
344+"""
345+import sys
346+from os import path
347+from unittest import TestLoader, TextTestRunner
348+from doctest import DocTestSuite
349+
350+import filestore
351+
352+
353+pynames = (
354+ 'filestore',
355+ 'filestore.protocols',
356+ 'filestore.misc',
357+ 'filestore.tests',
358+ 'filestore.tests.test_protocols',
359+ 'filestore.tests.test_misc'
360+)
361+
362+
363+def run_tests():
364+ # Add unit-tests:
365+ loader = TestLoader()
366+ suite = loader.loadTestsFromNames(pynames)
367+
368+ # Add doc-tests:
369+ for name in pynames:
370+ suite.addTest(DocTestSuite(name))
371+
372+ # Run the tests:
373+ runner = TextTestRunner(verbosity=2)
374+ result = runner.run(suite)
375+ success = result.wasSuccessful()
376+ print(
377+ 'filestore: {!r}'.format(path.abspath(filestore.__file__)),
378+ file=sys.stderr
379+ )
380+ if success:
381+ print('Tests passed.', file=sys.stderr)
382+ else:
383+ print('Tests FAILED!', file=sys.stderr)
384+ return success
385+
386+
387+if __name__ == '__main__':
388+ if not run_tests():
389+ raise SystemExit('2')
390+
391
392=== modified file 'setup.py'
393--- setup.py 2012-12-30 11:29:17 +0000
394+++ setup.py 2013-02-18 13:31:28 +0000
395@@ -26,15 +26,14 @@
396 """
397
398 import sys
399-if sys.version_info < (3, 2):
400- sys.exit('filestore requires Python 3.2 or newer')
401+if sys.version_info < (3, 3):
402+ sys.exit('filestore requires Python 3.3 or newer')
403
404 from distutils.core import setup, Extension
405 from distutils.cmd import Command
406-from unittest import TestLoader, TextTestRunner
407-from doctest import DocTestSuite
408
409 import filestore
410+from filestore.tests.run import run_tests
411
412
413 class Test(Command):
414@@ -49,27 +48,7 @@
415 pass
416
417 def run(self):
418- pynames = [
419- 'filestore',
420- 'filestore.protocols',
421- 'filestore.misc',
422- 'filestore.tests',
423- 'filestore.tests.test_protocols',
424- 'filestore.tests.test_misc'
425- ]
426-
427- # Add unit-tests:
428- loader = TestLoader()
429- suite = loader.loadTestsFromNames(pynames)
430-
431- # Add doc-tests:
432- for name in pynames:
433- suite.addTest(DocTestSuite(name))
434-
435- # Run the tests:
436- runner = TextTestRunner(verbosity=2)
437- result = runner.run(suite)
438- if not result.wasSuccessful():
439+ if not run_tests():
440 raise SystemExit(2)
441
442
443@@ -84,7 +63,10 @@
444 author='Jason Gerard DeRose',
445 author_email='jderose@novacut.com',
446 license='AGPLv3+',
447- packages=['filestore', 'filestore.tests'],
448+ packages=[
449+ 'filestore',
450+ 'filestore.tests'
451+ ],
452 package_data={'filestore': ['data/test-vectors.json']},
453 scripts=['dmediasum'],
454 cmdclass={'test': Test},

Subscribers

People subscribed via source and target branches