55from pathlib import Path
66from glob import glob
77import itertools
8+ from more_itertools import intersperse
89import logging
910from functools import lru_cache
1011import shutil
@@ -390,26 +391,50 @@ def mime(path: PathIsh) -> Optional[str]:
390391 return magic (ps )
391392
392393
393- def find_args (root : Path , follow : bool ) -> List [str ]:
394+ def find_args (root : Path , follow : bool , ignore : List [str ]= []) -> List [str ]:
395+ prune_dir_args = []
396+ ignore_file_args = []
397+ if ignore :
398+ # -name {name} for all the file/directories in ignore
399+ ignore_names = [['-name' , n ] for n in ignore ]
400+ # OR (-o) all the names together and flatten
401+ ignore_names_l = list (itertools .chain (* intersperse (['-o' ], ignore_names )))
402+ # Prune all of those directories, and make the entire clause evaluate to false
403+ # (so that it doesn't match anything and make find print)
404+ prune_dir_args = ['-type' , 'd' , '-a' , '(' , * ignore_names_l , ')' , '-prune' , '-false' , '-o' ]
405+ # Also ignore any files with the names as well
406+ ignore_file_args = ['-a' , '-not' , '(' , * ignore_names_l , ')' ]
407+
394408 return [
395409 * (['-L' ] if follow else []),
396410 str (root ),
411+ * prune_dir_args ,
397412 '-type' , 'f' ,
413+ * ignore_file_args
398414 ]
399415
400416
401- def fdfind_args (root : Path , follow : bool ) -> List [str ]:
417+ def fdfind_args (root : Path , follow : bool , ignore : List [ str ] = [] ) -> List [str ]:
402418 from .config import extra_fd_args
419+
420+ ignore_args = []
421+ if ignore :
422+ # Add a statment that excludes the folder
423+ ignore_args = [['--exclude' , f'{ n } ' ] for n in ignore ]
424+ # Flatten the list of lists
425+ ignore_args_l = list (itertools .chain (* ignore_args ))
426+
403427 return [
404428 * extra_fd_args (),
429+ * ignore_args_l ,
405430 * (['--follow' ] if follow else []),
406431 '--type' , 'f' ,
407432 '.' ,
408433 str (root ),
409434 ]
410435
411436
412- def traverse (root : Path , * , follow : bool = True ) -> Iterable [Path ]:
437+ def traverse (root : Path , * , follow : bool = True , ignore : List [ str ] = [] ) -> Iterable [Path ]:
413438 if not root .is_dir ():
414439 yield root
415440 return
@@ -418,16 +443,20 @@ def traverse(root: Path, *, follow: bool=True) -> Iterable[Path]:
418443 if _is_windows :
419444 # on windows could use 'forfiles'... but probably easier not to bother for now
420445 # todo coild use followlinks=True? walk could end up in infinite loop?
421- for r , _ , files in os .walk (root ):
422- yield from (Path (r ) / f for f in files )
446+ for r , dirs , files in os .walk (root ):
447+ # Remove dirs specified in ignore (clone dirs() as we have to remove in place)
448+ for i , d in enumerate (list (dirs )):
449+ if d in ignore :
450+ del dirs [i ]
451+ yield from (Path (r ) / f for f in files if f not in ignore )
423452 return
424453
425454 from .compat import Popen , PIPE
426- cmd = ['find' , * find_args (root , follow = follow )]
455+ cmd = ['find' , * find_args (root , follow = follow , ignore = ignore )]
427456 # try to use fd.. it cooperates well with gitignore etc, also faster than find
428457 for x in ('fd' , 'fd-find' , 'fdfind' ): # has different names on different dists..
429458 if shutil .which (x ):
430- cmd = [x , * fdfind_args (root , follow = follow )]
459+ cmd = [x , * fdfind_args (root , follow = follow , ignore = ignore )]
431460 break
432461 else :
433462 warnings .warn ("'fdfind' is recommended for the best indexing performance. See https://github.com/sharkdp/fd#installation. Falling back to 'find'" )
0 commit comments