Issue #23605: os.walk() now calls os.scandir() instead of os.listdir().

The usage of os.scandir() reduces the number of calls to os.stat().
Initial patch written by Ben Hoyt.
This commit is contained in:
Victor Stinner 2015-03-10 13:20:34 +01:00
parent 283f3f8ab3
commit 524a5ba111
3 changed files with 45 additions and 15 deletions

View File

@ -2618,6 +2618,11 @@ features:
for name in dirs:
os.rmdir(os.path.join(root, name))
.. versionchanged:: 3.5
The function now calls :func:`os.scandir` instead of :func:`os.listdir`.
The usage of :func:`os.scandir` reduces the number of calls to
:func:`os.stat`.
.. function:: fwalk(top='.', topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=None)

View File

@ -323,7 +323,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
the value of topdown, the list of subdirectories is retrieved before the
tuples for the directory and its subdirectories are generated.
By default errors from the os.listdir() call are ignored. If
By default errors from the os.scandir() call are ignored. If
optional arg 'onerror' is specified, it should be a function; it
will be called with one argument, an OSError instance. It can
report the error to continue with the walk, or raise the exception
@ -352,7 +352,9 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
"""
islink, join, isdir = path.islink, path.join, path.isdir
dirs = []
nondirs = []
symlinks = set()
# 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
@ -360,27 +362,46 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
# minor reason when (say) a thousand readable directories are still
# left to visit. That logic is copied here.
try:
# Note that listdir is global in this module due
# Note that scandir is global in this module due
# to earlier import-*.
names = listdir(top)
except OSError as err:
for entry in scandir(top):
try:
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:
dirs.append(entry.name)
try:
if entry.is_symlink():
symlinks.add(entry.name)
except OSError:
# If is_symlink() raises an OSError, consider that the
# entry is not a symbolik link, same behaviour than
# os.path.islink().
pass
else:
nondirs.append(entry.name)
except OSError as error:
# scandir() or iterating into scandir() iterator raised an OSError
if onerror is not None:
onerror(err)
onerror(error)
return
dirs, nondirs = [], []
for name in names:
if isdir(join(top, name)):
dirs.append(name)
else:
nondirs.append(name)
# Yield before recursion if going top down
if topdown:
yield top, dirs, nondirs
# Recurse into sub-directories
for name in dirs:
new_path = join(top, name)
if followlinks or not islink(new_path):
if followlinks or name not in symlinks:
new_path = path.join(top, name)
yield from walk(new_path, topdown, onerror, followlinks)
# Yield after recursion if going bottom up
if not topdown:
yield top, dirs, nondirs

View File

@ -13,6 +13,10 @@ Core and Builtins
Library
-------
- Issue #23605: os.walk() now calls os.scandir() instead of os.listdir().
The usage of os.scandir() reduces the number of calls to os.stat().
Initial patch written by Ben Hoyt.
What's New in Python 3.5 alpha 2?
=================================