[Python-checkins] bpo-32749: Make dbm.dumb databases more cosistent with other dbm databases. (#5497)

Serhiy Storchaka webhook-mailer at python.org
Mon Feb 5 15:47:50 EST 2018


https://github.com/python/cpython/commit/6c85efa5a66d7b254aa22a39d47f36c040d7a04e
commit: 6c85efa5a66d7b254aa22a39d47f36c040d7a04e
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-02-05T22:47:31+02:00
summary:

bpo-32749: Make dbm.dumb databases more cosistent with other dbm databases. (#5497)

files:
A Misc/NEWS.d/next/Library/2018-02-02-17-21-24.bpo-32749.u5scIn.rst
M Doc/library/dbm.rst
M Doc/whatsnew/3.8.rst
M Lib/dbm/dumb.py
M Lib/test/test_dbm_dumb.py

diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst
index 32e80b2cf6ed..1abc36c04a74 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 c4063ad76744..60f54a0561e5 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 5064668c77ea..e5c17f5ae2ed 100644
--- a/Lib/dbm/dumb.py
+++ b/Lib/dbm/dumb.py
@@ -82,10 +82,7 @@ def _create(self, flag):
             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 @@ def _create(self, flag):
 
     # 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 @@ def _addkey(self, key, pos_and_siz_pair):
 
     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 @@ def __setitem__(self, key, val):
 
     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 73f2a32a7b49..21f29af05d28 100644
--- a/Lib/test/test_dbm_dumb.py
+++ b/Lib/test/test_dbm_dumb.py
@@ -79,10 +79,10 @@ def test_dumbdbm_read(self):
         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 @@ def test_eval(self):
                     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 000000000000..9665ff1f8ec8
--- /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.



More information about the Python-checkins mailing list