changeset: 105501:0a74bc7ba462 branch: 3.5 parent: 105478:104fdc42bcd1 user: Serhiy Storchaka date: Wed Dec 07 10:56:39 2016 +0200 files: Lib/dbm/dumb.py Lib/test/support/__init__.py Lib/test/test_dbm_dumb.py Misc/NEWS description: Issue #28847: dbm.dumb now supports reading read-only files and no longer writes the index file when it is not changed. diff -r 104fdc42bcd1 -r 0a74bc7ba462 Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py Tue Dec 06 13:43:46 2016 +0200 +++ b/Lib/dbm/dumb.py Wed Dec 07 10:56:39 2016 +0200 @@ -47,6 +47,7 @@ def __init__(self, filebasename, mode, flag='c'): self._mode = mode + self._readonly = (flag == 'r') # The directory file is a text file. Each line looks like # "%r, (%d, %d)\n" % (key, pos, siz) @@ -91,8 +92,9 @@ try: f = _io.open(self._dirfile, 'r', encoding="Latin-1") except OSError: - pass + self._modified = not self._readonly else: + self._modified = False with f: for line in f: line = line.rstrip() @@ -107,7 +109,7 @@ # CAUTION: It's vital that _commit() succeed, and _commit() can # be called from __del__(). Therefore we must never reference a # global in this routine. - if self._index is None: + if self._index is None or not self._modified: return # nothing to do try: @@ -187,6 +189,7 @@ elif not isinstance(val, (bytes, bytearray)): raise TypeError("values must be bytes or strings") self._verify_open() + self._modified = True if key not in self._index: self._addkey(key, self._addval(val)) else: @@ -215,6 +218,7 @@ if isinstance(key, str): key = key.encode('utf-8') self._verify_open() + self._modified = True # The blocks used by the associated value are lost. del self._index[key] # XXX It's unclear why we do a _commit() here (the code always diff -r 104fdc42bcd1 -r 0a74bc7ba462 Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py Tue Dec 06 13:43:46 2016 +0200 +++ b/Lib/test/support/__init__.py Wed Dec 07 10:56:39 2016 +0200 @@ -359,9 +359,9 @@ mode = 0 if stat.S_ISDIR(mode): _waitfor(_rmtree_inner, fullname, waitall=True) - _force_run(path, os.rmdir, fullname) + _force_run(fullname, os.rmdir, fullname) else: - _force_run(path, os.unlink, fullname) + _force_run(fullname, os.unlink, fullname) _waitfor(_rmtree_inner, path, waitall=True) _waitfor(lambda p: _force_run(p, os.rmdir, p), path) else: @@ -933,7 +933,7 @@ yield path finally: if dir_created: - shutil.rmtree(path) + rmtree(path) @contextlib.contextmanager def change_cwd(path, quiet=False): diff -r 104fdc42bcd1 -r 0a74bc7ba462 Lib/test/test_dbm_dumb.py --- a/Lib/test/test_dbm_dumb.py Tue Dec 06 13:43:46 2016 +0200 +++ b/Lib/test/test_dbm_dumb.py Wed Dec 07 10:56:39 2016 +0200 @@ -5,6 +5,7 @@ import io import operator import os +import stat import unittest import dbm.dumb as dumbdbm from test import support @@ -234,6 +235,21 @@ pass self.assertEqual(stdout.getvalue(), '') + @unittest.skipUnless(hasattr(os, 'chmod'), 'test needs os.chmod()') + def test_readonly_files(self): + with support.temp_dir() as dir: + fname = os.path.join(dir, 'db') + with dumbdbm.open(fname, 'n') as f: + self.assertEqual(list(f.keys()), []) + for key in self._dict: + f[key] = self._dict[key] + os.chmod(fname + ".dir", stat.S_IRUSR) + os.chmod(fname + ".dat", stat.S_IRUSR) + os.chmod(dir, stat.S_IRUSR|stat.S_IXUSR) + with dumbdbm.open(fname, 'r') as f: + self.assertEqual(sorted(f.keys()), sorted(self._dict)) + f.close() # don't write + def tearDown(self): _delete_files() diff -r 104fdc42bcd1 -r 0a74bc7ba462 Misc/NEWS --- a/Misc/NEWS Tue Dec 06 13:43:46 2016 +0200 +++ b/Misc/NEWS Wed Dec 07 10:56:39 2016 +0200 @@ -121,6 +121,9 @@ Library ------- +- Issue #28847: dbm.dumb now supports reading read-only files and no longer + writes the index file when it is not changed. + - Issue #25659: In ctypes, prevent a crash calling the from_buffer() and from_buffer_copy() methods on abstract classes like Array.