]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-33106: change dbm key deletion error for readonly file from KeyError to dbm.error...
authorXiang Zhang <angwerzx@126.com>
Wed, 12 Dec 2018 12:46:55 +0000 (20:46 +0800)
committerGitHub <noreply@github.com>
Wed, 12 Dec 2018 12:46:55 +0000 (20:46 +0800)
Doc/library/dbm.rst
Doc/whatsnew/3.8.rst
Lib/dbm/dumb.py
Lib/test/test_dbm_dumb.py
Lib/test/test_dbm_gnu.py
Lib/test/test_dbm_ndbm.py
Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst [new file with mode: 0644]
Modules/_dbmmodule.c
Modules/_gdbmmodule.c

index ab45cac830e24d783d21beaa2186a235f0d171a3..57ae547b833cc0eb2a76cdfc4ed3d1f5d5b24794 100644 (file)
@@ -73,6 +73,10 @@ available, as well as :meth:`get` and :meth:`setdefault`.
 .. versionchanged:: 3.2
    :meth:`get` and :meth:`setdefault` are now available in all database modules.
 
+.. versionchanged:: 3.8
+   Deleting a key from a read-only database raises database module specific error
+   instead of :exc:`KeyError`.
+
 Key and values are always stored as bytes. This means that when
 strings are used they are implicitly converted to the default encoding before
 being stored.
index 0d0d1ca70e6cd25eeac07e6574e362c60be9ba33..b38a1a767c68625a7fe22927124ca41471ed06f8 100644 (file)
@@ -483,6 +483,12 @@ Changes in the Python API
   external entities by default.
   (Contributed by Christian Heimes in :issue:`17239`.)
 
+* Deleting a key from a read-only :mod:`dbm` database (:mod:`dbm.dumb`,
+  :mod:`dbm.gnu` or :mod:`dbm.ndbm`) raises :attr:`error` (:exc:`dbm.dumb.error`,
+  :exc:`dbm.gnu.error` or :exc:`dbm.ndbm.error`) instead of :exc:`KeyError`.
+  (Contributed by Xiang Zhang in :issue:`33106`.)
+
+
 CPython bytecode changes
 ------------------------
 
index e5c17f5ae2eddbac85985a2693ef2d012caaeed3..6cef72a49acc9a31b00f4ecff58da4b5c5f065c0 100644 (file)
@@ -185,7 +185,7 @@ class _Database(collections.abc.MutableMapping):
 
     def __setitem__(self, key, val):
         if self._readonly:
-            raise ValueError('The database is opened for reading only')
+            raise error('The database is opened for reading only')
         if isinstance(key, str):
             key = key.encode('utf-8')
         elif not isinstance(key, (bytes, bytearray)):
@@ -222,7 +222,7 @@ class _Database(collections.abc.MutableMapping):
 
     def __delitem__(self, key):
         if self._readonly:
-            raise ValueError('The database is opened for reading only')
+            raise error('The database is opened for reading only')
         if isinstance(key, str):
             key = key.encode('utf-8')
         self._verify_open()
index 58b9d1746568957c57dbbd8c7d339b6440296067..e8e827e5f5bce0553353ac2b39c7107d57bf9abc 100644 (file)
@@ -82,10 +82,10 @@ class DumbDBMTestCase(unittest.TestCase):
         self.init_db()
         f = dumbdbm.open(_fname, 'r')
         self.read_helper(f)
