]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #28847: dbm.dumb now supports reading read-only files and no longer
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 7 Dec 2016 08:56:39 +0000 (10:56 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Wed, 7 Dec 2016 08:56:39 +0000 (10:56 +0200)
writes the index file when it is not changed.

Lib/dbm/dumb.py
Lib/test/support/__init__.py
Lib/test/test_dbm_dumb.py
Misc/NEWS

index 7777a7cae67d0f17805fe06aa36af05525d41ced..e6d650515a74808be9af80738a96d10a18c805fe 100644 (file)
@@ -47,6 +47,7 @@ class _Database(collections.MutableMapping):
 
     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 @@ class _Database(collections.MutableMapping):
         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 @@ class _Database(collections.MutableMapping):
         # 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 @@ class _Database(collections.MutableMapping):
         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 @@ class _Database(collections.MutableMapping):
         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
index 97828afa5a39c24629a347764668a42026c83b2c..698f503c59b7e9e40b4fe24dca23f4b3c8cf5d65 100644 (file)
@@ -359,9 +359,9 @@ if sys.platform.startswith("win"):
                     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 @@ def temp_dir(path=None, quiet=False):
         yield path
     finally:
         if dir_created:
-            shutil.rmtree(path)
+            rmtree(path)
 
 @contextlib.contextmanager
 def change_cwd(path, quiet=False):
index ff63c88c0bc5c81342c961568b1ed82f315140ff..235ab11b614d6feb3001fe2adacdcb2c80a78e03 100644 (file)
@@ -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 @@ class DumbDBMTestCase(unittest.TestCase):
                     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()
 
index 1575c9ab53d94b67d9787a360dc01833db7b2cee..07897d7bb33bfc00a149a575d682d76030b4576c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -121,6 +121,9 @@ Core and Builtins
 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.