diff -r 6af0535b5e3a Doc/library/os.rst --- a/Doc/library/os.rst Mon Jun 25 23:20:27 2012 +0100 +++ b/Doc/library/os.rst Mon Jun 25 16:38:58 2012 -0700 @@ -2128,7 +2128,7 @@ and the *dir_fd*, *follow_symlinks*, and *ns* parameters. -.. function:: walk(top, topdown=True, onerror=None, followlinks=False) +.. function:: walk(top='.', topdown=True, onerror=None, followlinks=False, dir_fd=None) .. index:: single: directory; walking @@ -2167,6 +2167,9 @@ with the walk, or raise the exception to abort the walk. Note that the filename is available as the ``filename`` attribute of the exception object. + This function can support :ref:`paths relative to directory descriptors + `. + By default, :func:`walk` will not walk down into symbolic links that resolve to directories. Set *followlinks* to ``True`` to visit directories pointed to by symlinks, on systems that support them. @@ -2218,7 +2221,7 @@ single: directory; traversal This behaves exactly like :func:`walk`, except that it yields a 4-tuple - ``(dirpath, dirnames, filenames, dirfd)``, and it supports ``dir_fd``. + ``(dirpath, dirnames, filenames, dirfd)``. *dirpath*, *dirnames* and *filenames* are identical to :func:`walk` output, and *dirfd* is a file descriptor referring to the directory *dirpath*. diff -r 6af0535b5e3a Lib/os.py --- a/Lib/os.py Mon Jun 25 23:20:27 2012 +0100 +++ b/Lib/os.py Mon Jun 25 16:38:58 2012 -0700 @@ -331,7 +331,7 @@ __all__.extend(["makedirs", "removedirs", "renames"]) -def walk(top, topdown=True, onerror=None, followlinks=False): +def walk(top=".", topdown=True, onerror=None, followlinks=False, dir_fd=None): """Directory tree generator. For each directory in the directory tree rooted at top (including top @@ -360,6 +360,11 @@ dirnames have already been generated by the time dirnames itself is generated. + If dir_fd is not None, it should be a file descriptor open to a directory, + and top should be relative; top will then be relative to that directory. + dir_fd may not be supported on your platform; if it is unavailable, using + it will raise a NotImplementedError. + By default errors from the os.listdir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it will be called with one argument, an os.error instance. It can @@ -388,6 +393,14 @@ dirs.remove('CVS') # don't visit CVS directories """ + if dir_fd is not None: + fwalk = globals().get('fwalk') + if not fwalk: + raise NotImplementedError("walk: dir_fd unavailable on this platform") + for t in fwalk(top, topdown, onerror, followlinks, dir_fd=dir_fd): + yield t[:-1] + return + islink, join, isdir = path.islink, path.join, path.isdir # We may not have read permission for top, in which case we can't @@ -423,6 +436,7 @@ __all__.append("walk") if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: + supports_dir_fd.add(walk) def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None): """Directory tree generator. diff -r 6af0535b5e3a Lib/test/test_os.py --- a/Lib/test/test_os.py Mon Jun 25 23:20:27 2012 +0100 +++ b/Lib/test/test_os.py Mon Jun 25 16:38:58 2012 -0700 @@ -616,8 +616,9 @@ key = 'key=' self.assertRaises(OSError, os.environ.__delitem__, key) -class WalkTests(unittest.TestCase): - """Tests for os.walk().""" + +class SetupWalkTree(unittest.TestCase): + """Construct a file tree for os.walk and os.fwalk to examine.""" def setUp(self): import os @@ -636,24 +637,23 @@ # broken_link # TEST2/ # tmp4 a lone file - walk_path = join(support.TESTFN, "TEST1") - sub1_path = join(walk_path, "SUB1") - sub11_path = join(sub1_path, "SUB11") - sub2_path = join(walk_path, "SUB2") - tmp1_path = join(walk_path, "tmp1") - tmp2_path = join(sub1_path, "tmp2") - tmp3_path = join(sub2_path, "tmp3") - link_path = join(sub2_path, "link") - t2_path = join(support.TESTFN, "TEST2") - tmp4_path = join(support.TESTFN, "TEST2", "tmp4") - link_path = join(sub2_path, "link") - broken_link_path = join(sub2_path, "broken_link") + self.walk_path = walk_path = join(support.TESTFN, "TEST1") + self.sub1_path = sub1_path = join(walk_path, "SUB1") + self.sub11_path = join(sub1_path, "SUB11") + self.sub2_path = sub2_path = join(walk_path, "SUB2") + self.tmp1_path = join(walk_path, "tmp1") + self.tmp2_path = join(sub1_path, "tmp2") + self.tmp3_path = join(sub2_path, "tmp3") + self.t2_path = t2_path = join(support.TESTFN, "TEST2") + self.tmp4_path = join(support.TESTFN, "TEST2", "tmp4") + self.link_path = link_path = join(sub2_path, "link") + self.broken_link_path = broken_link_path = join(sub2_path, "broken_link") # Create stuff. - os.makedirs(sub11_path) - os.makedirs(sub2_path) - os.makedirs(t2_path) - for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path: + os.makedirs(self.sub11_path) + os.makedirs(self.sub2_path) + os.makedirs(self.t2_path) + for path in self.tmp1_path, self.tmp2_path, self.tmp3_path, self.tmp4_path: f = open(path, "w") f.write("I'm " + path + " and proud of it. Blame test_os.\n") f.close() @@ -663,62 +663,11 @@ os.symlink(src, dest, True) else: symlink_to_dir = os.symlink - symlink_to_dir(os.path.abspath(t2_path), link_path) + symlink_to_dir(os.path.abspath(self.t2_path), link_path) symlink_to_dir('broken', broken_link_path) - sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) + self.sub2_tree = (sub2_path, ["link"], ["broken_link", "tmp3"]) else: - sub2_tree = (sub2_path, [], ["tmp3"]) - - # Walk top-down. - all = list(os.walk(walk_path)) - self.assertEqual(len(all), 4) - # We can't know which order SUB1 and SUB2 will appear in. - # Not flipped: TESTFN, SUB1, SUB11, SUB2 - # flipped: TESTFN, SUB2, SUB1, SUB11 - flipped = all[0][1][0] != "SUB1" - all[0][1].sort() - all[3 - 2 * flipped][-1].sort() - self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 + flipped], (sub11_path, [], [])) - self.assertEqual(all[3 - 2 * flipped], sub2_tree) - - # Prune the search. - all = [] - for root, dirs, files in os.walk(walk_path): - all.append((root, dirs, files)) - # Don't descend into SUB1. - if 'SUB1' in dirs: - # Note that this also mutates the dirs we appended to all! - dirs.remove('SUB1') - self.assertEqual(len(all), 2) - self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"])) - all[1][-1].sort() - self.assertEqual(all[1], sub2_tree) - - # Walk bottom-up. - all = list(os.walk(walk_path, topdown=False)) - self.assertEqual(len(all), 4) - # We can't know which order SUB1 and SUB2 will appear in. - # Not flipped: SUB11, SUB1, SUB2, TESTFN - # flipped: SUB2, SUB11, SUB1, TESTFN - flipped = all[3][1][0] != "SUB1" - all[3][1].sort() - all[2 - 2 * flipped][-1].sort() - self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"])) - self.assertEqual(all[flipped], (sub11_path, [], [])) - self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"])) - self.assertEqual(all[2 - 2 * flipped], sub2_tree) - - if support.can_symlink(): - # Walk, following symlinks. - for root, dirs, files in os.walk(walk_path, followlinks=True): - if root == link_path: - self.assertEqual(dirs, []) - self.assertEqual(files, ["tmp4"]) - break - else: - self.fail("Didn't follow symlink with followlinks=True") + self.sub2_tree = (sub2_path, [], ["tmp3"]) def tearDown(self): # Tear everything down. This is a decent use for bottom-up on @@ -736,9 +685,78 @@ os.remove(dirname) os.rmdir(support.TESTFN) +class WalkTests(SetupWalkTree): + + def test_walk_no_arguments(self): + self.assertTrue(any(os.walk())) + + def test_walk(self): + # Walk top-down. + all = list(os.walk(self.walk_path)) + self.assertEqual(len(all), 4) + # We can't know which order SUB1 and SUB2 will appear in. + # Not flipped: TESTFN, SUB1, SUB11, SUB2 + # flipped: TESTFN, SUB2, SUB1, SUB11 + flipped = all[0][1][0] != "SUB1" + all[0][1].sort() + all[3 - 2 * flipped][-1].sort() + self.assertEqual(all[0], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[1 + flipped], (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) + self.assertEqual(all[3 - 2 * flipped], self.sub2_tree) + + # Prune the search. + all = [] + for root, dirs, files in os.walk(self.walk_path): + all.append((root, dirs, files)) + # Don't descend into SUB1. + if 'SUB1' in dirs: + # Note that this also mutates the dirs we appended to all! + dirs.remove('SUB1') + self.assertEqual(len(all), 2) + self.assertEqual(all[0], (self.walk_path, ["SUB2"], ["tmp1"])) + all[1][-1].sort() + self.assertEqual(all[1], self.sub2_tree) + + # Walk bottom-up. + all = list(os.walk(self.walk_path, topdown=False)) + self.assertEqual(len(all), 4) + # We can't know which order SUB1 and SUB2 will appear in. + # Not flipped: SUB11, SUB1, SUB2, TESTFN + # flipped: SUB2, SUB11, SUB1, TESTFN + flipped = all[3][1][0] != "SUB1" + all[3][1].sort() + all[2 - 2 * flipped][-1].sort() + self.assertEqual(all[3], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) + self.assertEqual(all[flipped], (self.sub11_path, [], [])) + self.assertEqual(all[flipped + 1], (self.sub1_path, ["SUB11"], ["tmp2"])) + self.assertEqual(all[2 - 2 * flipped], self.sub2_tree) + + if support.can_symlink(): + # Walk, following symlinks. + for root, dirs, files in os.walk(self.walk_path, followlinks=True): + if root == self.link_path: + self.assertEqual(dirs, []) + self.assertEqual(files, ["tmp4"]) + break + else: + self.fail("Didn't follow symlink with followlinks=True") + + @unittest.skipUnless(os.walk in os.supports_dir_fd, + "os.walk needs dir_fd for this test.") + def test_walk_dir_fd(self): + fd = os.open('.', os.O_RDONLY) + try: + list1 = list(os.walk(self.walk_path)) + list2 = list(os.walk(self.walk_path, dir_fd=fd)) + self.assertEqual(list1, list2) + finally: + if fd is not None: + os.close(fd) + @unittest.skipUnless(hasattr(os, 'fwalk'), "Test needs os.fwalk()") -class FwalkTests(WalkTests): +class FwalkTests(SetupWalkTree): """Tests for os.fwalk().""" def _compare_to_walk(self, walk_kwargs, fwalk_kwargs): @@ -758,11 +776,11 @@ self.assertIn(root, expected) self.assertEqual(expected[root], (set(dirs), set(files))) - def test_compare_to_walk(self): + def test_fwalk_compare_to_walk(self): kwargs = {'top': support.TESTFN} self._compare_to_walk(kwargs, kwargs) - def test_dir_fd(self): + def test_fwalk_use_dir_fd(self): try: fd = os.open(".", os.O_RDONLY) walk_kwargs = {'top': support.TESTFN} @@ -772,7 +790,7 @@ finally: os.close(fd) - def test_yields_correct_dir_fd(self): + def test_fwalk_yields_correct_dir_fd(self): # check returned file descriptors for topdown, followlinks in itertools.product((True, False), repeat=2): args = support.TESTFN, topdown, None, followlinks @@ -784,7 +802,7 @@ # check that listdir() returns consistent information self.assertEqual(set(os.listdir(rootfd)), set(dirs) | set(files)) - def test_fd_leak(self): + def test_fwalk_fd_leak(self): # Since we're opening a lot of FDs, we must be careful to avoid leaks: # we both check that calling fwalk() a large number of times doesn't # yield EMFILE, and that the minimum allocated FD hasn't changed.

AltStyle によって変換されたページ (->オリジナル) /