Skip to content

Commit 71ac0eb

Browse files
authored
Improve dbm/gdbm error messages
Improve dbm/gdbm error messages
2 parents 8400b23 + 8f548c7 commit 71ac0eb

6 files changed

Lines changed: 57 additions & 21 deletions

File tree

Lib/shelve.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ def _validate_serialized_value(serialized_value, original_value):
114114
invalid_type = "None"
115115
else:
116116
invalid_type = type(serialized_value).__name__
117-
msg = (f"Serializer returned {invalid_type} for value "
118-
f"{original_value!r} But database values must be "
117+
msg = (f"Serializer returned {serialized_value!r} for value "
118+
f"{original_value!r}, but database values must be "
119119
f"bytes or str, not {invalid_type}")
120120
raise ShelveError(msg)
121121

Lib/test/test_dbm_gnu.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,25 @@ def test_localized_error(self):
217217
create_empty_file(os.path.join(d, 'test'))
218218
self.assertRaises(gdbm.error, gdbm.open, filename, 'r')
219219

220+
def test_type_errors(self):
221+
self.g = gdbm.open(filename, 'c')
222+
with self.assertRaisesRegex(
223+
TypeError, "^a bytes-like object is required, not 'int'$",
224+
):
225+
self.g[123]
226+
with self.assertRaisesRegex(
227+
TypeError, "^gdbm key must be bytes or str, not 'int'$",
228+
):
229+
123 in self.g
230+
with self.assertRaisesRegex(
231+
TypeError, "^gdbm key must be bytes or str, not 'NoneType'$",
232+
):
233+
self.g[None] = 123
234+
with self.assertRaisesRegex(
235+
TypeError, "^gdbm value must be bytes or str, not 'int'$",
236+
):
237+
self.g['foo'] = 123
238+
220239

221240
if __name__ == '__main__':
222241
unittest.main()

Lib/test/test_dbm_ndbm.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,25 @@ def test_clear(self):
160160
self.assertNotIn(k, db)
161161
self.assertEqual(len(db), 0)
162162

163+
def test_type_errors(self):
164+
with dbm.ndbm.open(self.filename, 'c') as db:
165+
with self.assertRaisesRegex(
166+
TypeError, "^a bytes-like object is required, not 'int'$",
167+
):
168+
db[123]
169+
with self.assertRaisesRegex(
170+
TypeError, "^dbm key must be bytes or str, not 'int'$",
171+
):
172+
123 in db
173+
with self.assertRaisesRegex(
174+
TypeError, "^dbm key must be bytes or str, not 'NoneType'$",
175+
):
176+
db[None] = 123
177+
with self.assertRaisesRegex(
178+
TypeError, "^dbm value must be bytes or str, not 'int'$",
179+
):
180+
db['foo'] = 123
181+
163182

164183
if __name__ == '__main__':
165184
unittest.main()

Lib/test/test_shelve.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,16 +468,16 @@ def deserializer(data):
468468
deserializer=deserializer) as s:
469469
with self.assertRaises(shelve.ShelveError) as cm:
470470
s["key"] = "value"
471-
self.assertEqual("Serializer returned None for value 'value' "
472-
"But database values must be bytes or str, not None",
471+
self.assertEqual("Serializer returned None for value 'value', but "
472+
"database values must be bytes or str, not None",
473473
f"{cm.exception}")
474474

475475
with shelve.open(self.fname, serializer=int_serializer,
476476
deserializer=deserializer,) as s:
477477
with self.assertRaises(shelve.ShelveError) as cm:
478478
s["key"] = "value"
479-
self.assertEqual("Serializer returned int for value 'value' "
480-
"But database values must be bytes or str, not int",
479+
self.assertEqual("Serializer returned 3 for value 'value', but "
480+
"database values must be bytes or str, not int",
481481
f"{cm.exception}")
482482

483483
def test_shelve_type_compatibility(self):

Modules/_dbmmodule.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ dbm_ass_sub_lock_held(PyObject *self, PyObject *v, PyObject *w)
232232

233233
if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
234234
PyErr_Format(PyExc_TypeError,
235-
"dbm key returned %.100s for value %R But database keys must be bytes or str, not %.100s",
236-
Py_TYPE(v)->tp_name, v, Py_TYPE(v)->tp_name);
235+
"dbm key must be bytes or str, not '%T'",
236+
v);
237237
return -1;
238238
}
239239
_dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
@@ -260,8 +260,8 @@ dbm_ass_sub_lock_held(PyObject *self, PyObject *v, PyObject *w)
260260
} else {
261261
if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
262262
PyErr_Format(PyExc_TypeError,
263-
"dbm value returned %.100s for value %R But database values must be bytes or str, not %.100s",
264-
Py_TYPE(w)->tp_name, w, Py_TYPE(w)->tp_name);
263+
"dbm value must be bytes or str, not '%T'",
264+
w);
265265
return -1;
266266
}
267267
drec.dsize = tmp_size;
@@ -371,8 +371,7 @@ dbm_contains_lock_held(PyObject *self, PyObject *arg)
371371
}
372372
else if (!PyBytes_Check(arg)) {
373373
PyErr_Format(PyExc_TypeError,
374-
"dbm key must be bytes or string, not %.100s",
375-
Py_TYPE(arg)->tp_name);
374+
"dbm key must be bytes or str, not '%T'", arg);
376375
return -1;
377376
}
378377
else {

Modules/_gdbmmodule.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,14 @@ gdbm_bool(PyObject *op)
237237
// This function is needed to support PY_SSIZE_T_CLEAN.
238238
// Return 1 on success, same to PyArg_Parse().
239239
static int
240-
parse_datum(PyObject *o, datum *d, const char *failmsg)
240+
parse_datum(PyObject *o, datum *d, const char *item_name)
241241
{
242242
Py_ssize_t size;
243243
if (!PyArg_Parse(o, "s#", &d->dptr, &size)) {
244-
if (failmsg != NULL) {
245-
PyErr_Format(PyExc_TypeError, failmsg, Py_TYPE(o)->tp_name, o, Py_TYPE(o)->tp_name);
244+
if (item_name) {
245+
PyErr_Format(PyExc_TypeError,
246+
"gdbm %s must be bytes or str, not '%T'",
247+
item_name, o);
246248
}
247249
return 0;
248250
}
@@ -318,12 +320,10 @@ static int
318320
gdbm_ass_sub_lock_held(PyObject *op, PyObject *v, PyObject *w)
319321
{
320322
datum krec, drec;
321-
const char *key_failmsg = "dbm key returned %.100s for value %R But database keys must be bytes or str, not %.100s";
322-
const char *value_failmsg = "dbm value returned %.100s for value %R But database keys must be bytes or str, not %.100s";
323323
gdbmobject *dp = _gdbmobject_CAST(op);
324324
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
325325

326-
if (!parse_datum(v, &krec, key_failmsg)) {
326+
if (!parse_datum(v, &krec, "key")) {
327327
return -1;
328328
}
329329
if (dp->di_dbm == NULL) {
@@ -344,7 +344,7 @@ gdbm_ass_sub_lock_held(PyObject *op, PyObject *v, PyObject *w)
344344
}
345345
}
346346
else {
347-
if (!parse_datum(w, &drec, value_failmsg)) {
347+
if (!parse_datum(w, &drec, "value")) {
348348
return -1;
349349
}
350350
errno = 0;
@@ -492,8 +492,7 @@ gdbm_contains_lock_held(PyObject *self, PyObject *arg)
492492
}
493493
else if (!PyBytes_Check(arg)) {
494494
PyErr_Format(PyExc_TypeError,
495-
"gdbm key must be bytes or string, not %.100s",
496-
Py_TYPE(arg)->tp_name);
495+
"gdbm key must be bytes or str, not '%T'", arg);
497496
return -1;
498497
}
499498
else {

0 commit comments

Comments
 (0)