@@ -340,89 +340,95 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
340
340
341
341
"""
342
342
sys .audit ("os.walk" , top , topdown , onerror , followlinks )
343
- return _walk (fspath (top ), topdown , onerror , followlinks )
344
-
345
- 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
- except OSError as error :
360
- if onerror is not None :
361
- onerror (error )
362
- return
363
343
364
- with scandir_it :
365
- while True :
366
- try :
344
+ stack = [(False , fspath (top ))]
345
+ islink , join = path .islink , path .join
346
+ while stack :
347
+ must_yield , top = stack .pop ()
348
+ if must_yield :
349
+ yield top
350
+ continue
351
+
352
+ dirs = []
353
+ nondirs = []
354
+ walk_dirs = []
355
+
356
+ # We may not have read permission for top, in which case we can't
357
+ # get a list of the files the directory contains.
358
+ # We suppress the exception here, rather than blow up for a
359
+ # minor reason when (say) a thousand readable directories are still
360
+ # left to visit.
361
+ try :
362
+ scandir_it = scandir (top )
363
+ except OSError as error :
364
+ if onerror is not None :
365
+ onerror (error )
366
+ continue
367
+
368
+ cont = False
369
+ with scandir_it :
370
+ while True :
367
371
try :
368
- entry = next (scandir_it )
369
- except StopIteration :
372
+ try :
373
+ entry = next (scandir_it )
374
+ except StopIteration :
375
+ break
376
+ except OSError as error :
377
+ if onerror is not None :
378
+ onerror (error )
379
+ cont = True
370
380
break
371
- except OSError as error :
372
- if onerror is not None :
373
- onerror (error )
374
- return
375
381
376
- try :
377
- is_dir = entry .is_dir ()
378
- except OSError :
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
- if is_dir :
384
- dirs .append (entry .name )
385
- else :
386
- nondirs .append (entry .name )
382
+ try :
383
+ is_dir = entry .is_dir ()
384
+ except OSError :
385
+ # If is_dir() raises an OSError, consider the entry not to
386
+ # be a directory, same behaviour as os.path.isdir().
387
+ is_dir = False
387
388
388
- if not topdown and is_dir :
389
- # Bottom-up: recurse into sub-directory, but exclude symlinks to
390
- # directories if followlinks is False
391
- if followlinks :
392
- walk_into = True
389
+ if is_dir :
390
+ dirs .append (entry .name )
393
391
else :
394
- try :
395
- is_symlink = entry .is_symlink ()
396
- except OSError :
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 = not is_symlink
402
-
403
- if walk_into :
404
- walk_dirs .append (entry .path )
405
-
406
- # Yield before recursion if going top down
407
- if topdown :
408
- yield top , dirs , nondirs
409
-
410
- # Recurse into sub-directories
411
- islink , join = path .islink , path .join
412
- for dirname in dirs :
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
- if followlinks or not islink (new_path ):
419
- yield from _walk (new_path , topdown , onerror , followlinks )
420
- else :
421
- # Recurse into sub-directories
422
- for new_path in walk_dirs :
423
- yield from _walk (new_path , topdown , onerror , followlinks )
424
- # Yield after recursion if going bottom up
425
- yield top , dirs , nondirs
392
+ nondirs .append (entry .name )
393
+
394
+ if not topdown and is_dir :
395
+ # Bottom-up: traverse into sub-directory, but exclude
396
+ # symlinks to directories if followlinks is False
397
+ if followlinks :
398
+ walk_into = True
399
+ else :
400
+ try :
401
+ is_symlink = entry .is_symlink ()
402
+ except OSError :
403
+ # If is_symlink() raises an OSError, consider the
404
+ # entry not to be a symbolic link, same behaviour
405
+ # as os.path.islink().
406
+ is_symlink = False
407
+ walk_into = not is_symlink
408
+
409
+ if walk_into :
410
+ walk_dirs .append (entry .path )
411
+ if cont :
412
+ continue
413
+
414
+ if topdown :
415
+ # Yield before sub-directory traversal if going top down
416
+ yield top , dirs , nondirs
417
+ # Traverse into sub-directories
418
+ for dirname in reversed (dirs ):
419
+ new_path = join (top , dirname )
420
+ # bpo-23605: os.path.islink() is used instead of caching
421
+ # entry.is_symlink() result during the loop on os.scandir() because
422
+ # the caller can replace the directory entry during the "yield"
423
+ # above.
424
+ if followlinks or not islink (new_path ):
425
+ stack .append ((False , new_path ))
426
+ else :
427
+ # Yield after sub-directory traversal if going bottom up
428
+ stack .append ((True , (top , dirs , nondirs )))
429
+ # Traverse into sub-directories
430
+ for new_path in reversed (walk_dirs ):
431
+ stack .append ((False , new_path ))
426
432
427
433
__all__ .append ("walk" )
428
434
0 commit comments