diff -r 5faccb403ad8 Doc/library/os.rst --- a/Doc/library/os.rst Sat Apr 23 10:53:43 2016 +0300 +++ b/Doc/library/os.rst Tue Apr 26 16:34:39 2016 +0300 @@ -2536,8 +2536,8 @@ Generate the file names in a directory tree by walking the tree either top-down or bottom-up. For each directory in the tree rooted at directory - *top* (including *top* itself), it yields a 3-tuple ``(dirpath, dirnames, - filenames)``. + *top* (including *top* itself), it yields a :class:`walk_result` that represent + the 3-tuple ``(dirpath, dirnames,filenames)``. *dirpath* is a string, the path to the directory. *dirnames* is a list of the names of the subdirectories in *dirpath* (excluding ``'.'`` and ``'..'``). @@ -2617,6 +2617,30 @@ This function now calls :func:`os.scandir` instead of :func:`os.listdir`, making it faster by reducing the number of calls to :func:`os.stat`. + .. versionchanged:: 3.6 + The function yields :class:`walk_result` instead of a 3-tuple. + + +.. class:: walk_result + + This is a namedtuple that represent the result of a single dir entry + in :func:`walk`. + + Attributes: + + .. attribute:: dirpath + + The path to the directory. + + .. attribute:: dirnames + + List of the names of the subdirectories + in *dirpath* (excluding ``'.'`` and ``'..'``). + + .. attribute:: filenames + + List of the names of the non-directory files in *dirpath*. + .. function:: fwalk(top='.', topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None) @@ -2624,7 +2648,8 @@ single: directory; walking single: directory; traversal - This behaves exactly like :func:`walk`, except that it yields a 4-tuple + This behaves exactly like :func:`walk`, except that it yields a + :class:`fwalk_result` that represent 4-tuple ``(dirpath, dirnames, filenames, dirfd)``, and it supports ``dir_fd``. *dirpath*, *dirnames* and *filenames* are identical to :func:`walk` output, @@ -2673,6 +2698,21 @@ .. versionadded:: 3.3 + .. versionchanged:: 3.6 + The function yields :class:`walk_result` instead of 3-tuple. + + +.. class:: fwalk_result + + This class is the same as :class:`walk_result` with the addition + of the :attr:`dirfd` attribute. + + Attributes: + + .. attribute:: dirfd + + File descriptor referring to the directory *dirpath*. + Linux extended attributes ~~~~~~~~~~~~~~~~~~~~~~~~~ diff -r 5faccb403ad8 Lib/os.py --- a/Lib/os.py Sat Apr 23 10:53:43 2016 +0300 +++ b/Lib/os.py Tue Apr 26 16:34:39 2016 +0300 @@ -295,6 +295,10 @@ __all__.extend(["makedirs", "removedirs", "renames"]) +from collections import namedtuple + +walk_result = namedtuple('walk_result', ['dirpath', 'dirnames', 'filenames']) + def walk(top, topdown=True, onerror=None, followlinks=False): """Directory tree generator. @@ -419,7 +423,7 @@ # Yield before recursion if going top down if topdown: - yield top, dirs, nondirs + yield walk_result(top, dirs, nondirs) # Recurse into sub-directories islink, join = path.islink, path.join @@ -436,7 +440,7 @@ for new_path in walk_dirs: yield from walk(new_path, topdown, onerror, followlinks) # Yield after recursion if going bottom up - yield top, dirs, nondirs + yield walk_result(top, dirs, nondirs) class _DummyDirEntry: """Dummy implementation of DirEntry @@ -499,10 +503,12 @@ def __exit__(self, *args): self.it = iter(()) -__all__.append("walk") +__all__.extend(["walk", "walk_result"]) if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: + fwalk_result = namedtuple('fwalk_result', walk_result._fields + ('dirfd',)) + def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None): """Directory tree generator. @@ -574,7 +580,7 @@ continue if topdown: - yield toppath, dirs, nondirs, topfd + yield fwalk_result(toppath, dirs, nondirs, topfd) for name in dirs: try: @@ -592,9 +598,9 @@ close(dirfd) if not topdown: - yield toppath, dirs, nondirs, topfd + yield fwalk_result(toppath, dirs, nondirs, topfd) - __all__.append("fwalk") + __all__.extend(["fwalk", "fwalk_result"]) # Make sure os.environ exists, at least try: diff -r 5faccb403ad8 Lib/test/test_os.py --- a/Lib/test/test_os.py Sat Apr 23 10:53:43 2016 +0300 +++ b/Lib/test/test_os.py Tue Apr 26 16:34:39 2016 +0300 @@ -938,6 +938,12 @@ for dir2 in dirs[1:]: self.assertIn(os.path.join(root, dir2), roots) + def test_walk_yield_walktuple(self): + top = next(self.walk(self.walk_path)) + self.assertEqual(top.dirpath, self.walk_path) + self.assertEqual(top.dirnames, ["SUB1", "SUB2"]) + self.assertEqual(top.filenames, ["tmp1"]) + @unittest.skipUnless(hasattr(os, 'fwalk'), "Test needs os.fwalk()") class FwalkTests(WalkTests): @@ -945,7 +951,7 @@ def walk(self, top, **kwargs): for root, dirs, files, root_fd in os.fwalk(top, **kwargs): - yield (root, dirs, files) + yield os.walk_result(root, dirs, files) def _compare_to_walk(self, walk_kwargs, fwalk_kwargs): """ @@ -957,13 +963,11 @@ walk_kwargs.update(topdown=topdown, followlinks=follow_symlinks) fwalk_kwargs.update(topdown=topdown, follow_symlinks=follow_symlinks) - expected = {} - for root, dirs, files in os.walk(**walk_kwargs): - expected[root] = (set(dirs), set(files)) - - for root, dirs, files, rootfd in os.fwalk(**fwalk_kwargs): - self.assertIn(root, expected) - self.assertEqual(expected[root], (set(dirs), set(files))) + for walk_dir_entry, fwalk_dir_entry in zip(os.walk(**walk_kwargs), os.fwalk(**fwalk_kwargs)): + self.assertEqual(walk_dir_entry.dirpath, fwalk_dir_entry.dirpath) + self.assertEqual(walk_dir_entry.dirnames, fwalk_dir_entry.dirnames) + self.assertEqual(walk_dir_entry.filenames, fwalk_dir_entry.filenames) + def test_compare_to_walk(self): kwargs = {'top': support.TESTFN} @@ -1023,7 +1027,7 @@ root = os.fsdecode(broot) dirs = list(map(os.fsdecode, bdirs)) files = list(map(os.fsdecode, bfiles)) - yield (root, dirs, files) + yield os.walk_result(root, dirs, files) bdirs[:] = list(map(os.fsencode, dirs)) bfiles[:] = list(map(os.fsencode, files))