| --- a/Lib/os.py |
| +++ b/Lib/os.py |
| @@ -331,7 +331,16 @@ |
| __all__.extend(["makedirs", "removedirs", "renames"]) |
| -def walk(top, topdown=True, onerror=None, followlinks=False): |
| +def _isdir_dir_fd(path, dir_fd): |
| + """ |
| + A substitute for os.path.isdir that supports dir_fd. |
| + """ |
| + try: |
| + return st.S_ISDIR(stat(path, dir_fd=dir_fd).st_mode) |
| + except FileNotFoundError: |
| + return False |
|
storchaka
2012/06/25 14:08:10
Should onerror handler be used?
larry
2012/06/25 15:04:13
I have a much better implementation, so this one i
|
| + |
| +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 |
| @@ -395,36 +404,57 @@ |
| # always suppressed the exception then, rather than blow up for a |
| # minor reason when (say) a thousand readable directories are still |
| # left to visit. That logic is copied here. |
| + # |
| + # Note that listdir and error are globals in this module due |
| + # to earlier import-*. |
| try: |
| - # Note that listdir and error are globals in this module due |
| - # to earlier import-*. |
| - names = listdir(top) |
| + if dir_fd is None: |
| + close_me = None |
| + names = listdir(top) |
| + else: |
| + close_me = open(top, O_RDONLY, dir_fd=dir_fd) |
| + names = listdir(close_me) |
| except error as err: |
| + if close_me is not None: |
| + close(close_me) |
|
storchaka
2012/06/25 14:08:10
What if error happens on close?
larry
2012/06/25 15:04:13
Obviated by new implementation.
|
| if onerror is not None: |
| onerror(err) |
| return |
| dirs, nondirs = [], [] |
| - for name in names: |
| - if isdir(join(top, name)): |
| - dirs.append(name) |
| - else: |
| - nondirs.append(name) |
| + if close_me is not None: |
| + for name in names: |
| + # name is in directory referenced by close_me! |
| + # don't bother joining with top here. |
| + if _isdir_dir_fd(name, close_me): |
| + dirs.append(name) |
| + else: |
| + nondirs.append(name) |
| + close(close_me) |
| + else: |
| + for name in names: |
| + new_path = join(top, name) |
| + if isdir(new_path): |
| + dirs.append(name) |
| + else: |
| + nondirs.append(name) |
| if topdown: |
| yield top, dirs, nondirs |
| for name in dirs: |
| new_path = join(top, name) |
| if followlinks or not islink(new_path): |
|
storchaka
2012/06/25 14:08:10
st.S_ISLINK(stat(name, dir_fd=close_me).st_mode) ?
larry
2012/06/25 15:04:13
Obviated by new implementation.
|
| - yield from walk(new_path, topdown, onerror, followlinks) |
| + yield from walk(new_path, topdown, onerror, followlinks, dir_fd=dir_fd) |
| if not topdown: |
| yield top, dirs, nondirs |
| __all__.append("walk") |
| +if (listdir in supports_fd) and ({open, stat} <= supports_dir_fd): |
| + supports_dir_fd.add(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 +464,13 @@ |
| `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 +489,11 @@ |
| """ |
| # 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 +536,7 @@ |
| 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: |