bpo-32749: Make dbm.dumb databases more cosistent with other dbm databases. (#5497)
This commit is contained in:
parent
c309bcfb9f
commit
6c85efa5a6
|
@ -339,9 +339,23 @@ The module defines the following:
|
|||
dumbdbm database is created, files with :file:`.dat` and :file:`.dir` extensions
|
||||
are created.
|
||||
|
||||
The optional *flag* argument supports only the semantics of ``'c'``
|
||||
and ``'n'`` values. Other values will default to database being always
|
||||
opened for update, and will be created if it does not exist.
|
||||
The optional *flag* argument can be:
|
||||
|
||||
+---------+-------------------------------------------+
|
||||
| Value | Meaning |
|
||||
+=========+===========================================+
|
||||
| ``'r'`` | Open existing database for reading only |
|
||||
| | (default) |
|
||||
+---------+-------------------------------------------+
|
||||
| ``'w'`` | Open existing database for reading and |
|
||||
| | writing |
|
||||
+---------+-------------------------------------------+
|
||||
| ``'c'`` | Open database for reading and writing, |
|
||||
| | creating it if it doesn't exist |
|
||||
+---------+-------------------------------------------+
|
||||
| ``'n'`` | Always create a new, empty database, open |
|
||||
| | for reading and writing |
|
||||
+---------+-------------------------------------------+
|
||||
|
||||
The optional *mode* argument is the Unix mode of the file, used only when the
|
||||
database has to be created. It defaults to octal ``0o666`` (and will be modified
|
||||
|
@ -351,9 +365,10 @@ The module defines the following:
|
|||
:func:`.open` always creates a new database when the flag has the value
|
||||
``'n'``.
|
||||
|
||||
.. deprecated-removed:: 3.6 3.8
|
||||
Creating database in ``'r'`` and ``'w'`` modes. Modifying database in
|
||||
``'r'`` mode.
|
||||
.. versionchanged:: 3.8
|
||||
A database opened with flags ``'r'`` is now read-only. Opening with
|
||||
flags ``'r'`` and ``'w'`` no longer creates a database if it does not
|
||||
exist.
|
||||
|
||||
In addition to the methods provided by the
|
||||
:class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects
|
||||
|
|
|
@ -130,3 +130,8 @@ Changes in the Python API
|
|||
arguments for changing the selection was deprecated in Python 3.6. Use
|
||||
specialized methods like :meth:`~tkinter.ttk.Treeview.selection_set` for
|
||||
changing the selection. (Contributed by Serhiy Storchaka in :issue:`31508`.)
|
||||
|
||||
* A :mod:`dbm.dumb` database opened with flags ``'r'`` is now read-only.
|
||||
:func:`dbm.dumb.open` with flags ``'r'`` and ``'w'`` no longer creates
|
||||
a database if it does not exist.
|
||||
(Contributed by Serhiy Storchaka in :issue:`32749`.)
|
||||
|
|
|
@ -82,10 +82,7 @@ class _Database(collections.abc.MutableMapping):
|
|||
f = _io.open(self._datfile, 'r', encoding="Latin-1")
|
||||
except OSError:
|
||||
if flag not in ('c', 'n'):
|
||||
import warnings
|
||||
warnings.warn("The database file is missing, the "
|
||||
"semantics of the 'c' flag will be used.",
|
||||
DeprecationWarning, stacklevel=4)
|
||||
raise
|
||||
with _io.open(self._datfile, 'w', encoding="Latin-1") as f:
|
||||
self._chmod(self._datfile)
|
||||
else:
|
||||
|
@ -93,18 +90,15 @@ class _Database(collections.abc.MutableMapping):
|
|||
|
||||
# Read directory file into the in-memory index dict.
|
||||
def _update(self, flag):
|
||||
self._modified = False
|
||||
self._index = {}
|
||||
try:
|
||||
f = _io.open(self._dirfile, 'r', encoding="Latin-1")
|
||||
except OSError:
|
||||
self._modified = not self._readonly
|
||||
if flag not in ('c', 'n'):
|
||||
import warnings
|
||||
warnings.warn("The index file is missing, the "
|
||||
"semantics of the 'c' flag will be used.",
|
||||
DeprecationWarning, stacklevel=4)
|
||||
raise
|
||||
self._modified = True
|
||||
else:
|
||||
self._modified = False
|
||||
with f:
|
||||
for line in f:
|
||||
line = line.rstrip()
|
||||
|
@ -191,9 +185,7 @@ class _Database(collections.abc.MutableMapping):
|
|||
|
||||
def __setitem__(self, key, val):
|
||||
if self._readonly:
|
||||
import warnings
|
||||
warnings.warn('The database is opened for reading only',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
raise ValueError('The database is opened for reading only')
|
||||
if isinstance(key, str):
|
||||
key = key.encode('utf-8')
|
||||
elif not isinstance(key, (bytes, bytearray)):
|
||||
|
@ -230,9 +222,7 @@ class _Database(collections.abc.MutableMapping):
|
|||
|
||||
def __delitem__(self, key):
|
||||
if self._readonly:
|
||||
import warnings
|
||||
warnings.warn('The database is opened for reading only',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
raise ValueError('The database is opened for reading only')
|
||||
if isinstance(key, str):
|
||||
key = key.encode('utf-8')
|
||||
self._verify_open()
|
||||
|
@ -323,7 +313,5 @@ def open(file, flag='c', mode=0o666):
|
|||
# Turn off any bits that are set in the umask
|
||||
mode = mode & (~um)
|
||||
if flag not in ('r', 'w', 'c', 'n'):
|
||||
import warnings
|
||||
warnings.warn("Flag must be one of 'r', 'w', 'c', or 'n'",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
raise ValueError("Flag must be one of 'r', 'w', 'c', or 'n'")
|
||||
return _Database(file, mode, flag=flag)
|
||||
|
|
|
@ -79,10 +79,10 @@ class DumbDBMTestCase(unittest.TestCase):
|
|||
self.init_db()
|
||||
f = dumbdbm.open(_fname, 'r')
|
||||
self.read_helper(f)
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
'The database is opened for reading only'):
|
||||
f[b'g'] = b'x'
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
'The database is opened for reading only'):
|
||||
del f[b'a']
|
||||
f.close()
|
||||
|
@ -241,37 +241,30 @@ class DumbDBMTestCase(unittest.TestCase):
|
|||
pass
|
||||
self.assertEqual(stdout.getvalue(), '')
|
||||
|
||||
def test_warn_on_ignored_flags(self):
|
||||
def test_missing_data(self):
|
||||
for value in ('r', 'w'):
|
||||
_delete_files()
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
"The database file is missing, the "
|
||||
"semantics of the 'c' flag will "
|
||||
"be used."):
|
||||
f = dumbdbm.open(_fname, value)
|
||||
f.close()
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
dumbdbm.open(_fname, value)
|
||||
self.assertFalse(os.path.exists(_fname + '.dir'))
|
||||
self.assertFalse(os.path.exists(_fname + '.bak'))
|
||||
|
||||
def test_missing_index(self):
|
||||
with dumbdbm.open(_fname, 'n') as f:
|
||||
pass
|
||||
os.unlink(_fname + '.dir')
|
||||
for value in ('r', 'w'):
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
"The index file is missing, the "
|
||||
"semantics of the 'c' flag will "
|
||||
"be used."):
|
||||
f = dumbdbm.open(_fname, value)
|
||||
f.close()
|
||||
self.assertEqual(os.path.exists(_fname + '.dir'), value == 'w')
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
dumbdbm.open(_fname, value)
|
||||
self.assertFalse(os.path.exists(_fname + '.dir'))
|
||||
self.assertFalse(os.path.exists(_fname + '.bak'))
|
||||
|
||||
def test_invalid_flag(self):
|
||||
for flag in ('x', 'rf', None):
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
"Flag must be one of "
|
||||
"'r', 'w', 'c', or 'n'"):
|
||||
f = dumbdbm.open(_fname, flag)
|
||||
f.close()
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"Flag must be one of "
|
||||
"'r', 'w', 'c', or 'n'"):
|
||||
dumbdbm.open(_fname, flag)
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'chmod'), 'test needs os.chmod()')
|
||||
def test_readonly_files(self):
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
A :mod:`dbm.dumb` database opened with flags 'r' is now read-only.
|
||||
:func:`dbm.dumb.open` with flags 'r' and 'w' no longer creates a database if
|
||||
it does not exist.
|
Loading…
Reference in New Issue