From 6c85efa5a66d7b254aa22a39d47f36c040d7a04e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 5 Feb 2018 22:47:31 +0200 Subject: bpo-32749: Make dbm.dumb databases more cosistent with other dbm databases. (#5497) --- Doc/library/dbm.rst | 27 +++++++++++++---- Doc/whatsnew/3.8.rst | 5 ++++ Lib/dbm/dumb.py | 26 +++++----------- Lib/test/test_dbm_dumb.py | 35 +++++++++------------- .../2018-02-02-17-21-24.bpo-32749.u5scIn.rst | 3 ++ 5 files changed, 50 insertions(+), 46 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-02-02-17-21-24.bpo-32749.u5scIn.rst diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index 32e80b2..1abc36c 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -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 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index c4063ad..60f54a0 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -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`.) diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index 5064668..e5c17f5 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -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) diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 73f2a32..21f29af 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -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): diff --git a/Misc/NEWS.d/next/Library/2018-02-02-17-21-24.bpo-32749.u5scIn.rst b/Misc/NEWS.d/next/Library/2018-02-02-17-21-24.bpo-32749.u5scIn.rst new file mode 100644 index 0000000..9665ff1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-02-02-17-21-24.bpo-32749.u5scIn.rst @@ -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. -- cgit v0.12