From ff647215c073ea801d34d88c1df8079ab32ecec6 Mon Sep 17 00:00:00 2001 From: Guilherme Martins Crocetti <24530683+gmcrocetti@users.noreply.github.com> Date: Fri, 17 Apr 2026 11:22:34 -0300 Subject: [PATCH] gh-113093: add parameter 'mode' in shelve.open, letting users control file's type and access permissions --- Doc/library/shelve.rst | 14 +++++++++----- Doc/whatsnew/3.15.rst | 3 ++- Lib/shelve.py | 16 +++++++++------- Lib/test/test_shelve.py | 7 +++++++ ...026-04-17-10-42-00.gh-issue-113093.KzsNvW.rst | 2 ++ 5 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-04-17-10-42-00.gh-issue-113093.KzsNvW.rst diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index bd3d56f6af595a..3588344290a527 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -42,6 +42,11 @@ lots of shared sub-objects. The keys are ordinary strings. determine which accessed entries are mutable, nor which ones were actually mutated). + The optional *mode* parameter controls the file mode (permissions) when creating + a new shelf. It is passed to :func:`dbm.open` and defaults to ``0o666`` (octal), + which means read and write permissions for everyone, subject to the current umask. + This parameter has no effect if the database already exists. + By default, :mod:`!shelve` uses :func:`pickle.dumps` and :func:`pickle.loads` for serializing and deserializing. This can be changed by supplying *serializer* and *deserializer*, respectively. @@ -209,19 +214,19 @@ Restrictions .. class:: DbfilenameShelf(filename, flag='c', protocol=None, \ - writeback=False, *, serializer=None, \ - deserializer=None) + writeback=False, mode=0o666, *, \ + serializer=None, deserializer=None) A subclass of :class:`Shelf` which accepts a *filename* instead of a dict-like object. The underlying file will be opened using :func:`dbm.open`. By default, the file will be created and opened for both read and write. The optional *flag* parameter has the same interpretation as for the - :func:`.open` function. The optional *protocol*, *writeback*, *serializer* + :func:`.open` function. The optional *mode*, *protocol*, *writeback*, *serializer* and *deserializer* parameters have the same interpretation as in :func:`~shelve.open`. .. versionchanged:: 3.15 - Added the *serializer* and *deserializer* parameters. + Added the *mode*, *serializer* and *deserializer* parameters. .. _shelve-example: @@ -284,4 +289,3 @@ Exceptions Module :mod:`pickle` Object serialization used by :mod:`!shelve`. - diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 56cc71b40fce82..2e520077e2f9b3 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1024,7 +1024,8 @@ shelve * Add support for custom serialization and deserialization functions in the :mod:`shelve` module. (Contributed by Furkan Onder in :gh:`99631`.) - +* Add suport for custom mode in :func:`shelve.open`. + (Contributed by Guilherme Crocetti in :gh:`113093`.) socket ------ diff --git a/Lib/shelve.py b/Lib/shelve.py index 9f6296667fdb6b..824fd772156c18 100644 --- a/Lib/shelve.py +++ b/Lib/shelve.py @@ -235,10 +235,10 @@ class DbfilenameShelf(Shelf): See the module's __doc__ string for an overview of the interface. """ - def __init__(self, filename, flag='c', protocol=None, writeback=False, *, - serializer=None, deserializer=None): + def __init__(self, filename, flag='c', protocol=None, writeback=False, + mode=0o666, *, serializer=None, deserializer=None): import dbm - Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback, + Shelf.__init__(self, dbm.open(filename, flag, mode), protocol, writeback, serializer=serializer, deserializer=deserializer) def clear(self): @@ -248,8 +248,8 @@ def clear(self): self.cache.clear() self.dict.clear() -def open(filename, flag='c', protocol=None, writeback=False, *, - serializer=None, deserializer=None): +def open(filename, flag='c', protocol=None, writeback=False, + mode=0o666, *, serializer=None, deserializer=None): """Open a persistent dictionary for reading and writing. The filename parameter is the base filename for the underlying @@ -257,10 +257,12 @@ def open(filename, flag='c', protocol=None, writeback=False, *, filename and more than one file may be created. The optional flag parameter has the same interpretation as the flag parameter of dbm.open(). The optional protocol parameter specifies the - version of the pickle protocol. + version of the pickle protocol. The optional mode parameter is + passed to dbm.open() and controls the file mode when creating a + new shelf, set to 0666 by default. See the module's __doc__ string for an overview of the interface. """ - return DbfilenameShelf(filename, flag, protocol, writeback, + return DbfilenameShelf(filename, flag, protocol, writeback, mode, serializer=serializer, deserializer=deserializer) diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index 5f6a030e018f96..39242eef7bc3d7 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -1,5 +1,6 @@ import array import unittest +from unittest import mock import dbm import shelve import pickle @@ -47,6 +48,12 @@ class TestCase(unittest.TestCase): dirname = os_helper.TESTFN fn = os.path.join(os_helper.TESTFN, "shelftemp.db") + @mock.patch("dbm.open", autospec=True) + def test_open_calls_dbm_as_expected(self, dbm_open): + shelf_open_mode = 0o433 + shelve.open(filename=self.fn, mode=shelf_open_mode) + dbm_open.assert_called_once_with(self.fn, 'c', shelf_open_mode) + def test_close(self): d1 = {} s = shelve.Shelf(d1, protocol=2, writeback=False) diff --git a/Misc/NEWS.d/next/Library/2026-04-17-10-42-00.gh-issue-113093.KzsNvW.rst b/Misc/NEWS.d/next/Library/2026-04-17-10-42-00.gh-issue-113093.KzsNvW.rst new file mode 100644 index 00000000000000..18ed23aabe01de --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-17-10-42-00.gh-issue-113093.KzsNvW.rst @@ -0,0 +1,2 @@ +Add parameter *mode* in :func:`shelve.open`. +Contributed by Guilherme Crocetti.