Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit99f4691

Browse files
committed
gh-89727: Fix os.walk RecursionError on deep trees
Use a stack to implement os.walk iteratively instead of recursively toavoid hitting recursion limits on deeply nested trees.
1 parentb7e4f1d commit99f4691

File tree

2 files changed

+87
-74
lines changed

2 files changed

+87
-74
lines changed

‎Lib/os.py

Lines changed: 84 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -343,86 +343,96 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
343343
return_walk(fspath(top),topdown,onerror,followlinks)
344344

345345
def_walk(top,topdown,onerror,followlinks):
346-
dirs= []
347-
nondirs= []
348-
walk_dirs= []
349-
350-
# We may not have read permission for top, in which case we can't
351-
# get a list of the files the directory contains. os.walk
352-
# always suppressed the exception then, rather than blow up for a
353-
# minor reason when (say) a thousand readable directories are still
354-
# left to visit. That logic is copied here.
355-
try:
356-
# Note that scandir is global in this module due
357-
# to earlier import-*.
358-
scandir_it=scandir(top)
359-
exceptOSErroraserror:
360-
ifonerrorisnotNone:
361-
onerror(error)
362-
return
346+
stack= [(False,top)]
347+
whilestack:
348+
is_result,top=stack.pop()
349+
ifis_result:
350+
yieldtop
351+
continue
363352

364-
withscandir_it:
365-
whileTrue:
366-
try:
353+
dirs= []
354+
nondirs= []
355+
walk_dirs= []
356+
357+
# We may not have read permission for top, in which case we can't
358+
# get a list of the files the directory contains. os.walk
359+
# always suppressed the exception then, rather than blow up for a
360+
# minor reason when (say) a thousand readable directories are still
361+
# left to visit. That logic is copied here.
362+
try:
363+
# Note that scandir is global in this module due
364+
# to earlier import-*.
365+
scandir_it=scandir(top)
366+
exceptOSErroraserror:
367+
ifonerrorisnotNone:
368+
onerror(error)
369+
continue
370+
371+
cont=False
372+
withscandir_it:
373+
whileTrue:
367374
try:
368-
entry=next(scandir_it)
369-
exceptStopIteration:
375+
try:
376+
entry=next(scandir_it)
377+
exceptStopIteration:
378+
break
379+
exceptOSErroraserror:
380+
ifonerrorisnotNone:
381+
onerror(error)
382+
cont=True
370383
break
371-
exceptOSErroraserror:
372-
ifonerrorisnotNone:
373-
onerror(error)
374-
return
375384

376-
try:
377-
is_dir=entry.is_dir()
378-
exceptOSError:
379-
# If is_dir() raises an OSError, consider that the entry is not
380-
# a directory, same behaviour than os.path.isdir().
381-
is_dir=False
382-
383-
ifis_dir:
384-
dirs.append(entry.name)
385-
else:
386-
nondirs.append(entry.name)
385+
try:
386+
is_dir=entry.is_dir()
387+
exceptOSError:
388+
# If is_dir() raises an OSError, consider that the entry is not
389+
# a directory, same behaviour than os.path.isdir().
390+
is_dir=False
387391

388-
ifnottopdownandis_dir:
389-
# Bottom-up: recurse into sub-directory, but exclude symlinks to
390-
# directories if followlinks is False
391-
iffollowlinks:
392-
walk_into=True
392+
ifis_dir:
393+
dirs.append(entry.name)
393394
else:
394-
try:
395-
is_symlink=entry.is_symlink()
396-
exceptOSError:
397-
# If is_symlink() raises an OSError, consider that the
398-
# entry is not a symbolic link, same behaviour than
399-
# os.path.islink().
400-
is_symlink=False
401-
walk_into=notis_symlink
402-
403-
ifwalk_into:
404-
walk_dirs.append(entry.path)
405-
406-
# Yield before recursion if going top down
407-
iftopdown:
408-
yieldtop,dirs,nondirs
409-
410-
# Recurse into sub-directories
411-
islink,join=path.islink,path.join
412-
fordirnameindirs:
413-
new_path=join(top,dirname)
414-
# Issue #23605: os.path.islink() is used instead of caching
415-
# entry.is_symlink() result during the loop on os.scandir() because
416-
# the caller can replace the directory entry during the "yield"
417-
# above.
418-
iffollowlinksornotislink(new_path):
419-
yieldfrom_walk(new_path,topdown,onerror,followlinks)
420-
else:
421-
# Recurse into sub-directories
422-
fornew_pathinwalk_dirs:
423-
yieldfrom_walk(new_path,topdown,onerror,followlinks)
424-
# Yield after recursion if going bottom up
425-
yieldtop,dirs,nondirs
395+
nondirs.append(entry.name)
396+
397+
ifnottopdownandis_dir:
398+
# Bottom-up: traverse into sub-directory, but exclude symlinks to
399+
# directories if followlinks is False
400+
iffollowlinks:
401+
walk_into=True
402+
else:
403+
try:
404+
is_symlink=entry.is_symlink()
405+
exceptOSError:
406+
# If is_symlink() raises an OSError, consider that the
407+
# entry is not a symbolic link, same behaviour than
408+
# os.path.islink().
409+
is_symlink=False
410+
walk_into=notis_symlink
411+
412+
ifwalk_into:
413+
walk_dirs.append(entry.path)
414+
ifcont:
415+
continue
416+
417+
# Yield before sub-directory traversal if going top down
418+
iftopdown:
419+
yieldtop,dirs,nondirs
420+
# Traverse into sub-directories
421+
islink,join=path.islink,path.join
422+
fordirnameinreversed(dirs):
423+
new_path=join(top,dirname)
424+
# Issue #23605: os.path.islink() is used instead of caching
425+
# entry.is_symlink() result during the loop on os.scandir() because
426+
# the caller can replace the directory entry during the "yield"
427+
# above.
428+
iffollowlinksornotislink(new_path):
429+
stack.append((False,new_path))
430+
else:
431+
# Yield after sub-directory traversal if going bottom up
432+
stack.append((True, (top,dirs,nondirs)))
433+
# Traverse into sub-directories
434+
fornew_pathinreversed(walk_dirs):
435+
stack.append((False,new_path))
426436

427437
__all__.append("walk")
428438

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix issue with:func:`os.walk` where a:exc:`RecursionError` would occur on
2+
deep directory structures by adjusting the implementation of
3+
:func:`os._walk` to be iterative instead of recursive.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp