@@ -343,86 +343,96 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
343
343
return _walk (fspath (top ), topdown , onerror , followlinks )
344
344
345
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
346
+ stack = [(False , top )]
347
+ while stack :
348
+ is_result , top = stack .pop ()
349
+ if is_result :
350
+ yield top
351
+ continue
363
352
364
- with scandir_it :
365
- while True :
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
+ except OSError as error :
367
+ if onerror is not None :
368
+ onerror (error )
369
+ continue
370
+
371
+ cont = False
372
+ with scandir_it :
373
+ while True :
367
374
try :
368
- entry = next (scandir_it )
369
- except StopIteration :
375
+ try :
376
+ entry = next (scandir_it )
377
+ except StopIteration :
378
+ break
379
+ except OSError as error :
380
+ if onerror is not None :
381
+ onerror (error )
382
+ cont = True
370
383
break
371
- except OSError as error :
372
- if onerror is not None :
373
- onerror (error )
374
- return
375
384
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 )
385
+ try :
386
+ is_dir = entry .is_dir ()
387
+ except OSError :
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
387
391
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
392
+ if is_dir :
393
+ dirs .append (entry .name )
393
394
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
395
+ nondirs .append (entry .name )
396
+
397
+ if not topdown and is_dir :
398
+ # Bottom-up: traverse into sub-directory, but exclude symlinks to
399
+ # directories if followlinks is False
400
+ if followlinks :
401
+ walk_into = True
402
+ else :
403
+ try :
404
+ is_symlink = entry .is_symlink ()
405
+ except OSError :
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 = not is_symlink
411
+
412
+ if walk_into :
413
+ walk_dirs .append (entry .path )
414
+ if cont :
415
+ continue
416
+
417
+ # Yield before sub-directory traversal if going top down
418
+ if topdown :
419
+ yield top , dirs , nondirs
420
+ # Traverse into sub-directories
421
+ islink , join = path .islink , path .join
422
+ for dirname in reversed (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
+ if followlinks or not islink (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
+ for new_path in reversed (walk_dirs ):
435
+ stack .append ((False , new_path ))
426
436
427
437
__all__ .append ("walk" )
428
438
0 commit comments