Kaydet (Commit) 7fea974b authored tarafından Victor Stinner's avatar Victor Stinner

Issue #23605: Fix os.walk(topdown=True), don't cache entry.is_symlink() because

the caller can replace the directory with a different file kind.

The bottom-up way, os.walk(topdown=False), still uses entry.is_symlink(), and
so can be faster than Python 3.4.
üst a47fc5c2
...@@ -354,7 +354,6 @@ def walk(top, topdown=True, onerror=None, followlinks=False): ...@@ -354,7 +354,6 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
dirs = [] dirs = []
nondirs = [] nondirs = []
symlinks = set()
# We may not have read permission for top, in which case we can't # We may not have read permission for top, in which case we can't
# get a list of the files the directory contains. os.walk # get a list of the files the directory contains. os.walk
...@@ -364,46 +363,69 @@ def walk(top, topdown=True, onerror=None, followlinks=False): ...@@ -364,46 +363,69 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
try: try:
# Note that scandir is global in this module due # Note that scandir is global in this module due
# to earlier import-*. # to earlier import-*.
for entry in scandir(top): scandir_it = scandir(top)
except OSError as error:
if onerror is not None:
onerror(error)
return
while True:
try:
try: try:
is_dir = entry.is_dir() entry = next(scandir_it)
except OSError: except StopIteration:
# If is_dir() raises an OSError, consider that the entry is not break
# a directory, same behaviour than os.path.isdir(). except OSError as error:
is_dir = False if onerror is not None:
onerror(error)
if is_dir: return
dirs.append(entry.name)
else: try:
nondirs.append(entry.name) is_dir = entry.is_dir()
except OSError:
# If is_dir() raises an OSError, consider that the entry is not
# a directory, same behaviour than os.path.isdir().
is_dir = False
if is_dir and not followlinks: if is_dir:
dirs.append(entry.name)
else:
nondirs.append(entry.name)
if not topdown and is_dir:
# Bottom-up: recurse into sub-directory, but exclude symlinks to
# directories if followlinks is False
if followlinks:
walk_into = True
else:
try: try:
if entry.is_symlink(): is_symlink = entry.is_symlink()
symlinks.add(entry.name)
except OSError: except OSError:
# If is_symlink() raises an OSError, consider that the # If is_symlink() raises an OSError, consider that the
# entry is not a symbolik link, same behaviour than # entry is not a symbolik link, same behaviour than
# os.path.islink(). # os.path.islink().
pass is_symlink = False
except OSError as error: walk_into = not is_symlink
# scandir() or iterating into scandir() iterator raised an OSError
if onerror is not None: if walk_into:
onerror(error) yield from walk(entry.path, topdown, onerror, followlinks)
return
# Yield before recursion if going top down # Yield before recursion if going top down
if topdown: if topdown:
yield top, dirs, nondirs yield top, dirs, nondirs
# Recurse into sub-directories # Recurse into sub-directories
for name in dirs: islink, join = path.islink, path.join
if followlinks or name not in symlinks: for name in dirs:
new_path = path.join(top, name) new_path = join(top, name)
yield from walk(new_path, topdown, onerror, followlinks) # Issue #23605: os.path.islink() is used instead of caching
# entry.is_symlink() result during the loop on os.scandir() because
# Yield after recursion if going bottom up # the caller can replace the directory entry during the "yield"
if not topdown: # above.
if followlinks or not islink(new_path):
yield from walk(new_path, topdown, onerror, followlinks)
else:
# Yield after recursion if going bottom up
yield top, dirs, nondirs yield top, dirs, nondirs
__all__.append("walk") __all__.append("walk")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment