.. 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.
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
------------------------
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)):
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()
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
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):
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):
--- /dev/null
+Deleting a key from a read-only dbm database raises module specfic error
+instead of KeyError.
typedef struct {
PyObject_HEAD
+ int flags;
int di_size; /* -1 means recompute */
DBM *di_dbm;
} dbmobject;
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);
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;
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;
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;