-        with self.assertRaisesRegex(ValueError,
+        with self.assertRaisesRegex(dumbdbm.error,
                                    'The database is opened for reading only'):
             f[b'g'] = b'x'
-        with self.assertRaisesRegex(ValueError,
+        with self.assertRaisesRegex(dumbdbm.error,
                                    'The database is opened for reading only'):
             del f[b'a']
         # get() works as in the dict interface
index 16b7fe6fac1760e5bbd47d2ecbdc392354f3f600..f1c7d34085c5eee508219449ea70bc07f1a02870 100644 (file)
@@ -131,6 +131,17 @@ class TestGdbm(unittest.TestCase):
             self.assertEqual(db['Unicode key \U0001f40d'],
                              'Unicode value \U0001f40d'.encode())
 
+    def test_write_readonly_file(self):
+        with gdbm.open(filename, 'c') as db:
+            db[b'bytes key'] = b'bytes value'
+        with gdbm.open(filename, 'r') as db:
+            with self.assertRaises(gdbm.error):
+                del db[b'not exist key']
+            with self.assertRaises(gdbm.error):
+                del db[b'bytes key']
+            with self.assertRaises(gdbm.error):
+                db[b'not exist key'] = b'not exist value'
+
     @unittest.skipUnless(TESTFN_NONASCII,
                          'requires OS support of non-ASCII encodings')
     def test_nonascii_filename(self):
index bd411da652e66eb88de5cbd616f03a4944582e9f..7ac75c5fea1b5e761a42a5a0073a33f80fa83f33 100644 (file)
@@ -90,6 +90,17 @@ class DbmTestCase(unittest.TestCase):
             self.assertEqual(db['Unicode key \U0001f40d'],
                              'Unicode value \U0001f40d'.encode())
 
+    def test_write_readonly_file(self):
+        with dbm.ndbm.open(self.filename, 'c') as db:
+            db[b'bytes key'] = b'bytes value'
+        with dbm.ndbm.open(self.filename, 'r') as db:
+            with self.assertRaises(error):
+                del db[b'not exist key']
+            with self.assertRaises(error):
+                del db[b'bytes key']
+            with self.assertRaises(error):
+                db[b'not exist key'] = b'not exist value'
+
     @unittest.skipUnless(support.TESTFN_NONASCII,
                          'requires OS support of non-ASCII encodings')
     def test_nonascii_filename(self):
diff --git a/Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst b/Misc/NEWS.d/next/Library/2018-03-30-01-20-35.bpo-33106.zncfvW.rst
new file mode 100644 (file)
index 0000000..859c09c
--- /dev/null
@@ -0,0 +1,2 @@
+Deleting a key from a read-only dbm database raises module specfic error
+instead of KeyError.
index 081184a0b3224d522c216193fcd3312ccfbe6194..21784ae2ce4fd262ddef2276512ff521636abc27 100644 (file)
@@ -36,6 +36,7 @@ class _dbm.dbm "dbmobject *" "&Dbmtype"
 
 typedef struct {
     PyObject_HEAD
+    int flags;
     int di_size;        /* -1 means recompute */
     DBM *di_dbm;
 } dbmobject;
@@ -60,6 +61,7 @@ newdbmobject(const char *file, int flags, int mode)
     if (dp == NULL)
         return NULL;
     dp->di_size = -1;
+    dp->flags = flags;
     /* See issue #19296 */
     if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) {
         PyErr_SetFromErrnoWithFilename(DbmError, file);
@@ -143,13 +145,20 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
     if (w == NULL) {
         if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
             dbm_clearerr(dp->di_dbm);
-            PyErr_SetObject(PyExc_KeyError, v);
+            /* we might get a failure for reasons like file corrupted,
+               but we are not able to distinguish it */
+            if (dp->flags & O_RDWR) {
+                PyErr_SetObject(PyExc_KeyError, v);
+            }
+            else {
+                PyErr_SetString(DbmError, "cannot delete item from database");
+            }
             return -1;
         }
     } else {
         if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
             PyErr_SetString(PyExc_TypeError,
-                 "dbm mappings have byte or string elements only");
+                 "dbm mappings have bytes or string elements only");
             return -1;
         }
         drec.dsize = tmp_size;
@@ -335,7 +344,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, const char *key,
     else {
         if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
             PyErr_SetString(PyExc_TypeError,
-                "dbm mappings have byte string elements only");
+                "dbm mappings have bytes or string elements only");
             return NULL;
         }
         val.dsize = tmp_size;
index 46bb59ac952158d422231d5c0501e0ab9ad25aa3..cc94e60745c89df00f965e10b0fce660b28d1716 100644 (file)
@@ -186,14 +186,19 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
     dp->di_size = -1;
     if (w == NULL) {
         if (gdbm_delete(dp->di_dbm, krec) < 0) {
-            PyErr_SetObject(PyExc_KeyError, v);
+            if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
+                PyErr_SetObject(PyExc_KeyError, v);
+            }
+            else {
+                PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
+            }
             return -1;
         }
     }
     else {
         if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) {
             PyErr_SetString(PyExc_TypeError,
-                            "gdbm mappings have byte or string elements only");
+                            "gdbm mappings have bytes or string elements only");
             return -1;
         }
         errno = 0;