From 7a930a7fadabaecefab8a1679107b610a5c15bfe Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 21:24:10 +0900 Subject: [PATCH 1/7] gh-107122: Add clear method to dbm.ndbm module --- Doc/library/dbm.rst | 5 ++++ Lib/test/test_dbm.py | 15 ++++++++++ ...-07-23-21-16-54.gh-issue-107122.VNuNcq.rst | 1 + Modules/_dbmmodule.c | 29 +++++++++++++++++++ Modules/clinic/_dbmmodule.c.h | 24 ++++++++++++++- 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index d226fa9f962cb4..f4ea92198f07c9 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -320,6 +320,11 @@ to locate the appropriate header file to simplify building this module. Close the ``ndbm`` database. + .. method:: ndbm.clear() + Remove all items from the ``ndbm`` database. + + .. versionadded:: 3.13 + :mod:`dbm.dumb` --- Portable DBM implementation ----------------------------------------------- diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index f21eebc6530c76..e3924d8ec8b5c1 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -155,6 +155,21 @@ def test_keys(self): self.assertNotIn(b'xxx', d) self.assertRaises(KeyError, lambda: d[b'xxx']) + def test_clear(self): + with dbm.open(_fname, 'c') as d: + self.assertEqual(d.keys(), []) + a = [(b'a', b'b'), (b'12345678910', b'019237410982340912840198242')] + for k, v in a: + d[k] = v + for k, _ in a: + self.assertIn(k, d) + self.assertEqual(len(d), len(a)) + + d.clear() + self.assertEqual(len(d), 0) + for k, _ in a: + self.assertNotIn(k, d) + def setUp(self): self.addCleanup(setattr, dbm, '_defaultmod', dbm._defaultmod) dbm._defaultmod = self.module diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst new file mode 100644 index 00000000000000..8beaf51316762c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst @@ -0,0 +1 @@ +Add :meth:`clear` to :mod:`dbm.ndbm`. Patch By Dong-hee Na. diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 5be444d53e8da3..3cb2e55eb98c3f 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -414,6 +414,34 @@ _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, return default_value; } +/*[clinic input] +_dbm.dbm.clear + cls: defining_class + / +Remove all items from the database. + +[clinic start generated code]*/ + +static PyObject * +_dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) +/*[clinic end generated code: output=8d126b9e1d01a434 input=43aa6ca1acb7f5f5]*/ +{ + _dbm_state *state = PyType_GetModuleState(cls); + assert(state != NULL); + check_dbmobject_open(self, state->dbm_error); + for (datum key = dbm_firstkey(self->di_dbm); key.dptr; key = dbm_nextkey(self->di_dbm)) { + if (dbm_delete(self->di_dbm, key) < 0) { + dbm_clearerr(self->di_dbm); + PyErr_SetString(state->dbm_error, "cannot delete item from database"); + return NULL; + } + } + if (self->di_size > 0) { + self->di_size = 0; + } + Py_RETURN_NONE; +} + static PyObject * dbm__enter__(PyObject *self, PyObject *args) { @@ -431,6 +459,7 @@ static PyMethodDef dbm_methods[] = { _DBM_DBM_KEYS_METHODDEF _DBM_DBM_GET_METHODDEF _DBM_DBM_SETDEFAULT_METHODDEF + _DBM_DBM_CLEAR_METHODDEF {"__enter__", dbm__enter__, METH_NOARGS, NULL}, {"__exit__", dbm__exit__, METH_VARARGS, NULL}, {NULL, NULL} /* sentinel */ diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 172dc4b9d5793e..98aac07423c8ab 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -138,6 +138,28 @@ _dbm_dbm_setdefault(dbmobject *self, PyTypeObject *cls, PyObject *const *args, P return return_value; } +PyDoc_STRVAR(_dbm_dbm_clear__doc__, +"clear($self, /)\n" +"--\n" +"\n" +"Remove all items from the database."); + +#define _DBM_DBM_CLEAR_METHODDEF \ + {"clear", _PyCFunction_CAST(_dbm_dbm_clear), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _dbm_dbm_clear__doc__}, + +static PyObject * +_dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls); + +static PyObject * +_dbm_dbm_clear(dbmobject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + if (nargs) { + PyErr_SetString(PyExc_TypeError, "clear() takes no arguments"); + return NULL; + } + return _dbm_dbm_clear_impl(self, cls); +} + PyDoc_STRVAR(dbmopen__doc__, "open($module, filename, flags=\'r\', mode=0o666, /)\n" "--\n" @@ -200,4 +222,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=28dcf736654137c2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b3053c67ecfcc29c input=a9049054013a1b77]*/ From d0f930c0cf3bd682a8f52b0fbd7e8408cb91993c Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 21:36:02 +0900 Subject: [PATCH 2/7] Address code review --- Modules/_dbmmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 3cb2e55eb98c3f..a8bec6c142f05e 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -435,9 +435,9 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) PyErr_SetString(state->dbm_error, "cannot delete item from database"); return NULL; } - } - if (self->di_size > 0) { - self->di_size = 0; + if (self->di_size > 0) { + self->di_size--; + } } Py_RETURN_NONE; } From 7a7e8266c53e9b55185d3e1424dcb9bb379dd081 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 21:40:34 +0900 Subject: [PATCH 3/7] Be more safer --- Modules/_dbmmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index a8bec6c142f05e..c01a73be5769f2 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -429,7 +429,8 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) _dbm_state *state = PyType_GetModuleState(cls); assert(state != NULL); check_dbmobject_open(self, state->dbm_error); - for (datum key = dbm_firstkey(self->di_dbm); key.dptr; key = dbm_nextkey(self->di_dbm)) { + datum key; + while (key = dbm_firstkey(self->di_dbm), key.dptr) { if (dbm_delete(self->di_dbm, key) < 0) { dbm_clearerr(self->di_dbm); PyErr_SetString(state->dbm_error, "cannot delete item from database"); From 3affdb7feadcd9962e462191029a166eca661bcd Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 21:46:44 +0900 Subject: [PATCH 4/7] Address code review --- Modules/_dbmmodule.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index c01a73be5769f2..6ea4b7f1e9b43a 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -430,7 +430,11 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) assert(state != NULL); check_dbmobject_open(self, state->dbm_error); datum key; - while (key = dbm_firstkey(self->di_dbm), key.dptr) { + while (1) { + key = dbm_firstkey(self->di_dbm); + if (key.dptr == NULL) { + break; + } if (dbm_delete(self->di_dbm, key) < 0) { dbm_clearerr(self->di_dbm); PyErr_SetString(state->dbm_error, "cannot delete item from database"); From 2be5402780b48476219c6024455393c11dd96ca8 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 22:01:36 +0900 Subject: [PATCH 5/7] Update test_dbm_ndbm --- Lib/test/test_dbm_ndbm.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index 8f37e3cc624e2e..e0f31c9a9a337d 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -147,6 +147,19 @@ def test_bool_on_closed_db_raises(self): db['a'] = 'b' self.assertRaises(dbm.ndbm.error, bool, db) + def test_clear(self): + kvs = [('foo', 'bar'), ('1234', '5678')] + with dbm.ndbm.open(self.filename, 'c') as db: + for k, v in kvs: + db[k] = v + self.assertIn(k, db) + self.assertEqual(len(db), len(kvs)) + + db.clear() + for k, v in kvs: + self.assertNotIn(k, db) + self.assertEqual(len(db), 0) + if __name__ == '__main__': unittest.main() From 1d1444078097c0af3c3cbe4d86b625e6316a3793 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 22:11:24 +0900 Subject: [PATCH 6/7] Update docs --- Doc/library/dbm.rst | 1 + .../2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index f4ea92198f07c9..766847b971b645 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -321,6 +321,7 @@ to locate the appropriate header file to simplify building this module. Close the ``ndbm`` database. .. method:: ndbm.clear() + Remove all items from the ``ndbm`` database. .. versionadded:: 3.13 diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst index 8beaf51316762c..d63aeb54ae1454 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-07-23-21-16-54.gh-issue-107122.VNuNcq.rst @@ -1 +1 @@ -Add :meth:`clear` to :mod:`dbm.ndbm`. Patch By Dong-hee Na. +Add :meth:`dbm.ndbm.clear` to :mod:`dbm.ndbm`. Patch By Dong-hee Na. From 5eb2b1305376d5204391d14f0e4ae213aa8fdaae Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Sun, 23 Jul 2023 22:19:15 +0900 Subject: [PATCH 7/7] Simply invalidate cache --- Modules/_dbmmodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 6ea4b7f1e9b43a..bd807698927e86 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -430,6 +430,8 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) assert(state != NULL); check_dbmobject_open(self, state->dbm_error); datum key; + // Invalidate cache + self->di_size = -1; while (1) { key = dbm_firstkey(self->di_dbm); if (key.dptr == NULL) { @@ -440,9 +442,6 @@ _dbm_dbm_clear_impl(dbmobject *self, PyTypeObject *cls) PyErr_SetString(state->dbm_error, "cannot delete item from database"); return NULL; } - if (self->di_size > 0) { - self->di_size--; - } } Py_RETURN_NONE; }