From 5f6a0b4eb26695be759cd32e49e83f38b5123ce6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 8 Feb 2016 16:23:28 +0200 Subject: [PATCH] Issue #25911: Restored support of bytes paths in os.walk() on Windows. --- Lib/os.py | 27 ++++++++++++++++++++++----- Lib/test/test_os.py | 22 +++++++++++++++++----- Misc/NEWS | 2 ++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/Lib/os.py b/Lib/os.py index 13a42c331e7..674a7d7efda 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -363,9 +363,12 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # minor reason when (say) a thousand readable directories are still # left to visit. That logic is copied here. try: - # Note that scandir is global in this module due - # to earlier import-*. - scandir_it = scandir(top) + if name == 'nt' and isinstance(top, bytes): + scandir_it = _dummy_scandir(top) + else: + # Note that scandir is global in this module due + # to earlier import-*. + scandir_it = scandir(top) except OSError as error: if onerror is not None: onerror(error) @@ -418,8 +421,8 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # Recurse into sub-directories islink, join = path.islink, path.join - for name in dirs: - new_path = join(top, name) + for dirname in dirs: + new_path = join(top, dirname) # Issue #23605: os.path.islink() is used instead of caching # entry.is_symlink() result during the loop on os.scandir() because # the caller can replace the directory entry during the "yield" @@ -430,6 +433,20 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # Yield after recursion if going bottom up yield top, dirs, nondirs +class _DummyDirEntry: + def __init__(self, dir, name): + self.name = name + self.path = path.join(dir, name) + def is_dir(self): + return path.isdir(self.path) + def is_symlink(self): + return path.islink(self.path) + +def _dummy_scandir(dir): + # listdir-based implementation for bytes patches on Windows + for name in listdir(dir): + yield _DummyDirEntry(dir, name) + __all__.append("walk") if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index ade75a51ed5..73db39c8258 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -791,10 +791,10 @@ class WalkTests(unittest.TestCase): # Wrapper to hide minor differences between os.walk and os.fwalk # to tests both functions with the same code base - def walk(self, directory, **kwargs): + def walk(self, top, **kwargs): if 'follow_symlinks' in kwargs: kwargs['followlinks'] = kwargs.pop('follow_symlinks') - return os.walk(directory, **kwargs) + return os.walk(top, **kwargs) def setUp(self): join = os.path.join @@ -945,11 +945,10 @@ class WalkTests(unittest.TestCase): class FwalkTests(WalkTests): """Tests for os.fwalk().""" - def walk(self, directory, **kwargs): - for root, dirs, files, root_fd in os.fwalk(directory, **kwargs): + def walk(self, top, **kwargs): + for root, dirs, files, root_fd in os.fwalk(top, **kwargs): yield (root, dirs, files) - def _compare_to_walk(self, walk_kwargs, fwalk_kwargs): """ compare with walk() results. @@ -1020,6 +1019,19 @@ class FwalkTests(WalkTests): os.unlink(name, dir_fd=rootfd) os.rmdir(support.TESTFN) +class BytesWalkTests(WalkTests): + """Tests for os.walk() with bytes.""" + def walk(self, top, **kwargs): + if 'follow_symlinks' in kwargs: + kwargs['followlinks'] = kwargs.pop('follow_symlinks') + for broot, bdirs, bfiles in os.walk(os.fsencode(top), **kwargs): + root = os.fsdecode(broot) + dirs = list(map(os.fsdecode, bdirs)) + files = list(map(os.fsdecode, bfiles)) + yield (root, dirs, files) + bdirs[:] = list(map(os.fsencode, dirs)) + bfiles[:] = list(map(os.fsencode, files)) + class MakedirTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS b/Misc/NEWS index 29a09d756af..189ea311dd3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -73,6 +73,8 @@ Core and Builtins Library ------- +- Issue #25911: Restored support of bytes paths in os.walk() on Windows. + - Issue #26045: Add UTF-8 suggestion to error message when posting a non-Latin-1 string with http.client.