From e3083d3ad541432e2420869c7ed56fb9aff7a453 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 26 Apr 2014 16:56:52 -0400 Subject: make operations on closed dumb databases raise a consistent exception (closes #19385) Patch by Claudiu Popa. --- Lib/dbm/dumb.py | 12 ++++++++++++ Lib/test/test_dbm_dumb.py | 29 +++++++++++++++++++++++++---- Misc/NEWS | 3 +++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index ba6a20d..15dad9c 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -118,9 +118,14 @@ class _Database(collections.MutableMapping): sync = _commit + def _verify_open(self): + if self._index is None: + raise error('DBM object has already been closed') + def __getitem__(self, key): if isinstance(key, str): key = key.encode('utf-8') + self._verify_open() pos, siz = self._index[key] # may raise KeyError f = _io.open(self._datfile, 'rb') f.seek(pos) @@ -173,6 +178,7 @@ class _Database(collections.MutableMapping): val = val.encode('utf-8') elif not isinstance(val, (bytes, bytearray)): raise TypeError("values must be bytes or strings") + self._verify_open() if key not in self._index: self._addkey(key, self._addval(val)) else: @@ -200,6 +206,7 @@ class _Database(collections.MutableMapping): def __delitem__(self, key): if isinstance(key, str): key = key.encode('utf-8') + self._verify_open() # 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 @@ -209,21 +216,26 @@ class _Database(collections.MutableMapping): self._commit() def keys(self): + self._verify_open() return list(self._index.keys()) def items(self): + self._verify_open() return [(key, self[key]) for key in self._index.keys()] def __contains__(self, key): if isinstance(key, str): key = key.encode('utf-8') + self._verify_open() return key in self._index def iterkeys(self): + self._verify_open() return iter(self._index.keys()) __iter__ = iterkeys def __len__(self): + self._verify_open() return len(self._index) def close(self): diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 7601e64..29f48a3 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -3,10 +3,12 @@ """ import io +import operator import os import unittest import dbm.dumb as dumbdbm from test import support +from functools import partial _fname = support.TESTFN @@ -190,12 +192,31 @@ class DumbDBMTestCase(unittest.TestCase): with dumbdbm.open(_fname, 'r') as db: self.assertEqual(list(db.keys()), [b"dumbdbm context manager"]) - # This currently just raises AttributeError rather than a specific - # exception like the GNU or NDBM based implementations. See - # http://bugs.python.org/issue19385 for details. - with self.assertRaises(Exception): + with self.assertRaises(dumbdbm.error): db.keys() + def test_check_closed(self): + f = dumbdbm.open(_fname, 'c') + f.close() + + for meth in (partial(operator.delitem, f), + partial(operator.setitem, f, 'b'), + partial(operator.getitem, f), + partial(operator.contains, f)): + with self.assertRaises(dumbdbm.error) as cm: + meth('test') + self.assertEqual(str(cm.exception), + "DBM object has already been closed") + + for meth in (operator.methodcaller('keys'), + operator.methodcaller('iterkeys'), + operator.methodcaller('items'), + len): + with self.assertRaises(dumbdbm.error) as cm: + meth(f) + self.assertEqual(str(cm.exception), + "DBM object has already been closed") + def tearDown(self): _delete_files() diff --git a/Misc/NEWS b/Misc/NEWS index 5036af0..e3c8810 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -39,6 +39,9 @@ Core and Builtins Library ------- +- Issue #19385: Make operations on a closed dbm.dumb database always raise the + same exception. + - Issue #21207: Detect when the os.urandom cached fd has been closed or replaced, and open it anew. -- cgit v0.12