Skip to content

Commit 8f6b344

Browse files
bpo-28682: Added support for bytes paths in os.fwalk(). (#489)
1 parent 8886d5f commit 8f6b344

File tree

5 files changed

+37
-15
lines changed

5 files changed

+37
-15
lines changed

Doc/library/os.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,6 +2840,9 @@ features:
28402840
.. versionchanged:: 3.6
28412841
Accepts a :term:`path-like object`.
28422842

2843+
.. versionchanged:: 3.7
2844+
Added support for :class:`bytes` paths.
2845+
28432846

28442847
Linux extended attributes
28452848
~~~~~~~~~~~~~~~~~~~~~~~~~

Doc/whatsnew/3.7.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ New Modules
9797
Improved Modules
9898
================
9999

100+
os
101+
--
102+
103+
Added support for :class:`bytes` paths in :func:`~os.fwalk`.
104+
(Contributed by Serhiy Storchaka in :issue:`28682`.)
105+
100106
unittest.mock
101107
-------------
102108

Lib/os.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,16 +460,19 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=
460460
try:
461461
if (follow_symlinks or (st.S_ISDIR(orig_st.st_mode) and
462462
path.samestat(orig_st, stat(topfd)))):
463-
yield from _fwalk(topfd, top, topdown, onerror, follow_symlinks)
463+
yield from _fwalk(topfd, top, isinstance(top, bytes),
464+
topdown, onerror, follow_symlinks)
464465
finally:
465466
close(topfd)
466467

467-
def _fwalk(topfd, toppath, topdown, onerror, follow_symlinks):
468+
def _fwalk(topfd, toppath, isbytes, topdown, onerror, follow_symlinks):
468469
# Note: This uses O(depth of the directory tree) file descriptors: if
469470
# necessary, it can be adapted to only require O(1) FDs, see issue
470471
# #13734.
471472

472473
names = listdir(topfd)
474+
if isbytes:
475+
names = map(fsencode, names)
473476
dirs, nondirs = [], []
474477
for name in names:
475478
try:
@@ -504,7 +507,8 @@ def _fwalk(topfd, toppath, topdown, onerror, follow_symlinks):
504507
try:
505508
if follow_symlinks or path.samestat(orig_st, stat(dirfd)):
506509
dirpath = path.join(toppath, name)
507-
yield from _fwalk(dirfd, dirpath, topdown, onerror, follow_symlinks)
510+
yield from _fwalk(dirfd, dirpath, isbytes,
511+
topdown, onerror, follow_symlinks)
508512
finally:
509513
close(dirfd)
510514

Lib/test/test_os.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,9 +1010,12 @@ class FwalkTests(WalkTests):
10101010
"""Tests for os.fwalk()."""
10111011

10121012
def walk(self, top, **kwargs):
1013-
for root, dirs, files, root_fd in os.fwalk(top, **kwargs):
1013+
for root, dirs, files, root_fd in self.fwalk(top, **kwargs):
10141014
yield (root, dirs, files)
10151015

1016+
def fwalk(self, *args, **kwargs):
1017+
return os.fwalk(*args, **kwargs)
1018+
10161019
def _compare_to_walk(self, walk_kwargs, fwalk_kwargs):
10171020
"""
10181021
compare with walk() results.
@@ -1027,7 +1030,7 @@ def _compare_to_walk(self, walk_kwargs, fwalk_kwargs):
10271030
for root, dirs, files in os.walk(**walk_kwargs):
10281031
expected[root] = (set(dirs), set(files))
10291032

1030-
for root, dirs, files, rootfd in os.fwalk(**fwalk_kwargs):
1033+
for root, dirs, files, rootfd in self.fwalk(**fwalk_kwargs):
10311034
self.assertIn(root, expected)
10321035
self.assertEqual(expected[root], (set(dirs), set(files)))
10331036

@@ -1049,7 +1052,7 @@ def test_yields_correct_dir_fd(self):
10491052
# check returned file descriptors
10501053
for topdown, follow_symlinks in itertools.product((True, False), repeat=2):
10511054
args = support.TESTFN, topdown, None
1052-
for root, dirs, files, rootfd in os.fwalk(*args, follow_symlinks=follow_symlinks):
1055+
for root, dirs, files, rootfd in self.fwalk(*args, follow_symlinks=follow_symlinks):
10531056
# check that the FD is valid
10541057
os.fstat(rootfd)
10551058
# redundant check
@@ -1064,22 +1067,14 @@ def test_fd_leak(self):
10641067
minfd = os.dup(1)
10651068
os.close(minfd)
10661069
for i in range(256):
1067-
for x in os.fwalk(support.TESTFN):
1070+
for x in self.fwalk(support.TESTFN):
10681071
pass
10691072
newfd = os.dup(1)
10701073
self.addCleanup(os.close, newfd)
10711074
self.assertEqual(newfd, minfd)
10721075

10731076
class BytesWalkTests(WalkTests):
10741077
"""Tests for os.walk() with bytes."""
1075-
def setUp(self):
1076-
super().setUp()
1077-
self.stack = contextlib.ExitStack()
1078-
1079-
def tearDown(self):
1080-
self.stack.close()
1081-
super().tearDown()
1082-
10831078
def walk(self, top, **kwargs):
10841079
if 'follow_symlinks' in kwargs:
10851080
kwargs['followlinks'] = kwargs.pop('follow_symlinks')
@@ -1091,6 +1086,18 @@ def walk(self, top, **kwargs):
10911086
bdirs[:] = list(map(os.fsencode, dirs))
10921087
bfiles[:] = list(map(os.fsencode, files))
10931088

1089+
@unittest.skipUnless(hasattr(os, 'fwalk'), "Test needs os.fwalk()")
1090+
class BytesFwalkTests(FwalkTests):
1091+
"""Tests for os.walk() with bytes."""
1092+
def fwalk(self, top='.', *args, **kwargs):
1093+
for broot, bdirs, bfiles, topfd in os.fwalk(os.fsencode(top), *args, **kwargs):
1094+
root = os.fsdecode(broot)
1095+
dirs = list(map(os.fsdecode, bdirs))
1096+
files = list(map(os.fsdecode, bfiles))
1097+
yield (root, dirs, files, topfd)
1098+
bdirs[:] = list(map(os.fsencode, dirs))
1099+
bfiles[:] = list(map(os.fsencode, files))
1100+
10941101

10951102
class MakedirTests(unittest.TestCase):
10961103
def setUp(self):

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ Extension Modules
265265
Library
266266
-------
267267

268+
- bpo-28682: Added support for bytes paths in os.fwalk().
269+
268270
- bpo-29623: Allow use of path-like object as a single argument in
269271
ConfigParser.read(). Patch by David Ellis.
270272

0 commit comments

Comments
 (0)