make operations on closed dumb databases raise a consistent exception (closes #19385)
Patch by Claudiu Popa.
This commit is contained in:
parent
d4992dccfb
commit
e3083d3ad5
|
@ -118,9 +118,14 @@ class _Database(collections.MutableMapping):
|
||||||
|
|
||||||
sync = _commit
|
sync = _commit
|
||||||
|
|
||||||
|
def _verify_open(self):
|
||||||
|
if self._index is None:
|
||||||
|
raise error('DBM object has already been closed')
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
key = key.encode('utf-8')
|
key = key.encode('utf-8')
|
||||||
|
self._verify_open()
|
||||||
pos, siz = self._index[key] # may raise KeyError
|
pos, siz = self._index[key] # may raise KeyError
|
||||||
f = _io.open(self._datfile, 'rb')
|
f = _io.open(self._datfile, 'rb')
|
||||||
f.seek(pos)
|
f.seek(pos)
|
||||||
|
@ -173,6 +178,7 @@ class _Database(collections.MutableMapping):
|
||||||
val = val.encode('utf-8')
|
val = val.encode('utf-8')
|
||||||
elif not isinstance(val, (bytes, bytearray)):
|
elif not isinstance(val, (bytes, bytearray)):
|
||||||
raise TypeError("values must be bytes or strings")
|
raise TypeError("values must be bytes or strings")
|
||||||
|
self._verify_open()
|
||||||
if key not in self._index:
|
if key not in self._index:
|
||||||
self._addkey(key, self._addval(val))
|
self._addkey(key, self._addval(val))
|
||||||
else:
|
else:
|
||||||
|
@ -200,6 +206,7 @@ class _Database(collections.MutableMapping):
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
key = key.encode('utf-8')
|
key = key.encode('utf-8')
|
||||||
|
self._verify_open()
|
||||||
# The blocks used by the associated value are lost.
|
# The blocks used by the associated value are lost.
|
||||||
del self._index[key]
|
del self._index[key]
|
||||||
# XXX It's unclear why we do a _commit() here (the code always
|
# XXX It's unclear why we do a _commit() here (the code always
|
||||||
|
@ -209,21 +216,26 @@ class _Database(collections.MutableMapping):
|
||||||
self._commit()
|
self._commit()
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
|
self._verify_open()
|
||||||
return list(self._index.keys())
|
return list(self._index.keys())
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
|
self._verify_open()
|
||||||
return [(key, self[key]) for key in self._index.keys()]
|
return [(key, self[key]) for key in self._index.keys()]
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
key = key.encode('utf-8')
|
key = key.encode('utf-8')
|
||||||
|
self._verify_open()
|
||||||
return key in self._index
|
return key in self._index
|
||||||
|
|
||||||
def iterkeys(self):
|
def iterkeys(self):
|
||||||
|
self._verify_open()
|
||||||
return iter(self._index.keys())
|
return iter(self._index.keys())
|
||||||
__iter__ = iterkeys
|
__iter__ = iterkeys
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
self._verify_open()
|
||||||
return len(self._index)
|
return len(self._index)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import operator
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
import dbm.dumb as dumbdbm
|
import dbm.dumb as dumbdbm
|
||||||
from test import support
|
from test import support
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
_fname = support.TESTFN
|
_fname = support.TESTFN
|
||||||
|
|
||||||
|
@ -190,12 +192,31 @@ class DumbDBMTestCase(unittest.TestCase):
|
||||||
with dumbdbm.open(_fname, 'r') as db:
|
with dumbdbm.open(_fname, 'r') as db:
|
||||||
self.assertEqual(list(db.keys()), [b"dumbdbm context manager"])
|
self.assertEqual(list(db.keys()), [b"dumbdbm context manager"])
|
||||||
|
|
||||||
# This currently just raises AttributeError rather than a specific
|
with self.assertRaises(dumbdbm.error):
|
||||||
# exception like the GNU or NDBM based implementations. See
|
|
||||||
# http://bugs.python.org/issue19385 for details.
|
|
||||||
with self.assertRaises(Exception):
|
|
||||||
db.keys()
|
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):
|
def tearDown(self):
|
||||||
_delete_files()
|
_delete_files()
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #21207: Detect when the os.urandom cached fd has been closed or
|
||||||
replaced, and open it anew.
|
replaced, and open it anew.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue