@@ -110,6 +110,7 @@ def _add(str, fn):
110
110
_add ("HAVE_FCHMODAT" , "chmod" )
111
111
_add ("HAVE_FCHOWNAT" , "chown" )
112
112
_add ("HAVE_FSTATAT" , "stat" )
113
+ _add ("HAVE_LSTAT" , "lstat" )
113
114
_add ("HAVE_FUTIMESAT" , "utime" )
114
115
_add ("HAVE_LINKAT" , "link" )
115
116
_add ("HAVE_MKDIRAT" , "mkdir" )
@@ -131,6 +132,7 @@ def _add(str, fn):
131
132
_set = set ()
132
133
_add ("HAVE_FCHDIR" , "chdir" )
133
134
_add ("HAVE_FCHMOD" , "chmod" )
135
+ _add ("MS_WINDOWS" , "chmod" )
134
136
_add ("HAVE_FCHOWN" , "chown" )
135
137
_add ("HAVE_FDOPENDIR" , "listdir" )
136
138
_add ("HAVE_FDOPENDIR" , "scandir" )
@@ -171,6 +173,7 @@ def _add(str, fn):
171
173
_add ("HAVE_FSTATAT" , "stat" )
172
174
_add ("HAVE_LCHFLAGS" , "chflags" )
173
175
_add ("HAVE_LCHMOD" , "chmod" )
176
+ _add ("MS_WINDOWS" , "chmod" )
174
177
if _exists ("lchown" ): # mac os x10.3
175
178
_add ("HAVE_LCHOWN" , "chown" )
176
179
_add ("HAVE_LINKAT" , "link" )
@@ -279,6 +282,10 @@ def renames(old, new):
279
282
280
283
__all__ .extend (["makedirs" , "removedirs" , "renames" ])
281
284
285
+ # Private sentinel that makes walk() classify all symlinks and junctions as
286
+ # regular files.
287
+ _walk_symlinks_as_files = object ()
288
+
282
289
def walk (top , topdown = True , onerror = None , followlinks = False ):
283
290
"""Directory tree generator.
284
291
@@ -331,12 +338,12 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
331
338
332
339
import os
333
340
from os.path import join, getsize
334
- for root, dirs, files in os.walk('python/Lib/email '):
341
+ for root, dirs, files in os.walk('python/Lib/xml '):
335
342
print(root, "consumes ")
336
343
print(sum(getsize(join(root, name)) for name in files), end=" ")
337
344
print("bytes in", len(files), "non-directory files")
338
- if 'CVS ' in dirs:
339
- dirs.remove('CVS ') # don't visit CVS directories
345
+ if '__pycache__ ' in dirs:
346
+ dirs.remove('__pycache__ ') # don't visit __pycache__ directories
340
347
341
348
"""
342
349
sys .audit ("os.walk" , top , topdown , onerror , followlinks )
@@ -380,7 +387,10 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
380
387
break
381
388
382
389
try :
383
- is_dir = entry .is_dir ()
390
+ if followlinks is _walk_symlinks_as_files :
391
+ is_dir = entry .is_dir (follow_symlinks = False ) and not entry .is_junction ()
392
+ else :
393
+ is_dir = entry .is_dir ()
384
394
except OSError :
385
395
# If is_dir() raises an OSError, consider the entry not to
386
396
# be a directory, same behaviour as os.path.isdir().
@@ -459,34 +469,69 @@ def fwalk(top=".", topdown=True, onerror=None, *, follow_symlinks=False, dir_fd=
459
469
Example:
460
470
461
471
import os
462
- for root, dirs, files, rootfd in os.fwalk('python/Lib/email '):
472
+ for root, dirs, files, rootfd in os.fwalk('python/Lib/xml '):
463
473
print(root, "consumes", end="")
464
474
print(sum(os.stat(name, dir_fd=rootfd).st_size for name in files),
465
475
end="")
466
476
print("bytes in", len(files), "non-directory files")
467
- if 'CVS ' in dirs:
468
- dirs.remove('CVS ') # don't visit CVS directories
477
+ if '__pycache__ ' in dirs:
478
+ dirs.remove('__pycache__ ') # don't visit __pycache__ directories
469
479
"""
470
480
sys .audit ("os.fwalk" , top , topdown , onerror , follow_symlinks , dir_fd )
471
481
top = fspath (top )
472
- # Note: To guard against symlink races, we use the standard
473
- # lstat()/open()/fstat() trick.
474
- if not follow_symlinks :
475
- orig_st = stat (top , follow_symlinks = False , dir_fd = dir_fd )
476
- topfd = open (top , O_RDONLY | O_NONBLOCK , dir_fd = dir_fd )
482
+ stack = [(_fwalk_walk , (True , dir_fd , top , top , None ))]
483
+ isbytes = isinstance (top , bytes )
477
484
try :
478
- if (follow_symlinks or (st .S_ISDIR (orig_st .st_mode ) and
479
- path .samestat (orig_st , stat (topfd )))):
480
- yield from _fwalk (topfd , top , isinstance (top , bytes ),
481
- topdown , onerror , follow_symlinks )
485
+ while stack :
486
+ yield from _fwalk (stack , isbytes , topdown , onerror , follow_symlinks )
482
487
finally :
483
- close (topfd )
484
-
485
- def _fwalk (topfd , toppath , isbytes , topdown , onerror , follow_symlinks ):
488
+ # Close any file descriptors still on the stack.
489
+ while stack :
490
+ action , value = stack .pop ()
491
+ if action == _fwalk_close :
492
+ close (value )
493
+
494
+ # Each item in the _fwalk() stack is a pair (action, args).
495
+ _fwalk_walk = 0 # args: (isroot, dirfd, toppath, topname, entry)
496
+ _fwalk_yield = 1 # args: (toppath, dirnames, filenames, topfd)
497
+ _fwalk_close = 2 # args: dirfd
498
+
499
+ def _fwalk (stack , isbytes , topdown , onerror , follow_symlinks ):
486
500
# Note: This uses O(depth of the directory tree) file descriptors: if
487
501
# necessary, it can be adapted to only require O(1) FDs, see issue
488
502
# #13734.
489
503
504
+ action , value = stack .pop ()
505
+ if action == _fwalk_close :
506
+ close (value )
507
+ return
508
+ elif action == _fwalk_yield :
509
+ yield value
510
+ return
511
+ assert action == _fwalk_walk
512
+ isroot , dirfd , toppath , topname , entry = value
513
+ try :
514
+ if not follow_symlinks :
515
+ # Note: To guard against symlink races, we use the standard
516
+ # lstat()/open()/fstat() trick.
517
+ if entry is None :
518
+ orig_st = stat (topname , follow_symlinks = False , dir_fd = dirfd )
519
+ else :
520
+ orig_st = entry .stat (follow_symlinks = False )
521
+ topfd = open (topname , O_RDONLY | O_NONBLOCK , dir_fd = dirfd )
522
+ except OSError as err :
523
+ if isroot :
524
+ raise
525
+ if onerror is not None :
526
+ onerror (err )
527
+ return
528
+ stack .append ((_fwalk_close , topfd ))
529
+ if not follow_symlinks :
530
+ if isroot and not st .S_ISDIR (orig_st .st_mode ):
531
+ return
532
+ if not path .samestat (orig_st , stat (topfd )):
533
+ return
534
+
490
535
scandir_it = scandir (topfd )
491
536
dirs = []
492
537
nondirs = []
@@ -512,31 +557,18 @@ def _fwalk(topfd, toppath, isbytes, topdown, onerror, follow_symlinks):
512
557
513
558
if topdown :
514
559
yield toppath , dirs , nondirs , topfd
560
+ else :
561
+ stack .append ((_fwalk_yield , (toppath , dirs , nondirs , topfd )))
515
562
516
- for name in dirs if entries is None else zip (dirs , entries ):
517
- try :
518
- if not follow_symlinks :
519
- if topdown :
520
- orig_st = stat (name , dir_fd = topfd , follow_symlinks = False )
521
- else :
522
- assert entries is not None
523
- name , entry = name
524
- orig_st = entry .stat (follow_symlinks = False )
525
- dirfd = open (name , O_RDONLY | O_NONBLOCK , dir_fd = topfd )
526
- except OSError as err :
527
- if onerror is not None :
528
- onerror (err )
529
- continue
530
- try :
531
- if follow_symlinks or path .samestat (orig_st , stat (dirfd )):
532
- dirpath = path .join (toppath , name )
533
- yield from _fwalk (dirfd , dirpath , isbytes ,
534
- topdown , onerror , follow_symlinks )
535
- finally :
536
- close (dirfd )
537
-
538
- if not topdown :
539
- yield toppath , dirs , nondirs , topfd
563
+ toppath = path .join (toppath , toppath [:0 ]) # Add trailing slash.
564
+ if entries is None :
565
+ stack .extend (
566
+ (_fwalk_walk , (False , topfd , toppath + name , name , None ))
567
+ for name in dirs [::- 1 ])
568
+ else :
569
+ stack .extend (
570
+ (_fwalk_walk , (False , topfd , toppath + name , name , entry ))
571
+ for name , entry in zip (dirs [::- 1 ], entries [::- 1 ]))
540
572
541
573
__all__ .append ("fwalk" )
542
574
@@ -1061,6 +1093,12 @@ def _fspath(path):
1061
1093
else :
1062
1094
raise TypeError ("expected str, bytes or os.PathLike object, "
1063
1095
"not " + path_type .__name__ )
1096
+ except TypeError :
1097
+ if path_type .__fspath__ is None :
1098
+ raise TypeError ("expected str, bytes or os.PathLike object, "
1099
+ "not " + path_type .__name__ ) from None
1100
+ else :
1101
+ raise
1064
1102
if isinstance (path_repr , (str , bytes )):
1065
1103
return path_repr
1066
1104
else :
@@ -1079,6 +1117,8 @@ class PathLike(abc.ABC):
1079
1117
1080
1118
"""Abstract base class for implementing the file system path protocol."""
1081
1119
1120
+ __slots__ = ()
1121
+
1082
1122
@abc .abstractmethod
1083
1123
def __fspath__ (self ):
1084
1124
"""Return the file system path representation of the object."""
@@ -1128,3 +1168,17 @@ def add_dll_directory(path):
1128
1168
cookie ,
1129
1169
nt ._remove_dll_directory
1130
1170
)
1171
+
1172
+
1173
+ if _exists ('sched_getaffinity' ) and sys ._get_cpu_count_config () < 0 :
1174
+ def process_cpu_count ():
1175
+ """
1176
+ Get the number of CPUs of the current process.
1177
+
1178
+ Return the number of logical CPUs usable by the calling thread of the
1179
+ current process. Return None if indeterminable.
1180
+ """
1181
+ return len (sched_getaffinity (0 ))
1182
+ else :
1183
+ # Just an alias to cpu_count() (same docstring)
1184
+ process_cpu_count = cpu_count
0 commit comments