bpo-33106: change dbm key deletion error for readonly file from KeyError to dbm.error (#6295)
This commit is contained in:
parent
5a718e918d
commit
4fb0b8bc25
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
------------------------
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Deleting a key from a read-only dbm database raises module specfic error
|
||||
instead of KeyError.
|
|
@ -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);
|
||||
/* 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;
|
||||
|
|
|
@ -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) {
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue