Mercurial > hgrepos > Python > apps > py-cutils
diff cutils/util/fnmatch.py @ 302:bf88323d6bf7
treesum: Implement --exclude/--include.
- Filtering
- Document in output
- Handle in the "info" command
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Wed, 05 Mar 2025 10:07:44 +0100 |
| parents | 1fc117f5f9a1 |
| children | dc1f08937621 |
line wrap: on
line diff
--- a/cutils/util/fnmatch.py Wed Mar 05 10:06:38 2025 +0100 +++ b/cutils/util/fnmatch.py Wed Mar 05 10:07:44 2025 +0100 @@ -10,7 +10,12 @@ from __future__ import print_function, absolute_import -__all__ = [] +__all__ = ["FnMatcher"] + + +import re + +from . import glob HELP_DESCRIPTION = """\ @@ -22,6 +27,109 @@ re: regular expression path: plain text example (rooted), can be a file or a directory or a prefix thereof - filepath: exactly a single file, relative to the root of the tree + fullpath: exactly a single full path (file or directory), relative to the + root of the tree """ + + +def glob_factory(pattern): + + cpat = re.compile( + # automatically anchored + "\\A{}\\Z".format(glob.glob_to_regexp(pattern)), + re.DOTALL) + + def _glob_matcher(s): + return cpat.search(s) is not None + + return _glob_matcher + + +def iglob_factory(pattern): + + cpat = re.compile( + # automatically anchored + "\\A{}\\Z".format(glob.glob_to_regexp(pattern)), + re.DOTALL | re.IGNORECASE) + + def _iglob_matcher(s): + return cpat.search(s) is not None + + return _iglob_matcher + + +def re_factory(pattern): + + cpat = re.compile(pattern, re.DOTALL) + + def _re_matcher(s): + return cpat.search(s) is not None + + return _re_matcher + + +def path_factory(pattern): + + def _path_matcher(s): + return s.startswith(pattern) + + return _path_matcher + + +def fullpath_factory(pattern): + + def _fullpath_matcher(s): + return s == pattern + + return _fullpath_matcher + + +class FnMatcher(object): + + _registry = { + "glob": glob_factory, + "iglob": iglob_factory, + "re": re_factory, + "path": path_factory, + "fullpath": fullpath_factory, + } + + VISIT_DEFAULT = True # Overall default value for visiting + + def __init__(self, matchers): + super(FnMatcher, self).__init__() + self._matchers = matchers + + @classmethod + def build_from_commandline_patterns(klass, filter_definitions): + matchers = [] + for action, kpattern in filter_definitions: + kind, sep, pattern = kpattern.partition(':') + if not sep: + # use the default + kind = "glob" + pattern = kpattern + factory = klass._registry.get(kind, None) + if not factory: + raise RuntimeError("unknown pattern kind: {}".format(kind)) + matchers.append((action, kind, factory(pattern), pattern)) + + return klass(matchers) + + def shall_visit(self, fn): + visit = self.VISIT_DEFAULT + for action, kind, matcher, orig_pattern in self._matchers: + res = matcher(fn) + if res: + if action == "include": + visit = True + elif action == "exclude": + visit = False + else: + raise RuntimeError("unknown action: {}".format(action)) + return visit + + def definitions(self): + for action, kind, matcher, orig_pattern in self._matchers: + yield (action, kind, orig_pattern)
