Package: pathtraverser
PathTraverserBecause traversing through files and directories in Python should be as easy and fun as in Groovy.Installation$pipinstall--user--upgradePathTraverserUsageBasic example of simple iteration over files and directories>>>fromPathTraverserimportTraverser>>>>>>withTraverser('.')aspaths:>>>for_inpaths:>>>print(str(_))>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(Path.cwd())aspaths:>>>for_inpaths:>>>print(str(_))Iterated root path can be of type string or pathlib.Path and can be relative or absoluteWe can iterate multiple times over the same Traverser>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(Path.cwd())aspaths:>>>for_inpaths:>>>print(str(_))>>>for_inpaths:>>>print(str(_))We can nest multiple Traversers>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(Path.cwd())aspaths:>>>for_inpaths:>>>if_==Path.cwd()/'docs':>>>withTraverser(_)asdocs:>>>fordocindocs:>>>print(str(doc))>>>breakWe can filter iterated paths by exact name>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>namefilter='d.mp4'>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can filter iterated paths by regexp name>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>namefilter=re.compile(r'.*\.mp4')>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can filter iterated paths by extension>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>extfilter='txt'>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can limit max depth of iteration>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>maxdepth=0>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can set min depth of iteration>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>mindepth=2>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can filter different types of files>>>frompathlibimportPath>>>fromPathTraverserimportTraverser,PathTypes>>>>>>withTraverser(>>>Path.cwd(),>>>pathtype=PathTypes.FILE)asfiles:>>>for_infiles:>>>print(str(_))>>>frompathlibimportPath>>>fromPathTraverserimportTraverser,PathTypes>>>>>>withTraverser(>>>Path.cwd(),>>>pathtype=PathTypes.DIRECTORY)asdirs:>>>for_indirs:>>>print(str(_))>>>frompathlibimportPath>>>fromPathTraverserimportTraverser,PathTypes>>>>>>withTraverser(>>>Path.cwd(),>>>pathtype=PathTypes.LINK)aslinks:>>>for_inlinks:>>>print(str(_))We can build custom complex filters>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>pathfilter=lambda_:'dog'in_.name)aspaths:>>>for_inpaths:>>>print(str(_))First argument of path filter is positional and mandatory and is of type pathlib.Path. It means: current path to checkPath filter can also have any of following optional key word arguments:rel: relative form of current path against root path (of type pathlib.Path)root: root path (of type pathlib.Path)depth: current iteration depth (of type int)>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>pathfilter=lambda_,rel:rel.parts[0]in{'sub2','sub3'}>>>)aspaths:>>>for_inpaths:>>>print(str(_))>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>pathfilter=lambda_,depth:depth<1>>>)aspaths:>>>for_inpaths:>>>print(str(_))>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>fromPathTraverser.PathUtilsimportstarts_with>>>>>>withTraverser(>>>Path.cwd(),>>>pathfilter=lambda_,root:starts_with(_,root/'sub1')>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can use multiple filters at once>>>frompathlibimportPath>>>fromPathTraverserimportTraverser,PathTypes>>>>>>withTraverser(>>>Path.cwd(),>>>extfilter='txt',>>>pathtype=PathTypes.FILE,>>>pathfilter=lambda_,rel:rel.parts[0]in{'sub2','sub3'}>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can exclude iterated paths by exact name>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>exclude_namefilter='d.mp4'>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can exclude iterated paths by regexp name>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>exclude_namefilter=re.compile(r'.*\.mp4')>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can exclude iterated paths by extension>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>exclude_extfilter='txt'>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can build custom complex exclude filters>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(>>>Path.cwd(),>>>exclude_pathfilter=lambda_:'dog'in_.name)aspaths:>>>for_inpaths:>>>print(str(_))Arguments for exclude_pathfilter are the same as described for pathfilterWe can mix filters and exclude filters together>>>frompathlibimportPath>>>fromPathTraverserimportTraverser,PathTypes>>>>>>withTraverser(>>>Path.cwd(),>>>extfilter='txt',>>>pathtype=PathTypes.FILE,>>>pathfilter=lambda_,rel:rel.parts[0]in{'sub2','sub3'},>>>exclude_namefilter='d.txt'>>>)aspaths:>>>for_inpaths:>>>print(str(_))We can dynamically instruct Traverser to skip visiting hierarchies of some subdirectories. It means Traverser will not step down into skipped directories and will not iterate over contents of such directories>>>frompathlibimportPath>>>fromPathTraverserimportTraverser>>>>>>withTraverser(Path.cwd())aspaths:>>>for_inpaths:>>>ifpaths.depth==0and_.is_dir()and_.name=='x':>>>paths.skipsubtree(_)# do not enter 'x' directory and do not traverse its content>>>continue>>>print(str(_))Skipping subtrees is done bydef skipsubtree(self, *names: Union[Path, str]) -> 'Traverser'method which is part of a Traverser object.Please notice another usefull Traverser property that is used in this example:Traverser.depthwhich means depth of current iterationTraverser constructor has following input parametersroot: Union[Path, str]                                  -> root path
pathtype: PathTypes                                     -> path typ filter
mindepth: int = 0                                       -> min depth filter
maxdepth: int = None                                    -> max depth filter
pathfilter: Callable = None                             -> complex path filter
namefilter: Union[re.Pattern, str] = None               -> name filter
extfilter: str = None                                   -> extension filter
exclude_pathfilter: Callable = None                     -> exclude complex path filter
exclude_namefilter: Union[re.Pattern, str] = None       -> exclude name filter
exclude_extfilter: str = None                           -> exclude extension filter
followlinks: bool = True                                -> do follow symbolic links to subdirectories during traversing?
onerror: Callable = None                                -> by default errors from the os.scandir() call are ignored. If optional arg 'onerror' is specified, it should be a function; it will be called with one argument, an OSError instance. It can report the error to continue with the walk, or raise the exception to abort the walk. Note that the filename is available as the filename attribute of the exception objectTraverser object has some usefull read only propertiesroot: root directory being iterated (it points to a directory provided by user during instantiation of Traverser object)depth: depth of current iterationThis project provides also some usefull utilities as an extension to pathlib.Path built in functionalityfirst(path): returns first part of a given path (return type is pathlib.Path)last(path): returns last part of a given path (return type is pathlib.Path)part_count(path): returns number of path parts of a given path (return type is int)starts_with(path_a, path_b): check if path_b is parent to path_a. The comparison is done by comparing individual parts of the paths, not by comparing the characters of the simple string representation of the paths. Please note that no path resolution/normalization is done automatically when performing this function. Normalization should be performed by the user if necessary (return type is bool)>>>frompathlibimportPath>>>fromPathTraverser.PathUtilsimportfirst,last,part_count,starts_with>>>>>>first(Path('a/b/c')).name=='a'>>>last(Path('a/b/c')).name=='c'>>>part_count(Path('a/b/c'))==3>>>starts_with(Path('a/b/c/d'),Path('a/b/c'))==True>>>starts_with(Path('a/b/c/d2'),Path('a/b/c/d'))==FalseWe can also import this path utilities in the form of monkey patching of original pathlib.Path class:>>>fromPathTraverser.PathUtilsimportPath# this import executes also monkey patching of pathlib.Path>>>>>>Path('a/b/c').first.name=='a'>>>Path('a/b/c').last.name=='c'>>>Path('a/b/c').part_count==3>>>Path('a/b/c/d').starts_with(Path('a/b/c'))==True