PathTraverser


NamePathTraverser JSON
Version 2.0.0 PyPI version JSON
download
home_pagehttps://github.com/htarnacki/PathTraverser
SummaryTraversing file system in Python easily
upload_time2024-08-14 19:16:46
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseMIT License Copyright (c) 2023 htarnacki Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords traverse walk list files directories dirs filesystem path paths
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # PathTraverser

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Because traversing through files and directories in Python should be as easy and fun as in Groovy.


## Installation

```bash
$ pip install --user --upgrade PathTraverser
```

## Usage

### Basic example of recursively getting a list of all files and directories starting from current working directory
```python
>>> from PathTraverser import Traverser
>>>
>>> paths = list(Traverser())
```
or
```python
>>> from PathTraverser import Traverser
>>>
>>> paths = Traverser().get()
```

### Basic example of recursively iterating through a list of all files and directories, starting from the current working directory and printing their paths
```python
>>> from PathTraverser import Traverser
>>>
>>> for path in Traverser():
>>>     print(str(path))
```
or
```python
>>> from PathTraverser import Traverser
>>>
>>> Traverser().on_each(lambda _: print(str(_))).get()
```

### Advanced example of recursively iterating through a filtered list of all files and directories, starting from the current working directory
```python
>>> from PathTraverser import Traverser
>>>
>>> for path in Traverser(
>>>     0 < depth() < 4,
>>>     files,
>>>     -name('abc.jpg'),
>>>     readonly + hidden,
>>>     ext.jpg + ext.png,
>>>     size() < '1MB'
>>> ):
>>>     print(str(path))
```

### Input arguments
Two main input arguments for Traverser initialization are:
- root directory where iteration should start. This argument is optional and defaults to Path.cwd()
- list of filters which should be applied during iteration. This argument is optional and defaults to empty list of filters
```python
>>> Traverser(root, filter1, filter2, filter3, ...)
```
An iterated path is accepted and returned from the Traverser object only if it satisfies all the given filters.
So in the given example the path is accepted only if it meets the condition: filter1 and filter2 and filter3 and ...

Filters can also be combined in an "or" condition using the sum operation:
```python
>>> Traverser(root, filter1 + filter2 + filter3, filter4, filter5, ...)
```
In the given example the path is accepted only if it meets the condition: (filter1 or filter2 or filter3) and filter4 and filter5...

Filters can also be negated by the minus operator. If a filter is negative, it acts as the opposite filter to its positive counterpart:
```python
>>> Traverser(root, -(filter1 + filter2 + filter3), -filter4, filter5, ...)
```
In the given example the path is accepted only if it meets the condition: not(filter1 or filter2 or filter3) and not(filter4) and filter5...

### List of built in filters
- **files**: accepts only paths that are files or symlinks to files
- **dirs**: accepts only paths that are directories or symlinks to directories
- **symlinks**: accepts only paths that are symlinks
- **ext**: accepts only paths with the specified extensions
- **name**: accepts only paths whose names are equal to any of the given strings or match the given regular expressions or glob patterns
- **path**: accepts only paths that are equal to any of the given strings or match the given regular expressions or glob patterns
- **depth**: accepts only paths that are at the appropriate level of the root directory structure
- **hidden**: accepts only hidden paths
- **executable**: accepts only exacutable paths
- **writeable**: accepts only writeable paths
- **readonly**: accepts only readonly paths
- **owner**: accepts only paths that are owned by the specified person
- **size**: accepts only files whose size falls within the specified range
- **call**: custom callable filter

### Examples of "files" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import files, ext, readonly
>>>
>>> # recursively print all files
>>> for file in Traverser(files):
>>>     print(str(file))
>>>
>>> # recursively print all paths but no files
>>> for path in Traverser(-files):
>>>     print(str(path))
>>>
>>> # recursively print all files
>>> for file in Traverser().files:
>>>     print(str(file))
>>>
>>> # recursively print all files with extension '.txt' and readonly
>>> for file in Traverser().files(ext.txt, readonly):
>>>     print(str(file))
>>>
>>> # recursively get list of all files
>>> files = Traverser().files.get()
```

### Examples of "dirs" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import dirs, name, readonly
>>>
>>> # recursively print all directories
>>> for dir in Traverser(dirs):
>>>     print(str(dir))
>>>
>>> # recursively print all paths but no directories
>>> for path in Traverser(-dirs):
>>>     print(str(path))
>>>
>>> # recursively print all directories
>>> for dir in Traverser().dirs:
>>>     print(str(dir))
>>>
>>> # recursively print all directories with name like given regex and readonly
>>> for dir in Traverser().dirs(name.regex(r'pictures_\d+'), readonly):
>>>     print(str(dir))
>>>
>>> # recursively get list of all directories
>>> dirs = Traverser().dirs.get()
```


### Examples of "symlinks" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import symlinks, name, readonly
>>>
>>> # recursively print all symlinks
>>> for symlink in Traverser(symlinks):
>>>     print(str(symlink))
>>>
>>> # recursively print all paths but no symlinks
>>> for path in Traverser(-symlinks):
>>>     print(str(path))
>>>
>>> # recursively print all symlinks
>>> for symlink in Traverser().symlinks:
>>>     print(str(symlink))
>>>
>>> # recursively print all symlinks with name like given regex and readonly
>>> for symlink in Traverser().symlinks(name.regex(r'.+\.symlink'), readonly):
>>>     print(str(symlink))
>>>
>>> # recursively get list of all symlinks
>>> symlinks = Traverser().symlinks.get()
```


### Examples of "ext" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import ext
>>>
>>> # recursively print all files with extension '.jpg'
>>> for file in Traverser(ext('jpg')):
>>>     print(str(file))
>>>
>>> # recursively print all files with extension '.jpg'
>>> for file in Traverser(ext.jpg):
>>>     print(str(file))
>>>
>>> # recursively print all paths but no files with extension '.jpg' or '.png'
>>> for path in Traverser(-ext('jpg'), -ext.png):
>>>     print(str(path))
>>>
>>> # recursively print all files with extension '.jpg' or '.png' or '.gif'
>>> for file in Traverser(ext('jpg', 'png', 'gif')):
>>>     print(str(file))
>>>
>>> # recursively print all files with extension '.jpg' or '.png' or '.gif'
>>> for file in Traverser(ext.jpg + ext.png + ext.gif):
>>>     print(str(file))
```


### Examples of "name" filter
```python
>>> import re
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import name
>>>
>>> # recursively print all files named 'picture.jpg'
>>> for file in Traverser(name('picture.jpg')):
>>>     print(str(file))
>>>
>>> # recursively print all files named 'picture.jpg'
>>> for file in Traverser(name == 'picture.jpg'):
>>>     print(str(file))
>>>
>>> # recursively print all files named 'picture.jpg' or 'other.jpg'
>>> for file in Traverser(name('picture.jpg', 'other.jpg')):
>>>     print(str(file))
>>>
>>> # recursively print all files named 'picture.jpg' or 'other.jpg'
>>> for file in Traverser(name == ['picture.jpg', 'other.png']):
>>>     print(str(file))
>>>
>>> # recursively print all files named 'picture.jpg' or whose names match the regular expression pattern '.+\.png'
>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings
>>> for file in Traverser(name('picture.jpg', re.compile(r'.+\.png'))):
>>>     print(str(file))
>>>
>>> # recursively print all files named 'picture.jpg' or whose names match the regular expression pattern '.+\.png'
>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings
>>> for file in Traverser(name == ['picture.jpg', re.compile(r'.+\.png')]):
>>>     print(str(file))
>>>
>>> # recursively print all files whose names match the regular expression patterns '.+\.jpg' or '.+\.png' or '.+\.svg'
>>> # note: here regex patterns can be of type: string or re.Pattern. String values ​​are automatically compiled to re.Pattern
>>> for file in Traverser(name.regex(r'.+\.jpg', r'.+\.png', re.compile(r'.+\.svg'))):
>>>     print(str(file))
>>>
>>> # recursively print all files whose names match the glob patterns '*.jpg' or '*.png'
>>> for file in Traverser(name.glob('*.jpg', '*.png')):
>>>     print(str(file))
```


### Examples of "path" filter
```python
>>> import re
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import path
>>>
>>> # recursively print all files that have path '/etc/config.json'
>>> # of course the usefulness of this form of invocation is questionable
>>> for file in Traverser(path('/etc/config.json')):
>>>     print(str(file))
>>>
>>> # recursively print all files that have path '/etc/config.json'
>>> # of course the usefulness of this form of invocation is questionable
>>> for file in Traverser(path == '/etc/config.json'):
>>>     print(str(file))
>>>
>>> # recursively print all files that have path '/etc/config.json' or '/etc/config.yaml'
>>> # of course the usefulness of this form of invocation is questionable
>>> for file in Traverser(path('/etc/config.json', '/etc/config.yaml')):
>>>     print(str(file))
>>>
>>> # recursively print all files that have path '/etc/config.json' or '/etc/config.yaml'
>>> # of course the usefulness of this form of invocation is questionable
>>> for file in Traverser(path == ['/etc/config.json', '/etc/config.yaml']):
>>>     print(str(file))
>>>
>>> # recursively print all files that have path '/etc/config.json' or whose paths match the regular expression pattern '/etc/.*/config\.yaml'
>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings
>>> for file in Traverser(path('/etc/config.json', re.compile(r'/etc/.*/config\.yaml'))):
>>>     print(str(file))
>>>
>>> # recursively print all files that have path '/etc/config.json' or whose paths match the regular expression pattern '/etc/.*/config\.yaml'
>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings
>>> for file in Traverser(path == ['/etc/config.json', re.compile(r'/etc/.*/config\.yaml')]):
>>>     print(str(file))
>>>
>>> # recursively print all files whose paths match the regular expression patterns '/etc/.*/config\.json' or '/etc/.*/config\.yaml' or '/etc/.*/config\.xml'
>>> # note: here regex patterns can be of type: string or re.Pattern. String values ​​are automatically compiled to re.Pattern
>>> for file in Traverser(path.regex(r'/etc/.*/config\.json', r'/etc/.*/config\.yaml', re.compile(r'/etc/.*/config\.xml'))):
>>>     print(str(file))
>>>
>>> # recursively print all files whose paths match the glob patterns '**/*.jpg' or '**/*.png'
>>> for file in Traverser(path.glob('**/*.jpg', '**/*.png')):
>>>     print(str(file))
```


### Examples of "depth" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import depth
>>>
>>> # recursively print all files located in range <1, 4> of levels of the root directory subtree hierarchy
>>> for file in Traverser(depth(1, 4)):
>>>     print(str(file))
>>>
>>> # recursively print all files located in range (1, ∞) of levels of the root directory subtree hierarchy
>>> for file in Traverser(1 < depth()):
>>>     print(str(file))
>>>
>>> # recursively print all files located in range <0, 2) of levels of the root directory subtree hierarchy
>>> for file in Traverser(depth() < 2):
>>>     print(str(file))
>>>
>>> # recursively print all files located in range (1, 4> of levels of the root directory subtree hierarchy
>>> for file in Traverser(1 < depth() <= 4):
>>>     print(str(file))
>>>
>>> # recursively print all files located at level 2 of the root directory subtree hierarchy
>>> for file in Traverser(depth() == 2):
>>>     print(str(file))
>>>
>>> # recursively print all files located at any level but no 2 of the root directory subtree hierarchy
>>> for file in Traverser(-(depth() == 2)):
>>>     print(str(file))
```


### Examples of "hidden" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import hidden
>>>
>>> # recursively print all paths with hidden attribute
>>> for path in Traverser(hidden):
>>>     print(str(path))
>>>
>>> # recursively print all files with hidden attribute
>>> for file in Traverser(hidden).files(hidden):
>>>     print(str(file))
>>>
>>> # recursively print all dirs with hidden attribute
>>> for dir in Traverser(hidden).dirs(hidden):
>>>     print(str(dir))
```


### Examples of "executable" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import executable
>>>
>>> # recursively print all paths with executable attribute
>>> for path in Traverser(executable):
>>>     print(str(path))
>>>
>>> # recursively print all files with executable attribute
>>> for file in Traverser(executable).files(executable):
>>>     print(str(file))
>>>
>>> # recursively print all dirs with executable attribute
>>> for dir in Traverser(executable).dirs(executable):
>>>     print(str(dir))
```


### Examples of "writeable" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import writeable
>>>
>>> # recursively print all paths with writeable attribute
>>> for path in Traverser(writeable):
>>>     print(str(path))
>>>
>>> # recursively print all files with writeable attribute
>>> for file in Traverser(writeable).files(writeable):
>>>     print(str(file))
>>>
>>> # recursively print all dirs with writeable attribute
>>> for dir in Traverser(writeable).dirs(writeable):
>>>     print(str(dir))
``` 


### Examples of "readonly" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import readonly
>>>
>>> # recursively print all paths with readonly attribute
>>> for path in Traverser(readonly):
>>>     print(str(path))
>>>
>>> # recursively print all files with readonly attribute
>>> for file in Traverser(readonly).files(readonly):
>>>     print(str(file))
>>>
>>> # recursively print all dirs with readonly attribute
>>> for dir in Traverser(readonly).dirs(readonly):
>>>     print(str(dir))
```


### Examples of "owner" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import owner
>>>
>>> # recursively print all paths owned by user 'htarnacki'
>>> for path in Traverser(owner('htarnacki')):
>>>     print(str(path))
>>>
>>> # recursively print all paths owned by user 'htarnacki'
>>> for path in Traverser(owner == 'htarnacki'):
>>>     print(str(path))
>>>
>>> # recursively print all paths owned by user 'root' or 'htarnacki'
>>> for path in Traverser(owner('root', 'htarnacki')):
>>>     print(str(path))
>>>
>>> # recursively print all paths owned by user 'root' or 'htarnacki'
>>> for path in Traverser(owner == ['root', 'htarnacki']):
>>>     print(str(path))
```


### Examples of "size" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import size
>>>
>>> # recursively print all files whose size is less than 2 MB
>>> for path in Traverser(size() < '2MB'):
>>>     print(str(path))
>>>
>>> # recursively print all files whose size is less than 2 MB and greater or equal to 100 KB
>>> for path in Traverser('100KB' <= size() < '2MB'):
>>>     print(str(path))
```
Supported units are: 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'


### Examples of "call" filter
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import call
>>> from PathTraverser.utils.Path import starts_with
>>>
>>> # recursively print all paths named 'a.mp4'
>>> for path in Traverser(call(lambda _: _.name == 'a.mp4')):
>>>     print(str(path))
>>>
>>> # recursively print all paths named 'a.mp4' and located at level 0 of the root directory subtree hierarchy
>>> for path in Traverser(call(lambda _, depth: _.name == 'a.mp4' and depth == 0)):
>>>     print(str(path))
>>>
>>> # recursively print all paths named "a.mp4" and located at a level lower than 5 in the root directory subtree hierarchy whose relative path starts with "a/b/c"
>>> for path in Traverser(
>>>     call(lambda _, depth, rel: _.name == 'a.mp4' and depth < 5 and starts_with(rel, Path('a/b/c')))
>>> ):
>>>     print(str(path))
```
- the user can use any callable object
- there is only one mandatory input parameter for the callable object, it is the absolute path to the accepted file or directory
- user can also use following optional input parameters:
    - **depth**: root directory subtree level
    - **rel**: relative path to the accepted file or directory
    - **root**: path to root directory
    - **skipsubtree**: a function that can be used to skip scanning some subdirectories


### Iterable and Iterator
- 'Traverser' is an iterable object. Iterable object can produce many iterators
- 'Traverser' can produce iterators in two ways:
    - **iter(Traverser())**
    - **Traverser().iter()**
    - **Traverser().files**: filters from 'Traverser' object + one additional 'files' filter
    - **Traverser().files(filter1, filter2, ...)**: filters from 'Traverser' object + [files, filter1, filter2, ...] filters
    - **Traverser().dirs**: filters from 'Traverser' object + one additional 'dirs' filter
    - **Traverser().dirs(filter1, filter2, ...)**: filters from 'Traverser' object + [dirs, filter1, filter2, ...] filters
    - **Traverser().symlinks**: filters from 'Traverser' object + one additional 'symlinks' filter
    - **Traverser().symlinks(filter1, filter2, ...)**: filters from 'Traverser' object + [symlinks, filter1, filter2, ...] filters
    - **Traverser().filter(filter1, filter2, ...)**: filters from 'Traverser' object + [filter1, filter2, ...] filters


### Skipping subtrees
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
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.Traverser import Iterator
>>> from PathTraverser.filters import call
>>> from PathTraverser.utils.Path import starts_with
>>> 
>>> # let's assume we have the following directory structure
>>> # | a
>>> # |-- b
>>> # |---- b1
>>> # |---- b2
>>> # |---- b3
>>> # |------- b4
>>> # |-- c
>>> # |-- d
>>> 
>>> # such code will recursively scan the entire root directory structure from top to bottom
>>> paths = Traverser(Path('a')).get()
>>> 
>>> # but what if we wanted to skip traversing directory b and all of its subdirectories? This code would allow us to do that
>>> for _ in (paths := Traverser(Path('a')).iter()):
>>>     if _.name == 'b' and _.is_dir():
>>>         paths.skipsubtree(_)
>>>         continue
>>>     print(str(_))
>>> 
>>> # and all 5 examples below have basically the same effect:
>>> 
>>> # 1
>>> for _ in (paths := iter(Traverser(TEST_DATA_ROOT))):
>>>     if _.name == 'b' and _.is_dir():
>>>         paths.skipsubtree(_)
>>>         continue
>>>     print(str(_))
>>> 
>>> # 2
>>> with Traverser(TEST_DATA_ROOT).iter() as paths:
>>>     for _ in paths:
>>>         if _.name == 'b' and _.is_dir():
>>>             paths.skipsubtree(_)
>>>             continue
>>>         print(str(_))
>>> 
>>> # 3
>>> with iter(Traverser(TEST_DATA_ROOT)) as paths:
>>>     for _ in paths:
>>>         if _.name == 'b' and _.is_dir():
>>>             paths.skipsubtree(_)
>>>             continue
>>>         print(str(_))
>>> 
>>> # 4
>>> paths: Iterator = Traverser(TEST_DATA_ROOT).iter()
>>> for _ in paths:
>>>     if _.name == 'b' and _.is_dir():
>>>         paths.skipsubtree(_)
>>>         continue
>>>     print(str(_))
>>> 
>>> # 5
>>> paths: Iterator = iter(Traverser(TEST_DATA_ROOT))
>>> for _ in paths:
>>>     if _.name == 'b' and _.is_dir():
>>>         paths.skipsubtree(_)
>>>         continue
>>>     print(str(_))
>>>
>>> # and let's compare all these examples with the one below
>>> for path in Traverser(call(lambda _, rel: not starts_with(rel, Path('a/b')))):
>>>     print(str(path))
>>>
>>> # the last example gives the same results, but is less efficient than the previous ones, because the directory "a/b" is still scanned recursively, but all of its contents are not accepted
```


### 'Traverser' object hooks: on_each, on_file, on_dir, on_symlink
- ```on_each```: reacts on each accepted path
- ```on_file```: reacts on each accepted file
- ```on_dir```: reacts on each accepted directory
- ```on_symlink```: reacts on each accepted symlink
```python
>>> from PathTraverser import Traverser
>>>
>>> Traverser() \
>>>     .on_each(lambda _: print(str(_), 'is any path')) \
>>>     .on_file(lambda _: print(str(_), 'is a file')) \
>>>     .on_dir(lambda _: print(str(_), 'is a directory')) \
>>>     .on_symlink(lambda _: print(str(_), 'is a symlink')) \
>>>     .get()
```

### Context manager
The Traverser object and all the iterators it generates are context managers. There is no magic behind it, but it allows you to write the same code in a different style if desired
```python
>>> from PathTraverser import Traverser
>>> from PathTraverser.filters import hidden, files, dirs
>>>
>>> # 'context manager' style
>>> # recursively print all hidden files and next all hidden dirs
>>> with Traverser(TEST_DATA_ROOT, hidden) as paths:
>>>     for _ in paths.files:
>>>         print(str(_))
>>>     for _ in paths.dirs:
>>>         print(str(_))
>>>
>>> # 'normal' style
>>> # recursively print all hidden files and next all hidden dirs
>>> paths = Traverser(TEST_DATA_ROOT, hidden)
>>> for _ in paths.files:
>>>     print(str(_))
>>> for _ in paths.dirs:
>>>     print(str(_))
>>>
>>> # 'context manager' style
>>> # recursively print all hidden files and next all hidden dirs
>>> with Traverser(TEST_DATA_ROOT, hidden) as paths:
>>>     for _ in paths.filter(files):
>>>         print(str(_))
>>>     for _ in paths.filter(dirs):
>>>         print(str(_))
>>>
>>> # 'normal' style
>>> # recursively print all hidden files and next all hidden dirs
>>> paths = Traverser(TEST_DATA_ROOT, hidden)
>>> for _ in paths.filter(files):
>>>     print(str(_))
>>> for _ in paths.filter(dirs):
>>>     print(str(_))
```


### Iterators produced by 'Traverser' object have some usefull read only properties
- ```root```: iterated root directory (points to the directory specified by the user when creating the Traverser instance)
- ```depth```: current iteration depth
```python
>>> from PathTraverser import Traverser
>>>
>>> # recursively print all files and directories with iteration depth == 0
>>> for _ in (paths := iter(Traverser(TEST_DATA_ROOT))):
>>>     if paths.depth == 0 and _.is_dir():
>>>         paths.skipsubtree(_)
>>>     print(str(_))
```


### This project also provides some useful tools as an extension of the built-in pathlib.Path functionality
- ```first(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 a parent of or equal 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. Note that no path resolution/normalization is performed automatically when executing this function. Normalization should be performed by the user if necessary (return type is bool)

```python
>>> from pathlib import Path
>>> from PathTraverser.utils.Path import first, 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')) == False
```

We can also import these path utilities in the form of monkey patching the original pathlib.Path class:
```python
>>> from PathTraverser.utils.Path import Path  # 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
```


### More examples
More examples can be found in a test file of this project: **tests/testTraverser.py**

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/htarnacki/PathTraverser",
    "name": "PathTraverser",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": "Hubert Tarnacki <hubert.tarnacki@gmail.com>",
    "keywords": "traverse, walk, list, files, directories, dirs, filesystem, path, paths",
    "author": null,
    "author_email": "Hubert Tarnacki <hubert.tarnacki@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/1e/59/dd21afce7bc3b506b8735fa96d4144ad86c741a7f0283fdd6941c2653d9d/pathtraverser-2.0.0.tar.gz",
    "platform": null,
    "description": "# PathTraverser\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nBecause traversing through files and directories in Python should be as easy and fun as in Groovy.\n\n\n## Installation\n\n```bash\n$ pip install --user --upgrade PathTraverser\n```\n\n## Usage\n\n### Basic example of recursively getting a list of all files and directories starting from current working directory\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> paths = list(Traverser())\n```\nor\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> paths = Traverser().get()\n```\n\n### Basic example of recursively iterating through a list of all files and directories, starting from the current working directory and printing their paths\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> for path in Traverser():\n>>>     print(str(path))\n```\nor\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> Traverser().on_each(lambda _: print(str(_))).get()\n```\n\n### Advanced example of recursively iterating through a filtered list of all files and directories, starting from the current working directory\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> for path in Traverser(\n>>>     0 < depth() < 4,\n>>>     files,\n>>>     -name('abc.jpg'),\n>>>     readonly + hidden,\n>>>     ext.jpg + ext.png,\n>>>     size() < '1MB'\n>>> ):\n>>>     print(str(path))\n```\n\n### Input arguments\nTwo main input arguments for Traverser initialization are:\n- root directory where iteration should start. This argument is optional and defaults to Path.cwd()\n- list of filters which should be applied during iteration. This argument is optional and defaults to empty list of filters\n```python\n>>> Traverser(root, filter1, filter2, filter3, ...)\n```\nAn iterated path is accepted and returned from the Traverser object only if it satisfies all the given filters.\nSo in the given example the path is accepted only if it meets the condition: filter1 and filter2 and filter3 and ...\n\nFilters can also be combined in an \"or\" condition using the sum operation:\n```python\n>>> Traverser(root, filter1 + filter2 + filter3, filter4, filter5, ...)\n```\nIn the given example the path is accepted only if it meets the condition: (filter1 or filter2 or filter3) and filter4 and filter5...\n\nFilters can also be negated by the minus operator. If a filter is negative, it acts as the opposite filter to its positive counterpart:\n```python\n>>> Traverser(root, -(filter1 + filter2 + filter3), -filter4, filter5, ...)\n```\nIn the given example the path is accepted only if it meets the condition: not(filter1 or filter2 or filter3) and not(filter4) and filter5...\n\n### List of built in filters\n- **files**: accepts only paths that are files or symlinks to files\n- **dirs**: accepts only paths that are directories or symlinks to directories\n- **symlinks**: accepts only paths that are symlinks\n- **ext**: accepts only paths with the specified extensions\n- **name**: accepts only paths whose names are equal to any of the given strings or match the given regular expressions or glob patterns\n- **path**: accepts only paths that are equal to any of the given strings or match the given regular expressions or glob patterns\n- **depth**: accepts only paths that are at the appropriate level of the root directory structure\n- **hidden**: accepts only hidden paths\n- **executable**: accepts only exacutable paths\n- **writeable**: accepts only writeable paths\n- **readonly**: accepts only readonly paths\n- **owner**: accepts only paths that are owned by the specified person\n- **size**: accepts only files whose size falls within the specified range\n- **call**: custom callable filter\n\n### Examples of \"files\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import files, ext, readonly\n>>>\n>>> # recursively print all files\n>>> for file in Traverser(files):\n>>>     print(str(file))\n>>>\n>>> # recursively print all paths but no files\n>>> for path in Traverser(-files):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files\n>>> for file in Traverser().files:\n>>>     print(str(file))\n>>>\n>>> # recursively print all files with extension '.txt' and readonly\n>>> for file in Traverser().files(ext.txt, readonly):\n>>>     print(str(file))\n>>>\n>>> # recursively get list of all files\n>>> files = Traverser().files.get()\n```\n\n### Examples of \"dirs\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import dirs, name, readonly\n>>>\n>>> # recursively print all directories\n>>> for dir in Traverser(dirs):\n>>>     print(str(dir))\n>>>\n>>> # recursively print all paths but no directories\n>>> for path in Traverser(-dirs):\n>>>     print(str(path))\n>>>\n>>> # recursively print all directories\n>>> for dir in Traverser().dirs:\n>>>     print(str(dir))\n>>>\n>>> # recursively print all directories with name like given regex and readonly\n>>> for dir in Traverser().dirs(name.regex(r'pictures_\\d+'), readonly):\n>>>     print(str(dir))\n>>>\n>>> # recursively get list of all directories\n>>> dirs = Traverser().dirs.get()\n```\n\n\n### Examples of \"symlinks\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import symlinks, name, readonly\n>>>\n>>> # recursively print all symlinks\n>>> for symlink in Traverser(symlinks):\n>>>     print(str(symlink))\n>>>\n>>> # recursively print all paths but no symlinks\n>>> for path in Traverser(-symlinks):\n>>>     print(str(path))\n>>>\n>>> # recursively print all symlinks\n>>> for symlink in Traverser().symlinks:\n>>>     print(str(symlink))\n>>>\n>>> # recursively print all symlinks with name like given regex and readonly\n>>> for symlink in Traverser().symlinks(name.regex(r'.+\\.symlink'), readonly):\n>>>     print(str(symlink))\n>>>\n>>> # recursively get list of all symlinks\n>>> symlinks = Traverser().symlinks.get()\n```\n\n\n### Examples of \"ext\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import ext\n>>>\n>>> # recursively print all files with extension '.jpg'\n>>> for file in Traverser(ext('jpg')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files with extension '.jpg'\n>>> for file in Traverser(ext.jpg):\n>>>     print(str(file))\n>>>\n>>> # recursively print all paths but no files with extension '.jpg' or '.png'\n>>> for path in Traverser(-ext('jpg'), -ext.png):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files with extension '.jpg' or '.png' or '.gif'\n>>> for file in Traverser(ext('jpg', 'png', 'gif')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files with extension '.jpg' or '.png' or '.gif'\n>>> for file in Traverser(ext.jpg + ext.png + ext.gif):\n>>>     print(str(file))\n```\n\n\n### Examples of \"name\" filter\n```python\n>>> import re\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import name\n>>>\n>>> # recursively print all files named 'picture.jpg'\n>>> for file in Traverser(name('picture.jpg')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files named 'picture.jpg'\n>>> for file in Traverser(name == 'picture.jpg'):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files named 'picture.jpg' or 'other.jpg'\n>>> for file in Traverser(name('picture.jpg', 'other.jpg')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files named 'picture.jpg' or 'other.jpg'\n>>> for file in Traverser(name == ['picture.jpg', 'other.png']):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files named 'picture.jpg' or whose names match the regular expression pattern '.+\\.png'\n>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings\n>>> for file in Traverser(name('picture.jpg', re.compile(r'.+\\.png'))):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files named 'picture.jpg' or whose names match the regular expression pattern '.+\\.png'\n>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings\n>>> for file in Traverser(name == ['picture.jpg', re.compile(r'.+\\.png')]):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files whose names match the regular expression patterns '.+\\.jpg' or '.+\\.png' or '.+\\.svg'\n>>> # note: here regex patterns can be of type: string or re.Pattern. String values \u200b\u200bare automatically compiled to re.Pattern\n>>> for file in Traverser(name.regex(r'.+\\.jpg', r'.+\\.png', re.compile(r'.+\\.svg'))):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files whose names match the glob patterns '*.jpg' or '*.png'\n>>> for file in Traverser(name.glob('*.jpg', '*.png')):\n>>>     print(str(file))\n```\n\n\n### Examples of \"path\" filter\n```python\n>>> import re\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import path\n>>>\n>>> # recursively print all files that have path '/etc/config.json'\n>>> # of course the usefulness of this form of invocation is questionable\n>>> for file in Traverser(path('/etc/config.json')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files that have path '/etc/config.json'\n>>> # of course the usefulness of this form of invocation is questionable\n>>> for file in Traverser(path == '/etc/config.json'):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files that have path '/etc/config.json' or '/etc/config.yaml'\n>>> # of course the usefulness of this form of invocation is questionable\n>>> for file in Traverser(path('/etc/config.json', '/etc/config.yaml')):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files that have path '/etc/config.json' or '/etc/config.yaml'\n>>> # of course the usefulness of this form of invocation is questionable\n>>> for file in Traverser(path == ['/etc/config.json', '/etc/config.yaml']):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files that have path '/etc/config.json' or whose paths match the regular expression pattern '/etc/.*/config\\.yaml'\n>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings\n>>> for file in Traverser(path('/etc/config.json', re.compile(r'/etc/.*/config\\.yaml'))):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files that have path '/etc/config.json' or whose paths match the regular expression pattern '/etc/.*/config\\.yaml'\n>>> # note: here regex patterns must be of type: re.Pattern. If not, they are treated as normal strings\n>>> for file in Traverser(path == ['/etc/config.json', re.compile(r'/etc/.*/config\\.yaml')]):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files whose paths match the regular expression patterns '/etc/.*/config\\.json' or '/etc/.*/config\\.yaml' or '/etc/.*/config\\.xml'\n>>> # note: here regex patterns can be of type: string or re.Pattern. String values \u200b\u200bare automatically compiled to re.Pattern\n>>> for file in Traverser(path.regex(r'/etc/.*/config\\.json', r'/etc/.*/config\\.yaml', re.compile(r'/etc/.*/config\\.xml'))):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files whose paths match the glob patterns '**/*.jpg' or '**/*.png'\n>>> for file in Traverser(path.glob('**/*.jpg', '**/*.png')):\n>>>     print(str(file))\n```\n\n\n### Examples of \"depth\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import depth\n>>>\n>>> # recursively print all files located in range <1, 4> of levels of the root directory subtree hierarchy\n>>> for file in Traverser(depth(1, 4)):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files located in range (1, \u221e) of levels of the root directory subtree hierarchy\n>>> for file in Traverser(1 < depth()):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files located in range <0, 2) of levels of the root directory subtree hierarchy\n>>> for file in Traverser(depth() < 2):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files located in range (1, 4> of levels of the root directory subtree hierarchy\n>>> for file in Traverser(1 < depth() <= 4):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files located at level 2 of the root directory subtree hierarchy\n>>> for file in Traverser(depth() == 2):\n>>>     print(str(file))\n>>>\n>>> # recursively print all files located at any level but no 2 of the root directory subtree hierarchy\n>>> for file in Traverser(-(depth() == 2)):\n>>>     print(str(file))\n```\n\n\n### Examples of \"hidden\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import hidden\n>>>\n>>> # recursively print all paths with hidden attribute\n>>> for path in Traverser(hidden):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files with hidden attribute\n>>> for file in Traverser(hidden).files(hidden):\n>>>     print(str(file))\n>>>\n>>> # recursively print all dirs with hidden attribute\n>>> for dir in Traverser(hidden).dirs(hidden):\n>>>     print(str(dir))\n```\n\n\n### Examples of \"executable\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import executable\n>>>\n>>> # recursively print all paths with executable attribute\n>>> for path in Traverser(executable):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files with executable attribute\n>>> for file in Traverser(executable).files(executable):\n>>>     print(str(file))\n>>>\n>>> # recursively print all dirs with executable attribute\n>>> for dir in Traverser(executable).dirs(executable):\n>>>     print(str(dir))\n```\n\n\n### Examples of \"writeable\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import writeable\n>>>\n>>> # recursively print all paths with writeable attribute\n>>> for path in Traverser(writeable):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files with writeable attribute\n>>> for file in Traverser(writeable).files(writeable):\n>>>     print(str(file))\n>>>\n>>> # recursively print all dirs with writeable attribute\n>>> for dir in Traverser(writeable).dirs(writeable):\n>>>     print(str(dir))\n``` \n\n\n### Examples of \"readonly\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import readonly\n>>>\n>>> # recursively print all paths with readonly attribute\n>>> for path in Traverser(readonly):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files with readonly attribute\n>>> for file in Traverser(readonly).files(readonly):\n>>>     print(str(file))\n>>>\n>>> # recursively print all dirs with readonly attribute\n>>> for dir in Traverser(readonly).dirs(readonly):\n>>>     print(str(dir))\n```\n\n\n### Examples of \"owner\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import owner\n>>>\n>>> # recursively print all paths owned by user 'htarnacki'\n>>> for path in Traverser(owner('htarnacki')):\n>>>     print(str(path))\n>>>\n>>> # recursively print all paths owned by user 'htarnacki'\n>>> for path in Traverser(owner == 'htarnacki'):\n>>>     print(str(path))\n>>>\n>>> # recursively print all paths owned by user 'root' or 'htarnacki'\n>>> for path in Traverser(owner('root', 'htarnacki')):\n>>>     print(str(path))\n>>>\n>>> # recursively print all paths owned by user 'root' or 'htarnacki'\n>>> for path in Traverser(owner == ['root', 'htarnacki']):\n>>>     print(str(path))\n```\n\n\n### Examples of \"size\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import size\n>>>\n>>> # recursively print all files whose size is less than 2 MB\n>>> for path in Traverser(size() < '2MB'):\n>>>     print(str(path))\n>>>\n>>> # recursively print all files whose size is less than 2 MB and greater or equal to 100 KB\n>>> for path in Traverser('100KB' <= size() < '2MB'):\n>>>     print(str(path))\n```\nSupported units are: 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'\n\n\n### Examples of \"call\" filter\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import call\n>>> from PathTraverser.utils.Path import starts_with\n>>>\n>>> # recursively print all paths named 'a.mp4'\n>>> for path in Traverser(call(lambda _: _.name == 'a.mp4')):\n>>>     print(str(path))\n>>>\n>>> # recursively print all paths named 'a.mp4' and located at level 0 of the root directory subtree hierarchy\n>>> for path in Traverser(call(lambda _, depth: _.name == 'a.mp4' and depth == 0)):\n>>>     print(str(path))\n>>>\n>>> # recursively print all paths named \"a.mp4\" and located at a level lower than 5 in the root directory subtree hierarchy whose relative path starts with \"a/b/c\"\n>>> for path in Traverser(\n>>>     call(lambda _, depth, rel: _.name == 'a.mp4' and depth < 5 and starts_with(rel, Path('a/b/c')))\n>>> ):\n>>>     print(str(path))\n```\n- the user can use any callable object\n- there is only one mandatory input parameter for the callable object, it is the absolute path to the accepted file or directory\n- user can also use following optional input parameters:\n    - **depth**: root directory subtree level\n    - **rel**: relative path to the accepted file or directory\n    - **root**: path to root directory\n    - **skipsubtree**: a function that can be used to skip scanning some subdirectories\n\n\n### Iterable and Iterator\n- 'Traverser' is an iterable object. Iterable object can produce many iterators\n- 'Traverser' can produce iterators in two ways:\n    - **iter(Traverser())**\n    - **Traverser().iter()**\n    - **Traverser().files**: filters from 'Traverser' object + one additional 'files' filter\n    - **Traverser().files(filter1, filter2, ...)**: filters from 'Traverser' object + [files, filter1, filter2, ...] filters\n    - **Traverser().dirs**: filters from 'Traverser' object + one additional 'dirs' filter\n    - **Traverser().dirs(filter1, filter2, ...)**: filters from 'Traverser' object + [dirs, filter1, filter2, ...] filters\n    - **Traverser().symlinks**: filters from 'Traverser' object + one additional 'symlinks' filter\n    - **Traverser().symlinks(filter1, filter2, ...)**: filters from 'Traverser' object + [symlinks, filter1, filter2, ...] filters\n    - **Traverser().filter(filter1, filter2, ...)**: filters from 'Traverser' object + [filter1, filter2, ...] filters\n\n\n### Skipping subtrees\nWe 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\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.Traverser import Iterator\n>>> from PathTraverser.filters import call\n>>> from PathTraverser.utils.Path import starts_with\n>>> \n>>> # let's assume we have the following directory structure\n>>> # | a\n>>> # |-- b\n>>> # |---- b1\n>>> # |---- b2\n>>> # |---- b3\n>>> # |------- b4\n>>> # |-- c\n>>> # |-- d\n>>> \n>>> # such code will recursively scan the entire root directory structure from top to bottom\n>>> paths = Traverser(Path('a')).get()\n>>> \n>>> # but what if we wanted to skip traversing directory b and all of its subdirectories? This code would allow us to do that\n>>> for _ in (paths := Traverser(Path('a')).iter()):\n>>>     if _.name == 'b' and _.is_dir():\n>>>         paths.skipsubtree(_)\n>>>         continue\n>>>     print(str(_))\n>>> \n>>> # and all 5 examples below have basically the same effect:\n>>> \n>>> # 1\n>>> for _ in (paths := iter(Traverser(TEST_DATA_ROOT))):\n>>>     if _.name == 'b' and _.is_dir():\n>>>         paths.skipsubtree(_)\n>>>         continue\n>>>     print(str(_))\n>>> \n>>> # 2\n>>> with Traverser(TEST_DATA_ROOT).iter() as paths:\n>>>     for _ in paths:\n>>>         if _.name == 'b' and _.is_dir():\n>>>             paths.skipsubtree(_)\n>>>             continue\n>>>         print(str(_))\n>>> \n>>> # 3\n>>> with iter(Traverser(TEST_DATA_ROOT)) as paths:\n>>>     for _ in paths:\n>>>         if _.name == 'b' and _.is_dir():\n>>>             paths.skipsubtree(_)\n>>>             continue\n>>>         print(str(_))\n>>> \n>>> # 4\n>>> paths: Iterator = Traverser(TEST_DATA_ROOT).iter()\n>>> for _ in paths:\n>>>     if _.name == 'b' and _.is_dir():\n>>>         paths.skipsubtree(_)\n>>>         continue\n>>>     print(str(_))\n>>> \n>>> # 5\n>>> paths: Iterator = iter(Traverser(TEST_DATA_ROOT))\n>>> for _ in paths:\n>>>     if _.name == 'b' and _.is_dir():\n>>>         paths.skipsubtree(_)\n>>>         continue\n>>>     print(str(_))\n>>>\n>>> # and let's compare all these examples with the one below\n>>> for path in Traverser(call(lambda _, rel: not starts_with(rel, Path('a/b')))):\n>>>     print(str(path))\n>>>\n>>> # the last example gives the same results, but is less efficient than the previous ones, because the directory \"a/b\" is still scanned recursively, but all of its contents are not accepted\n```\n\n\n### 'Traverser' object hooks: on_each, on_file, on_dir, on_symlink\n- ```on_each```: reacts on each accepted path\n- ```on_file```: reacts on each accepted file\n- ```on_dir```: reacts on each accepted directory\n- ```on_symlink```: reacts on each accepted symlink\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> Traverser() \\\n>>>     .on_each(lambda _: print(str(_), 'is any path')) \\\n>>>     .on_file(lambda _: print(str(_), 'is a file')) \\\n>>>     .on_dir(lambda _: print(str(_), 'is a directory')) \\\n>>>     .on_symlink(lambda _: print(str(_), 'is a symlink')) \\\n>>>     .get()\n```\n\n### Context manager\nThe Traverser object and all the iterators it generates are context managers. There is no magic behind it, but it allows you to write the same code in a different style if desired\n```python\n>>> from PathTraverser import Traverser\n>>> from PathTraverser.filters import hidden, files, dirs\n>>>\n>>> # 'context manager' style\n>>> # recursively print all hidden files and next all hidden dirs\n>>> with Traverser(TEST_DATA_ROOT, hidden) as paths:\n>>>     for _ in paths.files:\n>>>         print(str(_))\n>>>     for _ in paths.dirs:\n>>>         print(str(_))\n>>>\n>>> # 'normal' style\n>>> # recursively print all hidden files and next all hidden dirs\n>>> paths = Traverser(TEST_DATA_ROOT, hidden)\n>>> for _ in paths.files:\n>>>     print(str(_))\n>>> for _ in paths.dirs:\n>>>     print(str(_))\n>>>\n>>> # 'context manager' style\n>>> # recursively print all hidden files and next all hidden dirs\n>>> with Traverser(TEST_DATA_ROOT, hidden) as paths:\n>>>     for _ in paths.filter(files):\n>>>         print(str(_))\n>>>     for _ in paths.filter(dirs):\n>>>         print(str(_))\n>>>\n>>> # 'normal' style\n>>> # recursively print all hidden files and next all hidden dirs\n>>> paths = Traverser(TEST_DATA_ROOT, hidden)\n>>> for _ in paths.filter(files):\n>>>     print(str(_))\n>>> for _ in paths.filter(dirs):\n>>>     print(str(_))\n```\n\n\n### Iterators produced by 'Traverser' object have some usefull read only properties\n- ```root```: iterated root directory (points to the directory specified by the user when creating the Traverser instance)\n- ```depth```: current iteration depth\n```python\n>>> from PathTraverser import Traverser\n>>>\n>>> # recursively print all files and directories with iteration depth == 0\n>>> for _ in (paths := iter(Traverser(TEST_DATA_ROOT))):\n>>>     if paths.depth == 0 and _.is_dir():\n>>>         paths.skipsubtree(_)\n>>>     print(str(_))\n```\n\n\n### This project also provides some useful tools as an extension of the built-in pathlib.Path functionality\n- ```first(path)```: returns first part of a given path (return type is pathlib.Path)\n- ```last(path)```: returns last part of a given path (return type is pathlib.Path)\n- ```part_count(path)```: returns number of path parts of a given path (return type is int)\n- ```starts_with(path_a, path_b)```: check if path_b is a parent of or equal 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. Note that no path resolution/normalization is performed automatically when executing this function. Normalization should be performed by the user if necessary (return type is bool)\n\n```python\n>>> from pathlib import Path\n>>> from PathTraverser.utils.Path import first, last, part_count, starts_with\n>>>\n>>> first(Path('a/b/c')).name == 'a'\n>>> last(Path('a/b/c')).name == 'c'\n>>> part_count(Path('a/b/c')) == 3\n>>> starts_with(Path('a/b/c/d'), Path('a/b/c')) == True\n>>> starts_with(Path('a/b/c/d2'), Path('a/b/c/d')) == False\n```\n\nWe can also import these path utilities in the form of monkey patching the original pathlib.Path class:\n```python\n>>> from PathTraverser.utils.Path import Path  # this import executes also monkey patching of pathlib.Path\n>>>\n>>> Path('a/b/c').first.name == 'a'\n>>> Path('a/b/c').last.name == 'c'\n>>> Path('a/b/c').part_count == 3\n>>> Path('a/b/c/d').starts_with(Path('a/b/c')) == True\n```\n\n\n### More examples\nMore examples can be found in a test file of this project: **tests/testTraverser.py**\n",
    "bugtrack_url": null,
    "license": "MIT License\n        \n        Copyright (c) 2023 htarnacki\n        \n        Permission is hereby granted, free of charge, to any person obtaining a copy\n        of this software and associated documentation files (the \"Software\"), to deal\n        in the Software without restriction, including without limitation the rights\n        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n        copies of the Software, and to permit persons to whom the Software is\n        furnished to do so, subject to the following conditions:\n        \n        The above copyright notice and this permission notice shall be included in all\n        copies or substantial portions of the Software.\n        \n        THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n        SOFTWARE.",
    "summary": "Traversing file system in Python easily",
    "version": "2.0.0",
    "project_urls": {
        "Bug tracker": "https://github.com/htarnacki/PathTraverser/issues",
        "Homepage": "https://github.com/htarnacki/PathTraverser",
        "Repository": "https://github.com/htarnacki/PathTraverser"
    },
    "split_keywords": [
        "traverse",
        " walk",
        " list",
        " files",
        " directories",
        " dirs",
        " filesystem",
        " path",
        " paths"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "cafe351c1a89142cc352840adc6077a0f775ed0cb3705530e1c3810363e2a526",
                "md5": "5dce0f8774db7cd244f388eadd163a79",
                "sha256": "5635899e6d82c7e8ff2ed076fa879741d44bfca449a2304280a6cd0807ab2f9c"
            },
            "downloads": -1,
            "filename": "pathtraverser-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "5dce0f8774db7cd244f388eadd163a79",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 17172,
            "upload_time": "2024-08-14T19:16:45",
            "upload_time_iso_8601": "2024-08-14T19:16:45.024667Z",
            "url": "https://files.pythonhosted.org/packages/ca/fe/351c1a89142cc352840adc6077a0f775ed0cb3705530e1c3810363e2a526/pathtraverser-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "1e59dd21afce7bc3b506b8735fa96d4144ad86c741a7f0283fdd6941c2653d9d",
                "md5": "522693c99d740f81cf6dcae6afdd7a06",
                "sha256": "1f0cb38a11265096604f28722ec6168f92b6d51939eb916e1bd2479f0fd4fe8f"
            },
            "downloads": -1,
            "filename": "pathtraverser-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "522693c99d740f81cf6dcae6afdd7a06",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 17122,
            "upload_time": "2024-08-14T19:16:46",
            "upload_time_iso_8601": "2024-08-14T19:16:46.513627Z",
            "url": "https://files.pythonhosted.org/packages/1e/59/dd21afce7bc3b506b8735fa96d4144ad86c741a7f0283fdd6941c2653d9d/pathtraverser-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-14 19:16:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "htarnacki",
    "github_project": "PathTraverser",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "tox": true,
    "lcname": "pathtraverser"
}
        
Elapsed time: 0.29016s