Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4fb0b8b

Browse files
authored
bpo-33106: change dbm key deletion error for readonly file from KeyError to dbm.error (#6295)
1 parent 5a718e9 commit 4fb0b8b

File tree

9 files changed

+57
-9
lines changed

9 files changed

+57
-9
lines changed

Doc/library/dbm.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ available, as well as :meth:`get` and :meth:`setdefault`.
7373
.. versionchanged:: 3.2
7474
:meth:`get` and :meth:`setdefault` are now available in all database modules.
7575

76+
.. versionchanged:: 3.8
77+
Deleting a key from a read-only database raises database module specific error
78+
instead of :exc:`KeyError`.
79+
7680
Key and values are always stored as bytes. This means that when
7781
strings are used they are implicitly converted to the default encoding before
7882
being stored.

Doc/whatsnew/3.8.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,12 @@ Changes in the Python API
483483
external entities by default.
484484
(Contributed by Christian Heimes in :issue:`17239`.)
485485

486+
* Deleting a key from a read-only :mod:`dbm` database (:mod:`dbm.dumb`,
487+
:mod:`dbm.gnu` or :mod:`dbm.ndbm`) raises :attr:`error` (:exc:`dbm.dumb.error`,
488+
:exc:`dbm.gnu.error` or :exc:`dbm.ndbm.error`) instead of :exc:`KeyError`.
489+
(Contributed by Xiang Zhang in :issue:`33106`.)
490+
491+
486492
CPython bytecode changes
487493
------------------------
488494

Lib/dbm/dumb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def _addkey(self, key, pos_and_siz_pair):
185185

186186
def __setitem__(self, key, val):
187187
if self._readonly:
188-
raise ValueError('The database is opened for reading only')
188+
raise error('The database is opened for reading only')
189189
if isinstance(key, str):
190190
key = key.encode('utf-8')
191191
elif not isinstance(key, (bytes, bytearray)):
@@ -222,7 +222,7 @@ def __setitem__(self, key, val):
222222

223223
def __delitem__(self, key):
224224
if self._readonly:
225-
raise ValueError('The database is opened for reading only')
225+
raise error('The database is opened for reading only')
226226
if isinstance(key, str):
227227
key = key.encode('utf-8')
228228
self._verify_open()

Lib/test/test_dbm_dumb.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ def test_dumbdbm_read(self):
8282
self.init_db()
8383
f = dumbdbm.open(_fname, 'r')
8484
self.read_helper(f)
85-
with self.assertRaisesRegex(ValueError,
85+
with self.assertRaisesRegex(dumbdbm.error,
8686
'The database is opened for reading only'):
8787
f[b'g'] = b'x'
88-
with self.assertRaisesRegex(ValueError,
88+
with self.assertRaisesRegex(dumbdbm.error,
8989
'The database is opened for reading only'):
9090
del f[b'a']
9191
# get() works as in the dict interface

Lib/test/test_dbm_gnu.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ def test_unicode(self):
131131
self.assertEqual(db['Unicode key \U0001f40d'],
132132
'Unicode value \U0001f40d'.encode())
133133

134+
def test_write_readonly_file(self):
135+
with gdbm.open(filename, 'c') as db:
136+
db[b'bytes key'] = b'bytes value'
137+
with gdbm.open(filename, 'r') as db:
138+
with self.assertRaises(gdbm.error):
139+
del db[b'not exist key']
140+
with self.assertRaises(gdbm.error):
141+
del db[b'bytes key']
142+
with self.assertRaises(gdbm.error):
143+
db[b'not exist key'] = b'not exist value'
144+
134145
@unittest.skipUnless(TESTFN_NONASCII,
135146
'requires OS support of non-ASCII encodings')
136147
def test_nonascii_filename(self):

Lib/test/test_dbm_ndbm.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ def test_unicode(self):
9090
self.assertEqual(db['Unicode key \U0001f40d'],
9191
'Unicode value \U0001f40d'.encode())
9292

93+
def test_write_readonly_file(self):
94+
with dbm.ndbm.open(self.filename, 'c') as db:
95+
db[b'bytes key'] = b'bytes value'
96+
with dbm.ndbm.open(self.filename, 'r') as db:
97+
with self.assertRaises(error):
98+
del db[b'not exist key']
99+
with self.assertRaises(error):
100+
del db[b'bytes key']
101+
with self.assertRaises(error):
102+
db[b'not exist key'] = b'not exist value'
103+
93104
@unittest.skipUnless(support.TESTFN_NONASCII,
94105
'requires OS support of non-ASCII encodings')
95106
def test_nonascii_filename(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Deleting a key from a read-only dbm database raises module specfic error
2+
instead of KeyError.

Modules/_dbmmodule.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class _dbm.dbm "dbmobject *" "&Dbmtype"
3636

3737
typedef struct {
3838
PyObject_HEAD
39+
int flags;
3940
int di_size; /* -1 means recompute */
4041
DBM *di_dbm;
4142
} dbmobject;
@@ -60,6 +61,7 @@ newdbmobject(const char *file, int flags, int mode)
6061
if (dp == NULL)
6162
return NULL;
6263
dp->di_size = -1;
64+
dp->flags = flags;
6365
/* See issue #19296 */
6466
if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) {
6567
PyErr_SetFromErrnoWithFilename(DbmError, file);
@@ -143,13 +145,20 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
143145
if (w == NULL) {
144146
if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
145147
dbm_clearerr(dp->di_dbm);
146-
PyErr_SetObject(PyExc_KeyError, v);
148+
/* we might get a failure for reasons like file corrupted,
149+
but we are not able to distinguish it */
150+
if (dp->flags & O_RDWR) {
151+
PyErr_SetObject(PyExc_KeyError, v);
152+
}
153+
else {
154+
PyErr_SetString(DbmError, "cannot delete item from database");
155+
}
147156
return -1;
148157
}
149158
} else {
150159
if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
151160
PyErr_SetString(PyExc_TypeError,
152-
"dbm mappings have byte or string elements only");
161+
"dbm mappings have bytes or string elements only");
153162
return -1;
154163
}
155164
drec.dsize = tmp_size;
@@ -335,7 +344,7 @@ _dbm_dbm_setdefault_impl(dbmobject *self, const char *key,
335344
else {
336345
if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
337346
PyErr_SetString(PyExc_TypeError,
338-
"dbm mappings have byte string elements only");
347+
"dbm mappings have bytes or string elements only");
339348
return NULL;
340349
}
341350
val.dsize = tmp_size;

Modules/_gdbmmodule.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,19 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
186186
dp->di_size = -1;
187187
if (w == NULL) {
188188
if (gdbm_delete(dp->di_dbm, krec) < 0) {
189-
PyErr_SetObject(PyExc_KeyError, v);
189+
if (gdbm_errno == GDBM_ITEM_NOT_FOUND) {
190+
PyErr_SetObject(PyExc_KeyError, v);
191+
}
192+
else {
193+
PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno));
194+
}
190195
return -1;
191196
}
192197
}
193198
else {
194199
if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) {
195200
PyErr_SetString(PyExc_TypeError,
196-
"gdbm mappings have byte or string elements only");
201+
"gdbm mappings have bytes or string elements only");
197202
return -1;
198203
}
199204
errno = 0;

0 commit comments

Comments
 (0)