summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2012-06-25 11:49:05 (GMT)
committerLarry Hastings <larry@hastings.org>2012-06-25 11:49:05 (GMT)
commitc48fe98a7c3bb48fd689088baf3fedf9da7a8c70 (patch)
tree84126650691cd047e326368655e8fe5a9bd96101 /Lib
parent2a193a818a3a1e7ea8b184a068f39277c3468598 (diff)
downloadcpython-c48fe98a7c3bb48fd689088baf3fedf9da7a8c70.zip
cpython-c48fe98a7c3bb48fd689088baf3fedf9da7a8c70.tar.gz
cpython-c48fe98a7c3bb48fd689088baf3fedf9da7a8c70.tar.bz2
Issue #15177: Added dir_fd parameter to os.fwalk().
Diffstat (limited to 'Lib')
-rw-r--r--Lib/os.py18
-rw-r--r--Lib/test/test_os.py29
2 files changed, 35 insertions, 12 deletions
diff --git a/Lib/os.py b/Lib/os.py
index f990627..381737f 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -422,9 +422,9 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
__all__.append("walk")
-if open in supports_dir_fd:
+if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
- def fwalk(top, topdown=True, onerror=None, followlinks=False):
+ def fwalk(top=".", topdown=True, onerror=None, followlinks=False, *, dir_fd=None):
"""Directory tree generator.
This behaves exactly like walk(), except that it yields a 4-tuple
@@ -434,9 +434,13 @@ if open in supports_dir_fd:
`dirpath`, `dirnames` and `filenames` are identical to walk() output,
and `dirfd` is a file descriptor referring to the directory `dirpath`.
- The advantage of walkfd() over walk() is that it's safe against symlink
+ The advantage of fwalk() over walk() is that it's safe against symlink
races (when followlinks is False).
+ 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 is always supported for fwalk.)
+
Caution:
Since fwalk() yields file descriptors, those are only valid until the
next iteration step, so you should dup() them if you want to keep them
@@ -455,11 +459,11 @@ if open in supports_dir_fd:
"""
# Note: To guard against symlink races, we use the standard
# lstat()/open()/fstat() trick.
- orig_st = lstat(top)
- topfd = open(top, O_RDONLY)
+ orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd)
+ topfd = open(top, O_RDONLY, dir_fd=dir_fd)
try:
if (followlinks or (st.S_ISDIR(orig_st.st_mode) and
- path.samestat(orig_st, fstat(topfd)))):
+ path.samestat(orig_st, stat(topfd)))):
yield from _fwalk(topfd, top, topdown, onerror, followlinks)
finally:
close(topfd)
@@ -502,7 +506,7 @@ if open in supports_dir_fd:
onerror(err)
return
try:
- if followlinks or path.samestat(orig_st, fstat(dirfd)):
+ if followlinks or path.samestat(orig_st, stat(dirfd)):
dirpath = path.join(toppath, name)
yield from _fwalk(dirfd, dirpath, topdown, onerror, followlinks)
finally:
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index 62a7dad..57de993 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -741,19 +741,38 @@ class WalkTests(unittest.TestCase):
class FwalkTests(WalkTests):
"""Tests for os.fwalk()."""
- def test_compare_to_walk(self):
- # compare with walk() results
+ def _compare_to_walk(self, walk_kwargs, fwalk_kwargs):
+ """
+ compare with walk() results.
+ """
for topdown, followlinks in itertools.product((True, False), repeat=2):
- args = support.TESTFN, topdown, None, followlinks
+ d = {'topdown': topdown, 'followlinks': followlinks}
+ walk_kwargs.update(d)
+ fwalk_kwargs.update(d)
+
expected = {}
- for root, dirs, files in os.walk(*args):
+ for root, dirs, files in os.walk(**walk_kwargs):
expected[root] = (set(dirs), set(files))
- for root, dirs, files, rootfd in os.fwalk(*args):
+ for root, dirs, files, rootfd in os.fwalk(**fwalk_kwargs):
self.assertIn(root, expected)
self.assertEqual(expected[root], (set(dirs), set(files)))
+ def test_compare_to_walk(self):
+ kwargs = {'top': support.TESTFN}
+ self._compare_to_walk(kwargs, kwargs)
+
def test_dir_fd(self):
+ try:
+ fd = os.open(".", os.O_RDONLY)
+ walk_kwargs = {'top': support.TESTFN}
+ fwalk_kwargs = walk_kwargs.copy()
+ fwalk_kwargs['dir_fd'] = fd
+ self._compare_to_walk(walk_kwargs, fwalk_kwargs)
+ finally:
+ os.close(fd)
+
+ def test_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