From d1f47186ac3981f7414283cc842144fa49ce7b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Sun, 24 May 2020 11:45:21 +0300 Subject: [PATCH 01/13] Support pathlike objects on dbm/shelve --- Lib/dbm/__init__.py | 2 + Lib/dbm/dumb.py | 1 + Lib/test/test_dbm.py | 52 +++++++++++-------- Lib/test/test_dbm_dumb.py | 4 ++ Lib/test/test_dbm_gnu.py | 4 ++ Lib/test/test_dbm_ndbm.py | 4 ++ Lib/test/test_shelve.py | 31 ++++++----- .../2020-05-21-01-42-32.bpo-40563.fDn5bP.rst | 1 + 8 files changed, 62 insertions(+), 37 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py index f65da521af4da8d..51164fb0068108f 100644 --- a/Lib/dbm/__init__.py +++ b/Lib/dbm/__init__.py @@ -75,6 +75,7 @@ def open(file, flag='r', mode=0o666): raise ImportError("no dbm clone found; tried %s" % _names) # guess the type of an existing database, if not creating a new one + file = os.fspath(file) result = whichdb(file) if 'n' not in flag else None if result is None: # db doesn't exist or 'n' flag was specified to create a new db @@ -109,6 +110,7 @@ def whichdb(filename): """ # Check for ndbm first -- this has a .pag and a .dir file + filename = os.fspath(filename) try: f = io.open(filename + ".pag", "rb") f.close() diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index 864ad371ec95255..0ad075bb6290d37 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -46,6 +46,7 @@ class _Database(collections.abc.MutableMapping): _io = _io # for _commit() def __init__(self, filebasename, mode, flag='c'): + filebasename = self._os.fspath(filebasename) self._mode = mode self._readonly = (flag == 'r') diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index e02d1e16ae3da7c..ec256b130e4f223 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -4,6 +4,7 @@ import glob from test.support import import_helper from test.support import os_helper +from pathlib import Path # Skip tests if dbm module doesn't exist. dbm = import_helper.import_module('dbm') @@ -129,6 +130,9 @@ def test_anydbm_access(self): assert(f[key] == b"Python:") f.close() + def test_open_with_patlib_path(self): + dbm.open(Path(_fname), "c").close() + def read_helper(self, f): keys = self.keys_helper(f) for key in self._dict: @@ -144,34 +148,36 @@ def setUp(self): class WhichDBTestCase(unittest.TestCase): def test_whichdb(self): - for module in dbm_iterator(): - # Check whether whichdb correctly guesses module name - # for databases opened with "module" module. - # Try with empty files first - name = module.__name__ - if name == 'dbm.dumb': - continue # whichdb can't support dbm.dumb - delete_files() - f = module.open(_fname, 'c') - f.close() - self.assertEqual(name, self.dbm.whichdb(_fname)) - # Now add a key - f = module.open(_fname, 'w') - f[b"1"] = b"1" - # and test that we can find it - self.assertIn(b"1", f) - # and read it - self.assertEqual(f[b"1"], b"1") - f.close() - self.assertEqual(name, self.dbm.whichdb(_fname)) + for path in [_fname, Path(_fname)]: + for module in dbm_iterator(): + # Check whether whichdb correctly guesses module name + # for databases opened with "module" module. + # Try with empty files first + name = module.__name__ + if name == 'dbm.dumb': + continue # whichdb can't support dbm.dumb + delete_files() + f = module.open(path, 'c') + f.close() + self.assertEqual(name, self.dbm.whichdb(path)) + # Now add a key + f = module.open(path, 'w') + f[b"1"] = b"1" + # and test that we can find it + self.assertIn(b"1", f) + # and read it + self.assertEqual(f[b"1"], b"1") + f.close() + self.assertEqual(name, self.dbm.whichdb(path)) @unittest.skipUnless(ndbm, reason='Test requires ndbm') def test_whichdb_ndbm(self): # Issue 17198: check that ndbm which is referenced in whichdb is defined db_file = '{}_ndbm.db'.format(_fname) - with open(db_file, 'w'): - self.addCleanup(os_helper.unlink, db_file) - self.assertIsNone(self.dbm.whichdb(db_file[:-3])) + for path in [db_file, Path(db_file)]: + with open(path, 'w'): + self.addCleanup(test.support.unlink, path) + self.assertIsNone(self.dbm.whichdb(path[:-3])) def tearDown(self): delete_files() diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index ddaffb4757bc49d..db30a8655e40125 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -294,6 +294,10 @@ def test_nonascii_filename(self): self.assertTrue(b'key' in db) self.assertEqual(db[b'key'], b'value') + def test_open_with_patlib_path(self): + from pathlib import Path + dumbdbm.open(Path(_fname), "c").close() + def tearDown(self): _delete_files() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index f39b00293734853..32b266d8e880510 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -169,6 +169,10 @@ def test_nonexisting_file(self): self.assertIn(nonexisting_file, str(cm.exception)) self.assertEqual(cm.exception.filename, nonexisting_file) + def test_open_with_patlib_path(self): + from pathlib import Path + gdbm.open(Path(filename), "c").close() + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index 639c8330cd7b9eb..72b1fb567ba1763 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -124,6 +124,10 @@ def test_nonexisting_file(self): self.assertIn(nonexisting_file, str(cm.exception)) self.assertEqual(cm.exception.filename, nonexisting_file) + def test_open_with_patlib_path(self): + from pathlib import Path + dbm.ndbm.open(Path(self.filename), "c").close() + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index cfdd67c26c5f51f..f6d5b566ce74cab 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -65,29 +65,32 @@ def test_close(self): else: self.fail('Closed shelf should not find a key') - def test_ascii_file_shelf(self): - s = shelve.open(self.fn, protocol=0) + def test_open_template(self, filename=None, protocol=None): + kwargs = { + "filename": filename or self.fn, + } + if protocol: + kwargs["protocol"] = protocol + s = shelve.open(**kwargs) try: s['key1'] = (1,2,3,4) self.assertEqual(s['key1'], (1,2,3,4)) finally: s.close() + def test_ascii_file_shelf(self): + self.test_open_template(protocol=0) + def test_binary_file_shelf(self): - s = shelve.open(self.fn, protocol=1) - try: - s['key1'] = (1,2,3,4) - self.assertEqual(s['key1'], (1,2,3,4)) - finally: - s.close() + self.test_open_template(protocol=1) def test_proto2_file_shelf(self): - s = shelve.open(self.fn, protocol=2) - try: - s['key1'] = (1,2,3,4) - self.assertEqual(s['key1'], (1,2,3,4)) - finally: - s.close() + self.test_open_template(protocol=2) + + def test_patlib_path_file_shelf(self): + from pathlib import Path + self.test_open_template(filename=Path(self.fn)) + def test_in_memory_shelf(self): d1 = byteskeydict() diff --git a/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst b/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst new file mode 100644 index 000000000000000..f5e5bd1b2d3b11f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst @@ -0,0 +1 @@ +Support pathlike objects on dbm/shelve. Patch by Hakan Çelik. From 808cafa9f173ed1f77902102268fd967019c30bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Sun, 24 May 2020 12:29:20 +0300 Subject: [PATCH 02/13] doc update --- Doc/library/dbm.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index ff01ae90f642573..e004dc955fc3641 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -33,6 +33,8 @@ the Oracle Berkeley DB. file's format can't be guessed; or a string containing the required module name, such as ``'dbm.ndbm'`` or ``'dbm.gnu'``. +.. versionchanged:: 3.10 + Accepts :term:`path-like object` for filename. .. function:: open(file, flag='r', mode=0o666) @@ -77,6 +79,9 @@ available, as well as :meth:`get` and :meth:`setdefault`. Deleting a key from a read-only database raises database module specific error instead of :exc:`KeyError`. +.. versionchanged:: 3.10 + Accepts :term:`path-like object` for file. + Key and values are always stored as bytes. This means that when strings are used they are implicitly converted to the default encoding before being stored. @@ -202,6 +207,9 @@ supported. In addition to the dictionary-like methods, ``gdbm`` objects have the following methods: + .. versionchanged:: 3.10 + Accepts :term:`path-like object` for filename. + .. method:: gdbm.firstkey() It's possible to loop over every key in the database using this method and the @@ -298,6 +306,9 @@ to locate the appropriate header file to simplify building this module. In addition to the dictionary-like methods, ``ndbm`` objects provide the following method: + .. versionchanged:: 3.10 + Accepts :term:`path-like object` for filename. + .. method:: ndbm.close() Close the ``ndbm`` database. @@ -379,6 +390,9 @@ The module defines the following: flags ``'r'`` and ``'w'`` no longer creates a database if it does not exist. + .. versionchanged:: 3.10 + Accepts :term:`path-like object` for filename. + In addition to the methods provided by the :class:`collections.abc.MutableMapping` class, :class:`dumbdbm` objects provide the following methods: From 4aefe01e80fd7f48758b3aa0076193c95e2fb0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Sun, 24 May 2020 15:51:08 +0300 Subject: [PATCH 03/13] update _gdbmmodule.c --- Modules/_gdbmmodule.c | 12 +++++++++++- Modules/clinic/_gdbmmodule.c.h | 7 ------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 3c9a0e98e47427d..6ad4ef180d782eb 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -590,7 +590,7 @@ static PyType_Spec gdbmtype_spec = { /*[clinic input] _gdbm.open as dbmopen - filename: unicode + filename: object flags: str="r" mode: int(py_default="0o666") = 0o666 / @@ -672,6 +672,16 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, } } + PyObject *filenamebytes = PyOS_FSPath(filename); + if (filenamebytes == NULL) + return NULL; + if (PyUnicode_Check(filenamebytes) { + PyObject *tmp = PyUnicode_EncodeFSDefault(filenamebytes); + Py_DECREF(filenamebytes); + filenamebytes = tmp; + if (tmp == NULL) + return NULL; + } PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename); if (filenamebytes == NULL) { return NULL; diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 15baf52f60d35a5..480867a83d75dd4 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -303,13 +303,6 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("open", nargs, 1, 3)) { goto exit; } - if (!PyUnicode_Check(args[0])) { - _PyArg_BadArgument("open", "argument 1", "str", args[0]); - goto exit; - } - if (PyUnicode_READY(args[0]) == -1) { - goto exit; - } filename = args[0]; if (nargs < 2) { goto skip_optional; From f3b42b516fd90392506e94d5ae92da90819ab211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87elik?= Date: Sun, 7 Jun 2020 15:37:16 +0300 Subject: [PATCH 04/13] syntax errors fix --- Modules/_dbmmodule.c | 10 ++++++++++ Modules/_gdbmmodule.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 3fe97eff1586bc4..8d3eeb167ff0214 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -479,6 +479,16 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, return NULL; } + PyObject *filenamebytes = PyOS_FSPath(filename); + if (filenamebytes == NULL) + return NULL; + if (PyUnicode_Check(filenamebytes)) { + PyObject *tmp = PyUnicode_EncodeFSDefault(filenamebytes); + Py_DECREF(filenamebytes); + filenamebytes = tmp; + if (tmp == NULL) + return NULL; + } PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename); if (filenamebytes == NULL) { return NULL; diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 6ad4ef180d782eb..50f387f88db056c 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -675,7 +675,7 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, PyObject *filenamebytes = PyOS_FSPath(filename); if (filenamebytes == NULL) return NULL; - if (PyUnicode_Check(filenamebytes) { + if (PyUnicode_Check(filenamebytes)) { PyObject *tmp = PyUnicode_EncodeFSDefault(filenamebytes); Py_DECREF(filenamebytes); filenamebytes = tmp; From a0ec1dbce8a15a585df6a84a3508d27cb3d4b844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Wed, 12 Aug 2020 17:44:40 +0200 Subject: [PATCH 05/13] =?UTF-8?q?patlib=20=E2=86=92=20patHlib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/test/test_dbm.py | 2 +- Lib/test/test_dbm_dumb.py | 2 +- Lib/test/test_dbm_gnu.py | 2 +- Lib/test/test_dbm_ndbm.py | 2 +- Lib/test/test_shelve.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index ec256b130e4f223..5aa1a77bc8bf011 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -130,7 +130,7 @@ def test_anydbm_access(self): assert(f[key] == b"Python:") f.close() - def test_open_with_patlib_path(self): + def test_open_with_pathlib_path(self): dbm.open(Path(_fname), "c").close() def read_helper(self, f): diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index db30a8655e40125..91977c75486ae2d 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -294,7 +294,7 @@ def test_nonascii_filename(self): self.assertTrue(b'key' in db) self.assertEqual(db[b'key'], b'value') - def test_open_with_patlib_path(self): + def test_open_with_pathlib_path(self): from pathlib import Path dumbdbm.open(Path(_fname), "c").close() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index 32b266d8e880510..a432cc1beecc0ca 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -169,7 +169,7 @@ def test_nonexisting_file(self): self.assertIn(nonexisting_file, str(cm.exception)) self.assertEqual(cm.exception.filename, nonexisting_file) - def test_open_with_patlib_path(self): + def test_open_with_pathlib_path(self): from pathlib import Path gdbm.open(Path(filename), "c").close() diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index 72b1fb567ba1763..b16ecf4c914e137 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -124,7 +124,7 @@ def test_nonexisting_file(self): self.assertIn(nonexisting_file, str(cm.exception)) self.assertEqual(cm.exception.filename, nonexisting_file) - def test_open_with_patlib_path(self): + def test_open_with_pathlib_path(self): from pathlib import Path dbm.ndbm.open(Path(self.filename), "c").close() diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index f6d5b566ce74cab..67b7f4c01a7c96d 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -87,7 +87,7 @@ def test_binary_file_shelf(self): def test_proto2_file_shelf(self): self.test_open_template(protocol=2) - def test_patlib_path_file_shelf(self): + def test_pathlib_path_file_shelf(self): from pathlib import Path self.test_open_template(filename=Path(self.fn)) From 8b7bad0dbe56110ac06de81ff14e5f81d834c78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Wed, 12 Aug 2020 17:56:03 +0200 Subject: [PATCH 06/13] Fix double filenamebytes definition --- Modules/_dbmmodule.c | 14 +++++++------- Modules/_gdbmmodule.c | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 8d3eeb167ff0214..f2ed3446ad46b22 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -479,17 +479,17 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, return NULL; } - PyObject *filenamebytes = PyOS_FSPath(filename); - if (filenamebytes == NULL) + PyObject *filenamechars = PyOS_FSPath(filename); + if (filenamechars == NULL) return NULL; - if (PyUnicode_Check(filenamebytes)) { - PyObject *tmp = PyUnicode_EncodeFSDefault(filenamebytes); - Py_DECREF(filenamebytes); - filenamebytes = tmp; + if (PyUnicode_Check(filenamechars)) { + PyObject *tmp = PyUnicode_EncodeFSDefault(filenamechars); + Py_DECREF(filenamechars); + filenamechars = tmp; if (tmp == NULL) return NULL; } - PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename); + PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filenamechars); if (filenamebytes == NULL) { return NULL; } diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 50f387f88db056c..43767226268e07e 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -672,17 +672,17 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, } } - PyObject *filenamebytes = PyOS_FSPath(filename); - if (filenamebytes == NULL) + PyObject *filenamechars = PyOS_FSPath(filename); + if (filenamechars == NULL) return NULL; - if (PyUnicode_Check(filenamebytes)) { - PyObject *tmp = PyUnicode_EncodeFSDefault(filenamebytes); - Py_DECREF(filenamebytes); - filenamebytes = tmp; + if (PyUnicode_Check(filenamechars)) { + PyObject *tmp = PyUnicode_EncodeFSDefault(filenamechars); + Py_DECREF(filenamechars); + filenamechars = tmp; if (tmp == NULL) return NULL; } - PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filename); + PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filenamechars); if (filenamebytes == NULL) { return NULL; } From 4e8df7a66ce8c25c7cfbc7e8b6cced5347639a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Wed, 12 Aug 2020 19:50:27 +0200 Subject: [PATCH 07/13] Avoid using [:-3] for Path objects, use .stem instead --- Lib/test/test_dbm.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 5aa1a77bc8bf011..5fec90055322b99 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -174,10 +174,13 @@ def test_whichdb(self): def test_whichdb_ndbm(self): # Issue 17198: check that ndbm which is referenced in whichdb is defined db_file = '{}_ndbm.db'.format(_fname) - for path in [db_file, Path(db_file)]: - with open(path, 'w'): - self.addCleanup(test.support.unlink, path) - self.assertIsNone(self.dbm.whichdb(path[:-3])) + with open(db_file, 'w'): + self.addCleanup(os_helper.unlink, db_file) + self.assertIsNone(self.dbm.whichdb(db_file[:-3])) + path = Path(db_file) + with open(path, 'w'): + self.addCleanup(os_helper.unlink, path) + self.assertIsNone(self.dbm.whichdb(path.stem)) def tearDown(self): delete_files() From 3282985f2bcc38e28243d5258dbf151b20d818b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Wed, 12 Aug 2020 19:51:26 +0200 Subject: [PATCH 08/13] Support PathLike, str and bytes as dbmopen_impl argument --- Modules/_dbmmodule.c | 26 ++++++++++++++------------ Modules/_gdbmmodule.c | 24 +++++++++++++----------- Modules/clinic/_dbmmodule.c.h | 7 ------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index f2ed3446ad46b22..0b8f7d435f744e9 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -433,7 +433,7 @@ static PyType_Spec dbmtype_spec = { _dbm.open as dbmopen - filename: unicode + filename: object The filename to open. flags: str="r" @@ -479,20 +479,22 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, return NULL; } - PyObject *filenamechars = PyOS_FSPath(filename); - if (filenamechars == NULL) + PyObject *filenamecharsorbytes = PyOS_FSPath(filename); + if (filenamecharsorbytes == NULL) { return NULL; - if (PyUnicode_Check(filenamechars)) { - PyObject *tmp = PyUnicode_EncodeFSDefault(filenamechars); - Py_DECREF(filenamechars); - filenamechars = tmp; - if (tmp == NULL) - return NULL; } - PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filenamechars); - if (filenamebytes == NULL) { - return NULL; + + PyObject *filenamebytes; + if (PyUnicode_Check(filenamecharsorbytes)) { + filenamebytes = PyUnicode_EncodeFSDefault(filenamecharsorbytes); + Py_DECREF(filenamecharsorbytes); + if (filenamebytes == NULL) { + return NULL; + } + } else { + filenamebytes = filenamecharsorbytes; } + const char *name = PyBytes_AS_STRING(filenamebytes); if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) { Py_DECREF(filenamebytes); diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 43767226268e07e..0e8a31c464e65f6 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -672,20 +672,22 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, } } - PyObject *filenamechars = PyOS_FSPath(filename); - if (filenamechars == NULL) + PyObject *filenamecharsorbytes = PyOS_FSPath(filename); + if (filenamecharsorbytes == NULL) { return NULL; - if (PyUnicode_Check(filenamechars)) { - PyObject *tmp = PyUnicode_EncodeFSDefault(filenamechars); - Py_DECREF(filenamechars); - filenamechars = tmp; - if (tmp == NULL) - return NULL; } - PyObject *filenamebytes = PyUnicode_EncodeFSDefault(filenamechars); - if (filenamebytes == NULL) { - return NULL; + + PyObject *filenamebytes; + if (PyUnicode_Check(filenamecharsorbytes)) { + filenamebytes = PyUnicode_EncodeFSDefault(filenamecharsorbytes); + Py_DECREF(filenamecharsorbytes); + if (filenamebytes == NULL) { + return NULL; + } + } else { + filenamebytes = filenamecharsorbytes; } + const char *name = PyBytes_AS_STRING(filenamebytes); if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) { Py_DECREF(filenamebytes); diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index b50db5d0d2e0d14..1ca96d40e4e1b91 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -149,13 +149,6 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) if (!_PyArg_CheckPositional("open", nargs, 1, 3)) { goto exit; } - if (!PyUnicode_Check(args[0])) { - _PyArg_BadArgument("open", "argument 1", "str", args[0]); - goto exit; - } - if (PyUnicode_READY(args[0]) == -1) { - goto exit; - } filename = args[0]; if (nargs < 2) { goto skip_optional; From 8da439cf87513ca5627b4f0f20465c77f750e8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Fri, 14 Aug 2020 10:16:55 +0200 Subject: [PATCH 09/13] Update clinic checksums Should have been done before instead of modifying clinic output code. --- Modules/_dbmmodule.c | 2 +- Modules/_gdbmmodule.c | 2 +- Modules/clinic/_dbmmodule.c.h | 2 +- Modules/clinic/_gdbmmodule.c.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index 0b8f7d435f744e9..e87238a12eeed43 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -452,7 +452,7 @@ Return a database object. static PyObject * dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, int mode) -/*[clinic end generated code: output=9527750f5df90764 input=376a9d903a50df59]*/ +/*[clinic end generated code: output=9527750f5df90764 input=d8cf50a9f81218c8]*/ { int iflags; _dbm_state *state = get_dbm_state(module); diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index 0e8a31c464e65f6..a71d93e11789227 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -622,7 +622,7 @@ when the database has to be created. It defaults to octal 0o666. static PyObject * dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, int mode) -/*[clinic end generated code: output=9527750f5df90764 input=812b7d74399ceb0e]*/ +/*[clinic end generated code: output=9527750f5df90764 input=bca6ec81dc49292c]*/ { int iflags; _gdbm_state *state = get_gdbm_state(module); diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 1ca96d40e4e1b91..f0b8220e7fee252 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -179,4 +179,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=13b6d821416be228 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=32ef6c0f8f2d3db9 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 480867a83d75dd4..a40e80d8e1a7b93 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -333,4 +333,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=1fed9ed50ad23551 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=63c507f93d84a3a4 input=a9049054013a1b77]*/ From b6d23e5e6f9410e99faef6649366d570e02fc491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Wed, 8 Sep 2021 17:53:40 +0200 Subject: [PATCH 10/13] Rework after serhiy-storchaka's review --- Lib/dbm/__init__.py | 16 ++++++++-------- Lib/dbm/dumb.py | 8 ++++---- Lib/test/test_dbm.py | 9 ++++----- Lib/test/test_dbm_dumb.py | 3 +-- Lib/test/test_dbm_gnu.py | 5 ++--- Lib/test/test_dbm_ndbm.py | 3 +-- Lib/test/test_shelve.py | 12 +++--------- .../2020-05-21-01-42-32.bpo-40563.fDn5bP.rst | 2 +- Modules/_dbmmodule.c | 15 ++------------- Modules/_gdbmmodule.c | 15 ++------------- 10 files changed, 28 insertions(+), 60 deletions(-) diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py index 51164fb0068108f..b219a3caa6b2b62 100644 --- a/Lib/dbm/__init__.py +++ b/Lib/dbm/__init__.py @@ -75,7 +75,7 @@ def open(file, flag='r', mode=0o666): raise ImportError("no dbm clone found; tried %s" % _names) # guess the type of an existing database, if not creating a new one - file = os.fspath(file) + file = os.fsencode(file) result = whichdb(file) if 'n' not in flag else None if result is None: # db doesn't exist or 'n' flag was specified to create a new db @@ -110,18 +110,18 @@ def whichdb(filename): """ # Check for ndbm first -- this has a .pag and a .dir file - filename = os.fspath(filename) + filename = os.fsencode(filename) try: - f = io.open(filename + ".pag", "rb") + f = io.open(filename + b".pag", "rb") f.close() - f = io.open(filename + ".dir", "rb") + f = io.open(filename + b".dir", "rb") f.close() return "dbm.ndbm" except OSError: # some dbm emulations based on Berkeley DB generate a .db file # some do not, but they should be caught by the bsd checks try: - f = io.open(filename + ".db", "rb") + f = io.open(filename + b".db", "rb") f.close() # guarantee we can actually open the file using dbm # kind of overkill, but since we are dealing with emulations @@ -136,12 +136,12 @@ def whichdb(filename): # Check for dumbdbm next -- this has a .dir and a .dat file try: # First check for presence of files - os.stat(filename + ".dat") - size = os.stat(filename + ".dir").st_size + os.stat(filename + b".dat") + size = os.stat(filename + b".dir").st_size # dumbdbm files with no keys are empty if size == 0: return "dbm.dumb" - f = io.open(filename + ".dir", "rb") + f = io.open(filename + b".dir", "rb") try: if f.read(1) in (b"'", b'"'): return "dbm.dumb" diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index 0ad075bb6290d37..754624ccc8f5008 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -46,7 +46,7 @@ class _Database(collections.abc.MutableMapping): _io = _io # for _commit() def __init__(self, filebasename, mode, flag='c'): - filebasename = self._os.fspath(filebasename) + filebasename = self._os.fsencode(filebasename) self._mode = mode self._readonly = (flag == 'r') @@ -55,14 +55,14 @@ def __init__(self, filebasename, mode, flag='c'): # where key is the string key, pos is the offset into the dat # file of the associated value's first byte, and siz is the number # of bytes in the associated value. - self._dirfile = filebasename + '.dir' + self._dirfile = filebasename + b'.dir' # The data file is a binary file pointed into by the directory # file, and holds the values associated with keys. Each value # begins at a _BLOCKSIZE-aligned byte offset, and is a raw # binary 8-bit string value. - self._datfile = filebasename + '.dat' - self._bakfile = filebasename + '.bak' + self._datfile = filebasename + b'.dat' + self._bakfile = filebasename + b'.bak' # The index is an in-memory dict, mirroring the directory file. self._index = None # maps keys to (pos, siz) pairs diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index 5fec90055322b99..e6a26ade19a2d16 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -4,7 +4,6 @@ import glob from test.support import import_helper from test.support import os_helper -from pathlib import Path # Skip tests if dbm module doesn't exist. dbm = import_helper.import_module('dbm') @@ -131,7 +130,7 @@ def test_anydbm_access(self): f.close() def test_open_with_pathlib_path(self): - dbm.open(Path(_fname), "c").close() + dbm.open(os_helper.FakePath(_fname), "c").close() def read_helper(self, f): keys = self.keys_helper(f) @@ -148,7 +147,7 @@ def setUp(self): class WhichDBTestCase(unittest.TestCase): def test_whichdb(self): - for path in [_fname, Path(_fname)]: + for path in [_fname, os_helper.FakePath(_fname)]: for module in dbm_iterator(): # Check whether whichdb correctly guesses module name # for databases opened with "module" module. @@ -177,10 +176,10 @@ def test_whichdb_ndbm(self): with open(db_file, 'w'): self.addCleanup(os_helper.unlink, db_file) self.assertIsNone(self.dbm.whichdb(db_file[:-3])) - path = Path(db_file) + path = os_helper.FakePath(db_file) with open(path, 'w'): self.addCleanup(os_helper.unlink, path) - self.assertIsNone(self.dbm.whichdb(path.stem)) + self.assertIsNone(self.dbm.whichdb(db_file[:-3])) def tearDown(self): delete_files() diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 91977c75486ae2d..6093233d0453e83 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -295,8 +295,7 @@ def test_nonascii_filename(self): self.assertEqual(db[b'key'], b'value') def test_open_with_pathlib_path(self): - from pathlib import Path - dumbdbm.open(Path(_fname), "c").close() + dumbdbm.open(os_helper.FakePath(_fname), "c").close() def tearDown(self): _delete_files() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index a432cc1beecc0ca..176b2918148d378 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -3,7 +3,7 @@ gdbm = import_helper.import_module("dbm.gnu") #skip if not supported import unittest import os -from test.support.os_helper import TESTFN, TESTFN_NONASCII, unlink +from test.support.os_helper import TESTFN, TESTFN_NONASCII, unlink, FakePath filename = TESTFN @@ -170,8 +170,7 @@ def test_nonexisting_file(self): self.assertEqual(cm.exception.filename, nonexisting_file) def test_open_with_pathlib_path(self): - from pathlib import Path - gdbm.open(Path(filename), "c").close() + gdbm.open(FakePath(filename), "c").close() if __name__ == '__main__': diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index b16ecf4c914e137..696144040929bba 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -125,8 +125,7 @@ def test_nonexisting_file(self): self.assertEqual(cm.exception.filename, nonexisting_file) def test_open_with_pathlib_path(self): - from pathlib import Path - dbm.ndbm.open(Path(self.filename), "c").close() + dbm.ndbm.open(os_helper.FakePath(self.filename), "c").close() if __name__ == '__main__': diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index 67b7f4c01a7c96d..3fa0763e4e879e6 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -66,12 +66,8 @@ def test_close(self): self.fail('Closed shelf should not find a key') def test_open_template(self, filename=None, protocol=None): - kwargs = { - "filename": filename or self.fn, - } - if protocol: - kwargs["protocol"] = protocol - s = shelve.open(**kwargs) + s = shelve.open(filename=filename if filename is not None else self.fn, + protocol=protocol) try: s['key1'] = (1,2,3,4) self.assertEqual(s['key1'], (1,2,3,4)) @@ -88,9 +84,7 @@ def test_proto2_file_shelf(self): self.test_open_template(protocol=2) def test_pathlib_path_file_shelf(self): - from pathlib import Path - self.test_open_template(filename=Path(self.fn)) - + self.test_open_template(filename=os_helper.FakePath(self.fn)) def test_in_memory_shelf(self): d1 = byteskeydict() diff --git a/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst b/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst index f5e5bd1b2d3b11f..f20664637669af2 100644 --- a/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst +++ b/Misc/NEWS.d/next/Library/2020-05-21-01-42-32.bpo-40563.fDn5bP.rst @@ -1 +1 @@ -Support pathlike objects on dbm/shelve. Patch by Hakan Çelik. +Support pathlike objects on dbm/shelve. Patch by Hakan Çelik and Henry-Joseph Audéoud. diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index e87238a12eeed43..4cbbac3326a8a79 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -479,20 +479,9 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, return NULL; } - PyObject *filenamecharsorbytes = PyOS_FSPath(filename); - if (filenamecharsorbytes == NULL) { - return NULL; - } - PyObject *filenamebytes; - if (PyUnicode_Check(filenamecharsorbytes)) { - filenamebytes = PyUnicode_EncodeFSDefault(filenamecharsorbytes); - Py_DECREF(filenamecharsorbytes); - if (filenamebytes == NULL) { - return NULL; - } - } else { - filenamebytes = filenamecharsorbytes; + if (!PyUnicode_FSConverter(filename, &filenamebytes)) { + return NULL; } const char *name = PyBytes_AS_STRING(filenamebytes); diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index a71d93e11789227..efbf331ca302d1e 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -672,20 +672,9 @@ dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, } } - PyObject *filenamecharsorbytes = PyOS_FSPath(filename); - if (filenamecharsorbytes == NULL) { - return NULL; - } - PyObject *filenamebytes; - if (PyUnicode_Check(filenamecharsorbytes)) { - filenamebytes = PyUnicode_EncodeFSDefault(filenamecharsorbytes); - Py_DECREF(filenamecharsorbytes); - if (filenamebytes == NULL) { - return NULL; - } - } else { - filenamebytes = filenamecharsorbytes; + if (!PyUnicode_FSConverter(filename, &filenamebytes)) { + return NULL; } const char *name = PyBytes_AS_STRING(filenamebytes); From 67771c18741f4dfbb626851e672c56ac7f84cbaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Thu, 9 Sep 2021 10:43:19 +0200 Subject: [PATCH 11/13] Second serhiy-storchaka's review --- Lib/dbm/__init__.py | 1 - Lib/test/test_dbm.py | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py index b219a3caa6b2b62..8055d3769f9dd00 100644 --- a/Lib/dbm/__init__.py +++ b/Lib/dbm/__init__.py @@ -75,7 +75,6 @@ def open(file, flag='r', mode=0o666): raise ImportError("no dbm clone found; tried %s" % _names) # guess the type of an existing database, if not creating a new one - file = os.fsencode(file) result = whichdb(file) if 'n' not in flag else None if result is None: # db doesn't exist or 'n' flag was specified to create a new db diff --git a/Lib/test/test_dbm.py b/Lib/test/test_dbm.py index e6a26ade19a2d16..0404e063fd3be18 100644 --- a/Lib/test/test_dbm.py +++ b/Lib/test/test_dbm.py @@ -2,6 +2,7 @@ import unittest import glob +import os from test.support import import_helper from test.support import os_helper @@ -129,9 +130,15 @@ def test_anydbm_access(self): assert(f[key] == b"Python:") f.close() + def test_open_with_bytes(self): + dbm.open(os.fsencode(_fname), "c").close() + def test_open_with_pathlib_path(self): dbm.open(os_helper.FakePath(_fname), "c").close() + def test_open_with_pathlib_path_bytes(self): + dbm.open(os_helper.FakePath(os.fsencode(_fname)), "c").close() + def read_helper(self, f): keys = self.keys_helper(f) for key in self._dict: @@ -147,7 +154,9 @@ def setUp(self): class WhichDBTestCase(unittest.TestCase): def test_whichdb(self): - for path in [_fname, os_helper.FakePath(_fname)]: + _bytes_fname = os.fsencode(_fname) + for path in [_fname, os_helper.FakePath(_fname), + _bytes_fname, os_helper.FakePath(_bytes_fname)]: for module in dbm_iterator(): # Check whether whichdb correctly guesses module name # for databases opened with "module" module. @@ -175,11 +184,11 @@ def test_whichdb_ndbm(self): db_file = '{}_ndbm.db'.format(_fname) with open(db_file, 'w'): self.addCleanup(os_helper.unlink, db_file) + db_file_bytes = os.fsencode(db_file) self.assertIsNone(self.dbm.whichdb(db_file[:-3])) - path = os_helper.FakePath(db_file) - with open(path, 'w'): - self.addCleanup(os_helper.unlink, path) - self.assertIsNone(self.dbm.whichdb(db_file[:-3])) + self.assertIsNone(self.dbm.whichdb(os_helper.FakePath(db_file[:-3]))) + self.assertIsNone(self.dbm.whichdb(db_file_bytes[:-3])) + self.assertIsNone(self.dbm.whichdb(os_helper.FakePath(db_file_bytes[:-3]))) def tearDown(self): delete_files() From deb1c9b9787cb29e11c6684c7b6c6a3916c10ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Fri, 10 Sep 2021 11:16:29 +0200 Subject: [PATCH 12/13] Fixup doc 3.10 -> 3.11 + versionchanged for shelve --- Doc/library/dbm.rst | 10 +++++----- Doc/library/shelve.rst | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst index e004dc955fc3641..2be499337a2a156 100644 --- a/Doc/library/dbm.rst +++ b/Doc/library/dbm.rst @@ -33,7 +33,7 @@ the Oracle Berkeley DB. file's format can't be guessed; or a string containing the required module name, such as ``'dbm.ndbm'`` or ``'dbm.gnu'``. -.. versionchanged:: 3.10 +.. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. .. function:: open(file, flag='r', mode=0o666) @@ -79,7 +79,7 @@ available, as well as :meth:`get` and :meth:`setdefault`. Deleting a key from a read-only database raises database module specific error instead of :exc:`KeyError`. -.. versionchanged:: 3.10 +.. versionchanged:: 3.11 Accepts :term:`path-like object` for file. Key and values are always stored as bytes. This means that when @@ -207,7 +207,7 @@ supported. In addition to the dictionary-like methods, ``gdbm`` objects have the following methods: - .. versionchanged:: 3.10 + .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. .. method:: gdbm.firstkey() @@ -306,7 +306,7 @@ to locate the appropriate header file to simplify building this module. In addition to the dictionary-like methods, ``ndbm`` objects provide the following method: - .. versionchanged:: 3.10 + .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. .. method:: ndbm.close() @@ -390,7 +390,7 @@ The module defines the following: flags ``'r'`` and ``'w'`` no longer creates a database if it does not exist. - .. versionchanged:: 3.10 + .. versionchanged:: 3.11 Accepts :term:`path-like object` for filename. In addition to the methods provided by the diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst index 684f239ef06fa8e..a50fc6f0bf77b20 100644 --- a/Doc/library/shelve.rst +++ b/Doc/library/shelve.rst @@ -45,6 +45,9 @@ lots of shared sub-objects. The keys are ordinary strings. :data:`pickle.DEFAULT_PROTOCOL` is now used as the default pickle protocol. + .. versionchanged:: 3.11 + Accepts :term:`path-like object` for filename. + .. note:: Do not rely on the shelf being closed automatically; always call From 8628a016d1b3012c8eb31692cf005beb95089dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henry-Joseph=20Aud=C3=A9oud?= Date: Fri, 10 Sep 2021 11:20:19 +0200 Subject: [PATCH 13/13] Add `FakePath(bytes)` & `bytes` filename tests for .open methods --- Lib/test/test_dbm_dumb.py | 6 ++++++ Lib/test/test_dbm_gnu.py | 6 ++++++ Lib/test/test_dbm_ndbm.py | 6 ++++++ Lib/test/test_shelve.py | 7 +++++++ 4 files changed, 25 insertions(+) diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index 6093233d0453e83..73cff638f1e1a4d 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -297,6 +297,12 @@ def test_nonascii_filename(self): def test_open_with_pathlib_path(self): dumbdbm.open(os_helper.FakePath(_fname), "c").close() + def test_open_with_bytes_path(self): + dumbdbm.open(os.fsencode(_fname), "c").close() + + def test_open_with_pathlib_bytes_path(self): + dumbdbm.open(os_helper.FakePath(os.fsencode(_fname)), "c").close() + def tearDown(self): _delete_files() diff --git a/Lib/test/test_dbm_gnu.py b/Lib/test/test_dbm_gnu.py index 176b2918148d378..4eaa0f474b02097 100644 --- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -172,6 +172,12 @@ def test_nonexisting_file(self): def test_open_with_pathlib_path(self): gdbm.open(FakePath(filename), "c").close() + def test_open_with_bytes_path(self): + gdbm.open(os.fsencode(filename), "c").close() + + def test_open_with_pathlib_bytes_path(self): + gdbm.open(FakePath(os.fsencode(filename)), "c").close() + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py index 696144040929bba..e57d9cab1154f7c 100644 --- a/Lib/test/test_dbm_ndbm.py +++ b/Lib/test/test_dbm_ndbm.py @@ -127,6 +127,12 @@ def test_nonexisting_file(self): def test_open_with_pathlib_path(self): dbm.ndbm.open(os_helper.FakePath(self.filename), "c").close() + def test_open_with_bytes_path(self): + dbm.ndbm.open(os.fsencode(self.filename), "c").close() + + def test_open_with_pathlib_bytes_path(self): + dbm.ndbm.open(os_helper.FakePath(os.fsencode(self.filename)), "c").close() + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_shelve.py b/Lib/test/test_shelve.py index 3fa0763e4e879e6..03c03472775a072 100644 --- a/Lib/test/test_shelve.py +++ b/Lib/test/test_shelve.py @@ -2,6 +2,7 @@ import shelve import glob import pickle +import os from test import support from test.support import os_helper @@ -86,6 +87,12 @@ def test_proto2_file_shelf(self): def test_pathlib_path_file_shelf(self): self.test_open_template(filename=os_helper.FakePath(self.fn)) + def test_bytes_path_file_shelf(self): + self.test_open_template(filename=os.fsencode(self.fn)) + + def test_pathlib_bytes_path_file_shelf(self): + self.test_open_template(filename=os_helper.FakePath(os.fsencode(self.fn))) + def test_in_memory_shelf(self): d1 = byteskeydict() with shelve.Shelf(d1, protocol=0) as s: