@@ -476,24 +476,52 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=
476
476
"""
477
477
sys .audit ("os.fwalk" , top , topdown , onerror , follow_symlinks , dir_fd )
478
478
top = fspath (top )
479
- # Note: To guard against symlink races, we use the standard
480
- # lstat()/open()/fstat() trick.
481
- if not follow_symlinks :
482
- orig_st = stat (top , follow_symlinks = False , dir_fd = dir_fd )
483
- topfd = open (top , O_RDONLY | O_NONBLOCK , dir_fd = dir_fd )
484
- try :
485
- if (follow_symlinks or (st .S_ISDIR (orig_st .st_mode ) and
486
- path .samestat (orig_st , stat (topfd )))):
487
- yield from _fwalk (topfd , top , isinstance (top , bytes ),
488
- topdown , onerror , follow_symlinks )
489
- finally :
490
- close (topfd )
491
-
492
- def _fwalk (topfd , toppath , isbytes , topdown , onerror , follow_symlinks ):
479
+ stack = [(_fwalk_walk , (True , dir_fd , top , top , None ))]
480
+ isbytes = isinstance (top , bytes )
481
+ while stack :
482
+ yield from _fwalk (stack , isbytes , topdown , onerror , follow_symlinks )
483
+
484
+ # Each item in the _fwalk() stack is a pair (action, args).
485
+ _fwalk_walk = 0 # args: (isroot, dirfd, toppath, topname, entry)
486
+ _fwalk_yield = 1 # args: (toppath, dirnames, filenames, topfd)
487
+ _fwalk_close = 2 # args: dirfd
488
+
489
+ def _fwalk (stack , isbytes , topdown , onerror , follow_symlinks ):
493
490
# Note: This uses O(depth of the directory tree) file descriptors: if
494
491
# necessary, it can be adapted to only require O(1) FDs, see issue
495
492
# #13734.
496
493
494
+ action , value = stack .pop ()
495
+ if action == _fwalk_close :
496
+ close (value )
497
+ return
498
+ elif action == _fwalk_yield :
499
+ yield value
500
+ return
501
+ assert action == _fwalk_walk
502
+ isroot , dirfd , toppath , topname , entry = value
503
+ try :
504
+ if not follow_symlinks :
505
+ # Note: To guard against symlink races, we use the standard
506
+ # lstat()/open()/fstat() trick.
507
+ if entry is None :
508
+ orig_st = stat (topname , follow_symlinks = False , dir_fd = dirfd )
509
+ else :
510
+ orig_st = entry .stat (follow_symlinks = False )
511
+ topfd = open (topname , O_RDONLY | O_NONBLOCK , dir_fd = dirfd )
512
+ except OSError as err :
513
+ if isroot :
514
+ raise
515
+ if onerror is not None :
516
+ onerror (err )
517
+ return
518
+ stack .append ((_fwalk_close , topfd ))
519
+ if not follow_symlinks :
520
+ if isroot and not st .S_ISDIR (orig_st .st_mode ):
521
+ return
522
+ if not path .samestat (orig_st , stat (topfd )):
523
+ return
524
+
497
525
scandir_it = scandir (topfd )
498
526
dirs = []
499
527
nondirs = []
@@ -519,31 +547,18 @@ def _fwalk(topfd, toppath, isbytes, topdown, onerror, follow_symlinks):
519
547
520
548
if topdown :
521
549
yield toppath , dirs , nondirs , topfd
550
+ else :
551
+ stack .append ((_fwalk_yield , (toppath , dirs , nondirs , topfd )))
522
552
523
- for name in dirs if entries is None else zip (dirs , entries ):
524
- try :
525
- if not follow_symlinks :
526
- if topdown :
527
- orig_st = stat (name , dir_fd = topfd , follow_symlinks = False )
528
- else :
529
- assert entries is not None
530
- name , entry = name
531
- orig_st = entry .stat (follow_symlinks = False )
532
- dirfd = open (name , O_RDONLY | O_NONBLOCK , dir_fd = topfd )
533
- except OSError as err :
534
- if onerror is not None :
535
- onerror (err )
536
- continue
537
- try :
538
- if follow_symlinks or path .samestat (orig_st , stat (dirfd )):
539
- dirpath = path .join (toppath , name )
540
- yield from _fwalk (dirfd , dirpath , isbytes ,
541
- topdown , onerror , follow_symlinks )
542
- finally :
543
- close (dirfd )
544
-
545
- if not topdown :
546
- yield toppath , dirs , nondirs , topfd
553
+ toppath = path .join (toppath , toppath [:0 ]) # Add trailing slash.
554
+ if entries is None :
555
+ stack .extend (
556
+ (_fwalk_walk , (False , topfd , toppath + name , name , None ))
557
+ for name in dirs [::- 1 ])
558
+ else :
559
+ stack .extend (
560
+ (_fwalk_walk , (False , topfd , toppath + name , name , entry ))
561
+ for name , entry in zip (dirs [::- 1 ], entries [::- 1 ]))
547
562
548
563
__all__ .append ("fwalk" )
549
564
0 commit comments