Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Lib/dbm/dumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,21 @@ def __enter__(self):
def __exit__(self, *args):
self.close()

def setdefault(self, key, default):
if isinstance(key, str):
key = key.encode('utf-8')
try:
if key in self._index:
return self[key]
else:
self[key] = default
return default
except TypeError:
if self._index is None:
raise error('DBM object has already been closed') from None
else:
raise

def reorganize(self):
if self._readonly:
raise error('The database is opened for reading only')
Expand Down
6 changes: 6 additions & 0 deletions Lib/dbm/sqlite3.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ def __exit__(self, *args):
def reorganize(self):
self._execute(REORGANIZE)

def setdefault(self, key, default):
if key in self:
return self[key]
else:
self[key] = default
return default
Comment on lines +134 to +138
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be optimized to two _execute() if inline __getitem__() and __setitem__(). Could it be done with a single _execute()? cc @erlend-aasland


def open(filename, /, flag="r", mode=0o666):
"""Open a dbm.sqlite3 database and return the dbm object.
Expand Down
23 changes: 22 additions & 1 deletion Lib/test/test_dbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ def test_anydbm_modification(self):
# setdefault() works as in the dict interface
self.assertEqual(f.setdefault(b'xxx', b'foo'), b'foo')
self.assertEqual(f[b'xxx'], b'foo')
self.assertEqual(f.setdefault(b'g', b'bar'), b"indented")
self.assertEqual(f[b'g'], b"indented")
self.assertNotEqual(f[b'g'], b'bar')
with self.assertRaises(TypeError):
f.setdefault(b'key_without_default')
with self.assertRaises(TypeError):
f.setdefault(b'g', b'value', b'extra')
if self.module.__name__ in ('dbm.dumb', 'dbm.gnu', 'dbm.ndbm'):
with self.assertRaises(TypeError):
f.setdefault(b'new_key', 123)
f.setdefault(b'g', 123)
if self.module.__name__ == "dbm.sqlite3":
f.setdefault(b'new_key', 123)
self.assertEqual(f[b'new_key'], b'123')
f.setdefault(b'g', 123)
self.assertNotEqual(f[b'g'], b'123')
f.close()

def test_anydbm_read(self):
Expand All @@ -105,6 +121,11 @@ def test_anydbm_read(self):
self.assertIsNone(f.get(b'xxx'))
with self.assertRaises(KeyError):
f[b'xxx']
with self.assertRaises(dbm.error):
f.setdefault(b'readonly_key', b'readonly_value')
f.setdefault(b'a', b'foo')
self.assertEqual(f[b'a'], b'Python:')
self.assertNotEqual(f[b'a'], b'foo')
f.close()

def test_anydbm_keys(self):
Expand All @@ -124,7 +145,7 @@ def test_empty_value(self):
self.assertIn(b'empty', f)
self.assertEqual(f[b'empty'], b'')
self.assertEqual(f.get(b'empty'), b'')
self.assertEqual(f.setdefault(b'empty'), b'')
self.assertEqual(f.setdefault(b'empty', b'x'), b'')
f.close()

def test_anydbm_access(self):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_dbm_ndbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_empty_value(self):
self.assertIn(b'empty', self.d)
self.assertEqual(self.d[b'empty'], b'')
self.assertEqual(self.d.get(b'empty'), b'')
self.assertEqual(self.d.setdefault(b'empty'), b'')
self.assertEqual(self.d.setdefault(b'empty', b'x'), b'')
self.d.close()

def test_modes(self):
Expand Down
23 changes: 8 additions & 15 deletions Modules/_dbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
_dbm.dbm.setdefault
cls: defining_class
key: str(accept={str, robuffer}, zeroes=True)
default: object(c_default="NULL") = b''
default: object
/

Return the value for key if present, otherwise default.
Expand All @@ -436,7 +436,7 @@ If key is not in the database, it is inserted with default as the value.
static PyObject *
_dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
Py_ssize_t key_length, PyObject *default_value)
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=c01510ef7571e13b]*/
/*[clinic end generated code: output=9c2f6ea6d0fb576c input=12069156f2ddea7e]*/
{
datum dbm_key, val;
Py_ssize_t tmp_size;
Expand All @@ -449,20 +449,13 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
if (val.dptr != NULL) {
return PyBytes_FromStringAndSize(val.dptr, val.dsize);
}
if (default_value == NULL) {
default_value = Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
val.dptr = NULL;
val.dsize = 0;
}
else {
if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
PyErr_SetString(PyExc_TypeError,
"dbm mappings have bytes or string elements only");
return NULL;
}
val.dsize = tmp_size;
Py_INCREF(default_value);
if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
PyErr_SetString(PyExc_TypeError,
"dbm mappings have bytes or string elements only");
return NULL;
}
val.dsize = tmp_size;
Py_INCREF(default_value);
if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) {
dbm_clearerr(self->di_dbm);
PyErr_SetString(state->dbm_error, "cannot add item to database");
Expand Down
4 changes: 2 additions & 2 deletions Modules/_gdbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w)
_gdbm.gdbm.setdefault

key: object
default: object = None
default: object
/

Get value for key, or set it to default and return default if not present.
Expand All @@ -384,7 +384,7 @@ Get value for key, or set it to default and return default if not present.
static PyObject *
_gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
PyObject *default_value)
/*[clinic end generated code: output=f3246e880509f142 input=f4008b358165bbb8]*/
/*[clinic end generated code: output=f3246e880509f142 input=6e9519905bc18910]*/
{
PyObject *res;

Expand Down
8 changes: 4 additions & 4 deletions Modules/clinic/_dbmmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 4 additions & 8 deletions Modules/clinic/_gdbmmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading