From ed4da7a30e7c65a999547f26566ec48e0b4e7920 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 8 Jul 2017 20:26:41 +0300 Subject: [PATCH 1/2] bpo-30879: os.listdir() and os.scandir() now emit bytes names when called with bytes-like argument. --- Lib/test/test_os.py | 19 +++++++++++-------- Lib/test/test_posix.py | 11 ++++++++--- Misc/NEWS | 3 +++ Modules/posixmodule.c | 6 +++--- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 49e5a37cd209d79..dcea6bf381b9528 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3411,14 +3411,17 @@ def test_broken_symlink(self): def test_bytes(self): self.create_file("file.txt") - path_bytes = os.fsencode(self.path) - entries = list(os.scandir(path_bytes)) - self.assertEqual(len(entries), 1, entries) - entry = entries[0] - - self.assertEqual(entry.name, b'file.txt') - self.assertEqual(entry.path, - os.fsencode(os.path.join(self.path, 'file.txt'))) + for cls in bytes, bytearray, memoryview: + path_bytes = cls(os.fsencode(self.path)) + entries = list(os.scandir(path_bytes)) + self.assertEqual(len(entries), 1, entries) + entry = entries[0] + + self.assertEqual(entry.name, b'file.txt') + self.assertEqual(entry.path, + os.fsencode(os.path.join(self.path, 'file.txt'))) + self.assertIs(type(entry.name), bytes) + self.assertIs(type(entry.path), bytes) @unittest.skipUnless(os.listdir in os.supports_fd, 'fd support for listdir required for this test.') diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index aa1b0e44635e163..7f0ab1d46616c17 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -643,17 +643,22 @@ def test_chdir(self): self.assertRaises(OSError, posix.chdir, support.TESTFN) def test_listdir(self): - self.assertTrue(support.TESTFN in posix.listdir(os.curdir)) + self.assertIn(support.TESTFN, posix.listdir(os.curdir)) def test_listdir_default(self): # When listdir is called without argument, # it's the same as listdir(os.curdir). - self.assertTrue(support.TESTFN in posix.listdir()) + self.assertIn(support.TESTFN, posix.listdir()) def test_listdir_bytes(self): # When listdir is called with a bytes object, # the returned strings are of type bytes. - self.assertTrue(os.fsencode(support.TESTFN) in posix.listdir(b'.')) + # Bytes-like objects are supported too. + for cls in bytes, bytearray, memoryview: + names = posix.listdir(cls(b'.')) + self.assertIn(os.fsencode(support.TESTFN), names) + for name in names: + self.assertIs(type(name), bytes) @unittest.skipUnless(posix.listdir in os.supports_fd, "test needs fd support for posix.listdir()") diff --git a/Misc/NEWS b/Misc/NEWS index 2f2fafff5ee910e..ce4874ac36df6a4 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -376,6 +376,9 @@ Extension Modules Library ------- +- bpo-30879: os.listdir() and os.scandir() now emit bytes names when called + with bytes-like argument. + - bpo-30746: Prohibited the '=' character in environment variable names in ``os.putenv()`` and ``os.spawn*()``. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 194a2b5aa563d1d..33465ef7fcde4ee 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -3588,8 +3588,8 @@ _posix_listdir(path_t *path, PyObject *list) const char *name; if (path->narrow) { name = path->narrow; - /* only return bytes if they specified a bytes object */ - return_str = !(PyBytes_Check(path->object)); + /* only return bytes if they specified a bytes-like object */ + return_str = !PyObject_CheckBuffer(path->object); } else { name = "."; @@ -11842,7 +11842,7 @@ DirEntry_from_posix_info(path_t *path, const char *name, Py_ssize_t name_len, goto error; } - if (!path->narrow || !PyBytes_Check(path->object)) { + if (!path->narrow || !PyObject_CheckBuffer(path->object)) { entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len); if (joined_path) entry->path = PyUnicode_DecodeFSDefault(joined_path); From 89c3fd6523e5fafabc0910f9110ba4a8956a1418 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 9 Jul 2017 06:55:32 +0300 Subject: [PATCH 2/2] Wrap testing deprecated features with assertWarns(). --- Lib/test/test_os.py | 17 +++++++++++++++-- Lib/test/test_posix.py | 9 ++++++--- Modules/posixmodule.c | 2 ++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index dcea6bf381b9528..5ff18cea13479b4 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3411,9 +3411,22 @@ def test_broken_symlink(self): def test_bytes(self): self.create_file("file.txt") - for cls in bytes, bytearray, memoryview: + path_bytes = os.fsencode(self.path) + entries = list(os.scandir(path_bytes)) + self.assertEqual(len(entries), 1, entries) + entry = entries[0] + + self.assertEqual(entry.name, b'file.txt') + self.assertEqual(entry.path, + os.fsencode(os.path.join(self.path, 'file.txt'))) + + def test_bytes_like(self): + self.create_file("file.txt") + + for cls in bytearray, memoryview: path_bytes = cls(os.fsencode(self.path)) - entries = list(os.scandir(path_bytes)) + with self.assertWarns(DeprecationWarning): + entries = list(os.scandir(path_bytes)) self.assertEqual(len(entries), 1, entries) entry = entries[0] diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 7f0ab1d46616c17..22b050d4d723c81 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -653,9 +653,12 @@ def test_listdir_default(self): def test_listdir_bytes(self): # When listdir is called with a bytes object, # the returned strings are of type bytes. - # Bytes-like objects are supported too. - for cls in bytes, bytearray, memoryview: - names = posix.listdir(cls(b'.')) + self.assertIn(os.fsencode(support.TESTFN), posix.listdir(b'.')) + + def test_listdir_bytes_like(self): + for cls in bytearray, memoryview: + with self.assertWarns(DeprecationWarning): + names = posix.listdir(cls(b'.')) self.assertIn(os.fsencode(support.TESTFN), names) for name in names: self.assertIs(type(name), bytes) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 33465ef7fcde4ee..0c6723fb09f114b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1037,6 +1037,8 @@ path_converter(PyObject *o, void *p) Py_INCREF(bytes); } else if (is_buffer) { + /* XXX Replace PyObject_CheckBuffer with PyBytes_Check in other code + after removing suport of non-bytes buffer objects. */ if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "%s%s%s should be %s, not %.200s", path->function_name ? path->function_name : "",