From 9747755131fadf909069d7a84dc793267af2c5ff Mon Sep 17 00:00:00 2001 From: Michael Hanselmann Date: Mon, 18 Jan 2016 13:32:49 +0100 Subject: [PATCH] Add pattern matcher wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The utility functions “adjust_patterns” and “exclude_path” produce respectively use a standard list object containing pattern objects. With the forthcoming introduction of patterns for filtering files to be extracted it's better to move the logic of these classes into a single class. The wrapper allows adding any number of patterns to an internal list together with a value to be returned if a match function finds that one of the patterns matches. A fallback value is returned otherwise. --- borg/helpers.py | 21 +++++++++++++++++++++ borg/testsuite/helpers.py | 25 ++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/borg/helpers.py b/borg/helpers.py index ce344e3f5..39aad5532 100644 --- a/borg/helpers.py +++ b/borg/helpers.py @@ -274,6 +274,27 @@ def exclude_path(path, patterns): return False +class PatternMatcher: + def __init__(self, fallback=None): + self._items = [] + + # Value to return from match function when none of the patterns match. + self.fallback = fallback + + def add(self, patterns, value): + """Add list of patterns to internal list. The given value is returned from the match function when one of the + given patterns matches. + """ + self._items.extend((i, value) for i in patterns) + + def match(self, path): + for (pattern, value) in self._items: + if pattern.match(path): + return value + + return self.fallback + + def normalized(func): """ Decorator for the Pattern match methods, returning a wrapper that normalizes OSX paths to match the normalized pattern on OSX, and diff --git a/borg/testsuite/helpers.py b/borg/testsuite/helpers.py index 725dba9b2..ee8a9e91d 100644 --- a/borg/testsuite/helpers.py +++ b/borg/testsuite/helpers.py @@ -12,7 +12,7 @@ import msgpack.fallback from ..helpers import exclude_path, Location, format_file_size, format_timedelta, PathPrefixPattern, FnmatchPattern, make_path_safe, \ prune_within, prune_split, get_cache_dir, Statistics, is_slow_msgpack, yes, RegexPattern, \ StableDict, int_to_bigint, bigint_to_int, parse_timestamp, CompressionSpec, ChunkerParams, \ - ProgressIndicatorPercent, ProgressIndicatorEndless, load_excludes, parse_pattern + ProgressIndicatorPercent, ProgressIndicatorEndless, load_excludes, parse_pattern, PatternMatcher from . import BaseTestCase, environment_variable, FakeInputs @@ -374,6 +374,29 @@ def test_parse_pattern_error(pattern): parse_pattern(pattern) +def test_pattern_matcher(): + pm = PatternMatcher() + + assert pm.fallback is None + + for i in ["", "foo", "bar"]: + assert pm.match(i) is None + + pm.add([RegexPattern("^a")], "A") + pm.add([RegexPattern("^b"), RegexPattern("^z")], "B") + pm.add([RegexPattern("^$")], "Empty") + pm.fallback = "FileNotFound" + + assert pm.match("") == "Empty" + assert pm.match("aaa") == "A" + assert pm.match("bbb") == "B" + assert pm.match("ccc") == "FileNotFound" + assert pm.match("xyz") == "FileNotFound" + assert pm.match("z") == "B" + + assert PatternMatcher(fallback="hey!").fallback == "hey!" + + def test_compression_specs(): with pytest.raises(ValueError): CompressionSpec('')