From d9e833c70a22558236fc863250870837d4ad8b16 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Dec 2010 09:14:36 +0000 Subject: [PATCH] #6045: provide at least get() and setdefault() for all dbm modules. --- Doc/library/dbm.rst | 9 ++++++-- Lib/dbm/dumb.py | 4 ++-- Lib/test/test_dbm_gnu.py | 4 ++++ Misc/NEWS | 4 +++- Modules/_gdbmmodule.c | 47 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 6926ca6d109..67c0025fe9d 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -61,10 +61,15 @@ the Oracle Berkeley DB. modified by the prevailing umask). -The object returned by :func:`.open` supports most of the same functionality as +The object returned by :func:`.open` supports the same basic functionality as dictionaries; keys and their corresponding values can be stored, retrieved, and deleted, and the :keyword:`in` operator and the :meth:`keys` method are -available. Key and values are always stored as bytes. This means that when +available, as well as :meth:`get` and :meth:`setdefault`. + +.. versionchanged:: 3.2 + :meth:`get` and :meth:`setdefault` are now available in all database modules. + +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. diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index 4d804daa0f6..cfb912371c9 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -203,7 +203,7 @@ class _Database(collections.MutableMapping): # The blocks used by the associated value are lost. del self._index[key] # XXX It's unclear why we do a _commit() here (the code always - # XXX has, so I'm not changing it). _setitem__ doesn't try to + # XXX has, so I'm not changing it). __setitem__ doesn't try to # XXX keep the directory file in synch. Why should we? Or # XXX why shouldn't __setitem__? self._commit() @@ -232,7 +232,7 @@ class _Database(collections.MutableMapping): __del__ = close - def _chmod (self, file): + def _chmod(self, file): if hasattr(self._os, 'chmod'): self._os.chmod(file, self._mode) diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index dad56e6bd99..ce96ce465e7 100755 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -32,6 +32,10 @@ class TestGdbm(unittest.TestCase): key_set.remove(key) key = self.g.nextkey(key) self.assertRaises(KeyError, lambda: self.g['xxx']) + # get() and setdefault() work as in the dict interface + self.assertEqual(self.g.get(b'xxx', b'foo'), b'foo') + self.assertEqual(self.g.setdefault(b'xxx', b'foo'), b'foo') + self.assertEqual(self.g[b'xxx'], b'foo') def test_error_conditions(self): # Try to open a non-existent database. diff --git a/Misc/NEWS b/Misc/NEWS index f53a4868929..bc4cd254d46 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -37,7 +37,9 @@ Core and Builtins Library ------- -- Issue 10620: `python -m unittest` can accept file paths instead of module +- Issue #6045: dbm.gnu databases now support get() and setdefault() methods. + +- Issue #10620: `python -m unittest` can accept file paths instead of module names for running specific tests. - Issue #9424: Deprecate the `unittest.TestCase` methods `assertEquals`, diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index f15fefdf822..474561b235f 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -135,6 +135,28 @@ dbm_subscript(dbmobject *dp, register PyObject *key) return v; } +PyDoc_STRVAR(dbm_get__doc__, +"get(key[, default]) -> value\n\ +Get the value for key, or default if not present; if not given,\n\ +default is None."); + +static PyObject * +dbm_get(dbmobject *dp, PyObject *args) +{ + PyObject *v, *res; + PyObject *def = Py_None; + + if (!PyArg_UnpackTuple(args, "get", 1, 2, &v, &def)) + return NULL; + res = dbm_subscript(dp, v); + if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + Py_INCREF(def); + return def; + } + return res; +} + static int dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) { @@ -176,6 +198,29 @@ dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) return 0; } +PyDoc_STRVAR(dbm_setdefault__doc__, +"setdefault(key[, default]) -> value\n\ +Get value for key, or set it to default and return default if not present;\n\ +if not given, default is None."); + +static PyObject * +dbm_setdefault(dbmobject *dp, PyObject *args) +{ + PyObject *v, *res; + PyObject *def = Py_None; + + if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &v, &def)) + return NULL; + res = dbm_subscript(dp, v); + if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + if (dbm_ass_sub(dp, v, def) < 0) + return NULL; + return dbm_subscript(dp, v); + } + return res; +} + static PyMappingMethods dbm_as_mapping = { (lenfunc)dbm_length, /*mp_length*/ (binaryfunc)dbm_subscript, /*mp_subscript*/ @@ -378,6 +423,8 @@ static PyMethodDef dbm_methods[] = { {"nextkey", (PyCFunction)dbm_nextkey, METH_VARARGS, dbm_nextkey__doc__}, {"reorganize",(PyCFunction)dbm_reorganize,METH_NOARGS, dbm_reorganize__doc__}, {"sync", (PyCFunction)dbm_sync, METH_NOARGS, dbm_sync__doc__}, + {"get", (PyCFunction)dbm_get, METH_VARARGS, dbm_get__doc__}, + {"setdefault",(PyCFunction)dbm_setdefault,METH_VARARGS, dbm_setdefault__doc__}, {NULL, NULL} /* sentinel */ };