Skip to content

Commit 691deae

Browse files
authored
Merge pull request #464 from atollk/issue_459_371
Add `filter_glob` and `exclude_glob` to fs.walk
2 parents 50b1c99 + d85d9db commit 691deae

File tree

8 files changed

+621
-159
lines changed

8 files changed

+621
-159
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88

99
## Unreleased
1010

11+
### Added
12+
13+
- Added `filter_glob` and `exclude_glob` parameters to `fs.walk.Walker`.
14+
Closes [#459](https://github.com/PyFilesystem/pyfilesystem2/issues/459).
15+
16+
### Fixed
17+
- Elaborated documentation of `filter_dirs` and `exclude_dirs` in `fs.walk.Walker`.
18+
Closes [#371](https://github.com/PyFilesystem/pyfilesystem2/issues/371).
19+
1120

1221
## [2.4.16] - 2022-05-02
1322

fs/base.py

+58-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from contextlib import closing
2222
from functools import partial, wraps
2323

24-
from . import copy, errors, fsencode, iotools, tools, walk, wildcard
24+
from . import copy, errors, fsencode, iotools, tools, walk, wildcard, glob
2525
from .copy import copy_modified_time
2626
from .glob import BoundGlobber
2727
from .mode import validate_open_mode
@@ -1696,6 +1696,63 @@ def match(self, patterns, name):
16961696
matcher = wildcard.get_matcher(patterns, case_sensitive)
16971697
return matcher(name)
16981698

1699+
def match_glob(self, patterns, path, accept_prefix=False):
1700+
# type: (Optional[Iterable[Text]], Text, bool) -> bool
1701+
"""Check if a path matches any of a list of glob patterns.
1702+
1703+
If a filesystem is case *insensitive* (such as Windows) then
1704+
this method will perform a case insensitive match (i.e. ``*.py``
1705+
will match the same names as ``*.PY``). Otherwise the match will
1706+
be case sensitive (``*.py`` and ``*.PY`` will match different
1707+
names).
1708+
1709+
Arguments:
1710+
patterns (list, optional): A list of patterns, e.g.
1711+
``['*.py']``, or `None` to match everything.
1712+
path (str): A resource path, starting with "/".
1713+
accept_prefix (bool): If ``True``, the path is
1714+
not required to match the patterns themselves
1715+
but only need to be a prefix of a string that does.
1716+
1717+
Returns:
1718+
bool: `True` if ``path`` matches any of the patterns.
1719+
1720+
Raises:
1721+
TypeError: If ``patterns`` is a single string instead of
1722+
a list (or `None`).
1723+
ValueError: If ``path`` is not a string starting with "/".
1724+
1725+
Example:
1726+
>>> my_fs.match_glob(['*.py'], '/__init__.py')
1727+
True
1728+
>>> my_fs.match_glob(['*.jpg', '*.png'], '/foo.gif')
1729+
False
1730+
>>> my_fs.match_glob(['dir/file.txt'], '/dir/', accept_prefix=True)
1731+
True
1732+
>>> my_fs.match_glob(['dir/file.txt'], '/dir/', accept_prefix=False)
1733+
False
1734+
>>> my_fs.match_glob(['dir/file.txt'], '/dir/gile.txt', accept_prefix=True)
1735+
False
1736+
1737+
Note:
1738+
If ``patterns`` is `None` (or ``['*']``), then this
1739+
method will always return `True`.
1740+
1741+
"""
1742+
if patterns is None:
1743+
return True
1744+
if not path or path[0] != "/":
1745+
raise ValueError("%s needs to be a string starting with /" % path)
1746+
if isinstance(patterns, six.text_type):
1747+
raise TypeError("patterns must be a list or sequence")
1748+
case_sensitive = not typing.cast(
1749+
bool, self.getmeta().get("case_insensitive", False)
1750+
)
1751+
matcher = glob.get_matcher(
1752+
patterns, case_sensitive, accept_prefix=accept_prefix
1753+
)
1754+
return matcher(path)
1755+
16991756
def tree(self, **kwargs):
17001757
# type: (**Any) -> None
17011758
"""Render a tree view of the filesystem to stdout or a file.

fs/errors.py

+17
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"OperationFailed",
4242
"OperationTimeout",
4343
"PathError",
44+
"PatternError",
4445
"PermissionDenied",
4546
"RemoteConnectionError",
4647
"RemoveRootError",
@@ -346,3 +347,19 @@ class UnsupportedHash(ValueError):
346347
not supported by hashlib.
347348
348349
"""
350+
351+
352+
class PatternError(ValueError):
353+
"""A string pattern with invalid syntax was given."""
354+
355+
default_message = "pattern '{pattern}' is invalid at position {position}"
356+
357+
def __init__(self, pattern, position, exc=None, msg=None): # noqa: D107
358+
# type: (Text, int, Optional[Exception], Optional[Text]) -> None
359+
self.pattern = pattern
360+
self.position = position
361+
self.exc = exc
362+
super(ValueError, self).__init__()
363+
364+
def __reduce__(self):
365+
return type(self), (self.path, self.position, self.exc, self._msg)

0 commit comments

Comments
 (0)