Skip to content

Commit 3fdf79d

Browse files
committed
Make setdefault methods require explicit default argument for dbm backends
1 parent ba8e20b commit 3fdf79d

File tree

8 files changed

+45
-34
lines changed

8 files changed

+45
-34
lines changed

Lib/dbm/dumb.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,21 @@ def __enter__(self):
287287
def __exit__(self, *args):
288288
self.close()
289289

290+
def setdefault(self, key, default):
291+
if isinstance(key, str):
292+
key = key.encode('utf-8')
293+
try:
294+
if key in self._index:
295+
return self[key]
296+
else:
297+
self[key] = default
298+
return default
299+
except TypeError:
300+
if self._index is None:
301+
raise error('DBM object has already been closed') from None
302+
else:
303+
raise
304+
290305
def reorganize(self):
291306
if self._readonly:
292307
raise error('The database is opened for reading only')

Lib/dbm/sqlite3.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ def __exit__(self, *args):
126126
def reorganize(self):
127127
self._execute(REORGANIZE)
128128

129+
def setdefault(self, key, default):
130+
if key in self:
131+
return self[key]
132+
else:
133+
self[key] = default
134+
return default
129135

130136
def open(filename, /, flag="r", mode=0o666):
131137
"""Open a dbm.sqlite3 database and return the dbm object.

Lib/test/test_dbm.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ def test_empty_value(self):
124124
self.assertIn(b'empty', f)
125125
self.assertEqual(f[b'empty'], b'')
126126
self.assertEqual(f.get(b'empty'), b'')
127-
self.assertEqual(f.setdefault(b'empty'), b'')
127+
# setdefault raises TypeError when missing default argument
128+
with self.assertRaises(TypeError):
129+
f.setdefault(b'empty')
128130
f.close()
129131

130132
def test_anydbm_access(self):

Lib/test/test_dbm_ndbm.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def test_empty_value(self):
4848
self.assertIn(b'empty', self.d)
4949
self.assertEqual(self.d[b'empty'], b'')
5050
self.assertEqual(self.d.get(b'empty'), b'')
51-
self.assertEqual(self.d.setdefault(b'empty'), b'')
51+
# setdefault raises TypeError when missing default argument
52+
with self.assertRaises(TypeError):
53+
self.d.setdefault(b'empty')
5254
self.d.close()
5355

5456
def test_modes(self):

Modules/_dbmmodule.c

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
431431
_dbm.dbm.setdefault
432432
cls: defining_class
433433
key: str(accept={str, robuffer}, zeroes=True)
434-
default: object(c_default="NULL") = b''
434+
default: object
435435
/
436436
437437
Return the value for key if present, otherwise default.
@@ -442,7 +442,7 @@ If key is not in the database, it is inserted with default as the value.
442442
static PyObject *
443443
_dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
444444
Py_ssize_t key_length, PyObject *default_value)
445-
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/
445+
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=12069156f2ddea7e]*/
446446
{
447447
datum dbm_key, val;
448448
Py_ssize_t tmp_size;
@@ -455,23 +455,13 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
455455
if (val.dptr != NULL) {
456456
return PyBytes_FromStringAndSize(val.dptr, val.dsize);
457457
}
458-
if (default_value == NULL) {
459-
default_value = PyBytes_FromStringAndSize(NULL, 0);
460-
if (default_value == NULL) {
461-
return NULL;
462-
}
463-
val.dptr = NULL;
464-
val.dsize = 0;
465-
}
466-
else {
467-
if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
468-
PyErr_SetString(PyExc_TypeError,
469-
"dbm mappings have bytes or string elements only");
470-
return NULL;
471-
}
472-
val.dsize = tmp_size;
473-
Py_INCREF(default_value);
458+
if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
459+
PyErr_SetString(PyExc_TypeError,
460+
"dbm mappings have bytes or string elements only");
461+
return NULL;
474462
}
463+
val.dsize = tmp_size;
464+
Py_INCREF(default_value);
475465
if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) {
476466
dbm_clearerr(self->di_dbm);
477467
PyErr_SetString(state->dbm_error, "cannot add item to database");

Modules/_gdbmmodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w)
382382
_gdbm.gdbm.setdefault
383383
384384
key: object
385-
default: object = None
385+
default: object
386386
/
387387
388388
Get value for key, or set it to default and return default if not present.
@@ -391,7 +391,7 @@ Get value for key, or set it to default and return default if not present.
391391
static PyObject *
392392
_gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
393393
PyObject *default_value)
394-
/*[clinic end generated code: output=f3246e880509f142 input=f4008b358165bbb8]*/
394+
/*[clinic end generated code: output=f3246e880509f142 input=6e9519905bc18910]*/
395395
{
396396
PyObject *res;
397397

Modules/clinic/_dbmmodule.c.h

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/clinic/_gdbmmodule.c.h

Lines changed: 4 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)