Issue #28847: dubmdbm no longer writes the index file in when it is not
changed and supports reading read-only files.
diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py
index 8ef7e79..ac73b89 100644
--- a/Lib/dumbdbm.py
+++ b/Lib/dumbdbm.py
@@ -45,8 +45,9 @@
_os = _os # for _commit()
_open = _open # for _commit()
- def __init__(self, filebasename, mode):
+ 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)
@@ -81,8 +82,9 @@
try:
f = _open(self._dirfile)
except IOError:
- pass
+ self._modified = not self._readonly
else:
+ self._modified = False
with f:
for line in f:
line = line.rstrip()
@@ -96,7 +98,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:
@@ -159,6 +161,7 @@
def __setitem__(self, key, val):
if not type(key) == type('') == type(val):
raise TypeError, "keys and values must be strings"
+ self._modified = True
if key not in self._index:
self._addkey(key, self._addval(val))
else:
@@ -184,6 +187,7 @@
# (so that _commit() never gets called).
def __delitem__(self, key):
+ 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
@@ -246,4 +250,4 @@
# Turn off any bits that are set in the umask
mode = mode & (~um)
- return _Database(file, mode)
+ return _Database(file, mode, flag)
diff --git a/Lib/test/test_dumbdbm.py b/Lib/test/test_dumbdbm.py
index 6520efd..024ddda 100644
--- a/Lib/test/test_dumbdbm.py
+++ b/Lib/test/test_dumbdbm.py
@@ -3,6 +3,7 @@
"""
import os
+import stat
import unittest
import dumbdbm
from test import test_support
@@ -168,6 +169,26 @@
dumbdbm.open(_fname).close()
self.assertEqual(stdout.getvalue(), '')
+ @unittest.skipUnless(hasattr(os, 'chmod'), 'test needs os.chmod()')
+ def test_readonly_files(self):
+ dir = _fname
+ os.mkdir(dir)
+ try:
+ fname = os.path.join(dir, 'db')
+ f = dumbdbm.open(fname, 'n')
+ self.assertEqual(list(f.keys()), [])
+ for key in self._dict:
+ f[key] = self._dict[key]
+ f.close()
+ os.chmod(fname + ".dir", stat.S_IRUSR)
+ os.chmod(fname + ".dat", stat.S_IRUSR)
+ os.chmod(dir, stat.S_IRUSR|stat.S_IXUSR)
+ f = dumbdbm.open(fname, 'r')
+ self.assertEqual(sorted(f.keys()), sorted(self._dict))
+ f.close() # don't write
+ finally:
+ test_support.rmtree(dir)
+
def tearDown(self):
_delete_files()