@@ -197,7 +197,7 @@ def prepare_path(pth):
197197 if not isinstance (pth , bytes ):
198198 pth = fsencode (pth )
199199 return pth
200- else :
200+ else :
201201 if not isinstance (pth , compat .unicode ):
202202 return fsdecode (pth )
203203 return pth
@@ -365,16 +365,19 @@ def ignore_nothing(_):
365365 return False
366366
367367
368- def walk (location , ignored = None ):
368+ def walk (location , ignored = None , follow_symlinks = False ):
369369 """
370370 Walk location returning the same tuples as os.walk but with a different
371371 behavior:
372372 - always walk top-down, breadth-first.
373- - always ignore and never follow symlinks,
373+ - always ignore and never follow symlinks (unless `follow_symlinks` is True) ,
374374 - always ignore special files (FIFOs, etc.)
375375 - optionally ignore files and directories by invoking the `ignored`
376376 callable on files and directories returning True if it should be ignored.
377377 - location is a directory or a file: for a file, the file is returned.
378+
379+ If `follow_symlinks` is True, then symlinks will not be ignored and be
380+ collected like regular files and directories
378381 """
379382 if on_linux and py2 :
380383 location = fsencode (location )
@@ -387,33 +390,38 @@ def walk(location, ignored=None):
387390 logger_debug ('walk: ignored:' , location , is_ignored )
388391 return
389392
390- if filetype .is_file (location ) :
393+ if filetype .is_file (location , follow_symlinks = follow_symlinks ) :
391394 yield parent_directory (location ), [], [file_name (location )]
392395
393- elif filetype .is_dir (location ):
396+ elif filetype .is_dir (location , follow_symlinks = follow_symlinks ):
394397 dirs = []
395398 files = []
396399 # TODO: consider using scandir
397400 for name in os .listdir (location ):
398401 loc = os .path .join (location , name )
399402 if filetype .is_special (loc ) or (ignored and ignored (loc )):
400- if TRACE :
401- ign = ignored and ignored (loc )
402- logger_debug ('walk: ignored:' , loc , ign )
403- continue
403+ if (follow_symlinks
404+ and filetype .is_link (loc )
405+ and not filetype .is_broken_link (location )):
406+ pass
407+ else :
408+ if TRACE :
409+ ign = ignored and ignored (loc )
410+ logger_debug ('walk: ignored:' , loc , ign )
411+ continue
404412 # special files and symlinks are always ignored
405- if filetype .is_dir (loc ):
413+ if filetype .is_dir (loc , follow_symlinks = follow_symlinks ):
406414 dirs .append (name )
407- elif filetype .is_file (loc ):
415+ elif filetype .is_file (loc , follow_symlinks = follow_symlinks ):
408416 files .append (name )
409417 yield location , dirs , files
410418
411419 for dr in dirs :
412- for tripple in walk (os .path .join (location , dr ), ignored ):
420+ for tripple in walk (os .path .join (location , dr ), ignored , follow_symlinks = follow_symlinks ):
413421 yield tripple
414422
415423
416- def resource_iter (location , ignored = ignore_nothing , with_dirs = True ):
424+ def resource_iter (location , ignored = ignore_nothing , with_dirs = True , follow_symlinks = False ):
417425 """
418426 Return an iterable of paths at `location` recursively.
419427
@@ -424,7 +432,7 @@ def resource_iter(location, ignored=ignore_nothing, with_dirs=True):
424432 """
425433 if on_linux and py2 :
426434 location = fsencode (location )
427- for top , dirs , files in walk (location , ignored ):
435+ for top , dirs , files in walk (location , ignored , follow_symlinks = follow_symlinks ):
428436 if with_dirs :
429437 for d in dirs :
430438 yield os .path .join (top , d )
0 commit comments