diff -r 7b9ad68db14e Doc/library/dbm.rst --- a/Doc/library/dbm.rst Mon Jun 20 00:05:40 2016 +0300 +++ b/Doc/library/dbm.rst Mon Jun 20 01:03:11 2016 +0300 @@ -341,6 +341,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. + In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects provide the following methods: diff -r 7b9ad68db14e Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Mon Jun 20 00:05:40 2016 +0300 +++ b/Doc/whatsnew/3.6.rst Mon Jun 20 01:03:11 2016 +0300 @@ -575,6 +575,11 @@ Deprecated features ``__package__`` are not defined now raises an :exc:`ImportWarning`. (Contributed by Rose Ames in :issue:`25791`.) +* Unlike to other :mod:`dbm` implementations, the :mod:`dbm.dumb` module + creates database in ``'r'`` and ``'w'`` modes if it doesn't exist and + allows modifying database in ``'r'`` mode. This behavior is now deprecated + and will be removed in 3.8. + (Contributed by Serhiy Storchaka in :issue:`21708`.) Deprecated Python behavior -------------------------- diff -r 7b9ad68db14e Lib/dbm/dumb.py --- a/Lib/dbm/dumb.py Mon Jun 20 00:05:40 2016 +0300 +++ b/Lib/dbm/dumb.py Mon Jun 20 01:03:11 2016 +0300 @@ -47,6 +47,7 @@ class _Database(collections.MutableMappi def __init__(self, filebasename, mode, flag='c'): self._mode = mode + self._readonly = (flag == 'r') # The directory file is a text file. Each line looks like # "%r, (%d, %d)\n" % (key, pos, siz) @@ -80,6 +81,11 @@ class _Database(collections.MutableMappi try: 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) with _io.open(self._datfile, 'w', encoding="Latin-1") as f: self._chmod(self._datfile) else: @@ -178,6 +184,10 @@ class _Database(collections.MutableMappi f.write("%r, %r\n" % (key.decode("Latin-1"), 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) if isinstance(key, str): key = key.encode('utf-8') elif not isinstance(key, (bytes, bytearray)): @@ -212,6 +222,10 @@ class _Database(collections.MutableMappi # (so that _commit() never gets called). def __delitem__(self, key): + if self._readonly: + import warnings + warnings.warn('The database is opened for reading only', + DeprecationWarning, stacklevel=2) if isinstance(key, str): key = key.encode('utf-8') self._verify_open() @@ -300,4 +314,8 @@ def open(file, flag='c', mode=0o666): else: # 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) return _Database(file, mode, flag=flag) diff -r 7b9ad68db14e Lib/test/test_dbm_dumb.py --- a/Lib/test/test_dbm_dumb.py Mon Jun 20 00:05:40 2016 +0300 +++ b/Lib/test/test_dbm_dumb.py Mon Jun 20 01:03:11 2016 +0300 @@ -6,6 +6,7 @@ import io import operator import os import unittest +import warnings import dbm.dumb as dumbdbm from test import support from functools import partial @@ -78,6 +79,12 @@ class DumbDBMTestCase(unittest.TestCase) self.init_db() f = dumbdbm.open(_fname, 'r') self.read_helper(f) + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + f[b'g'] = b'x' + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + del f[b'a'] f.close() def test_dumbdbm_keys(self): @@ -148,7 +155,7 @@ class DumbDBMTestCase(unittest.TestCase) self.assertEqual(self._dict[key], f[key]) def init_db(self): - f = dumbdbm.open(_fname, 'w') + f = dumbdbm.open(_fname, 'n') for k in self._dict: f[k] = self._dict[k] f.close() @@ -234,6 +241,24 @@ class DumbDBMTestCase(unittest.TestCase) pass self.assertEqual(stdout.getvalue(), '') + def test_warn_on_ignored_flags(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() + + 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() + def tearDown(self): _delete_files()