Browse Source

Merge pull request #557 from hansmi/pattern-cleanup

Rename pattern classes and refactor tests
TW 9 năm trước cách đây
mục cha
commit
d248a7d537
3 tập tin đã thay đổi với 57 bổ sung112 xóa
  1. 2 2
      borg/archiver.py
  2. 8 8
      borg/helpers.py
  3. 47 102
      borg/testsuite/helpers.py

+ 2 - 2
borg/archiver.py

@@ -17,7 +17,7 @@ import traceback
 
 from . import __version__
 from .helpers import Error, location_validator, format_time, format_file_size, \
-    format_file_mode, parse_pattern, IncludePattern, exclude_path, adjust_patterns, to_localtime, timestamp, \
+    format_file_mode, parse_pattern, PathPrefixPattern, exclude_path, adjust_patterns, to_localtime, timestamp, \
     get_cache_dir, get_keys_dir, prune_within, prune_split, unhexlify, \
     Manifest, remove_surrogates, update_excludes, format_archive, check_extension_modules, Statistics, \
     dir_is_tagged, bigint_to_int, ChunkerParams, CompressionSpec, is_slow_msgpack, yes, sysinfo, \
@@ -314,7 +314,7 @@ class Archiver:
             while dirs:
                 archive.extract_item(dirs.pop(-1))
         for pattern in (patterns or []):
-            if isinstance(pattern, IncludePattern) and  pattern.match_count == 0:
+            if isinstance(pattern, PathPrefixPattern) and pattern.match_count == 0:
                 self.print_warning("Include pattern '%s' never matched.", pattern)
         return self.exit_code
 

+ 8 - 8
borg/helpers.py

@@ -259,7 +259,7 @@ def update_excludes(args):
 
 def adjust_patterns(paths, excludes):
     if paths:
-        return (excludes or []) + [IncludePattern(path) for path in paths] + [ExcludePattern('*')]
+        return (excludes or []) + [PathPrefixPattern(path) for path in paths] + [FnmatchPattern('*')]
     else:
         return excludes
 
@@ -270,7 +270,7 @@ def exclude_path(path, patterns):
     """
     for pattern in (patterns or []):
         if pattern.match(path):
-            return isinstance(pattern, (ExcludePattern, ExcludeRegex))
+            return isinstance(pattern, (FnmatchPattern, RegexPattern))
     return False
 
 
@@ -326,14 +326,14 @@ class PatternBase:
         raise NotImplementedError
 
 
-# For both IncludePattern and ExcludePattern, we require that
+# For both PathPrefixPattern and FnmatchPattern, we require that
 # the pattern either match the whole path or an initial segment
 # of the path up to but not including a path separator.  To
 # unify the two cases, we add a path separator to the end of
 # the path before matching.
 
 
-class IncludePattern(PatternBase):
+class PathPrefixPattern(PatternBase):
     """Literal files or directories listed on the command line
     for some operations (e.g. extract, but not create).
     If a directory is specified, all paths that start with that
@@ -346,7 +346,7 @@ class IncludePattern(PatternBase):
         return (path + os.path.sep).startswith(self.pattern)
 
 
-class ExcludePattern(PatternBase):
+class FnmatchPattern(PatternBase):
     """Shell glob patterns to exclude.  A trailing slash means to
     exclude the contents of a directory, but not the directory itself.
     """
@@ -366,7 +366,7 @@ class ExcludePattern(PatternBase):
         return (self.regex.match(path + os.path.sep) is not None)
 
 
-class ExcludeRegex(PatternBase):
+class RegexPattern(PatternBase):
     """Regular expression to exclude.
     """
     def _prepare(self, pattern):
@@ -383,8 +383,8 @@ class ExcludeRegex(PatternBase):
 
 _DEFAULT_PATTERN_STYLE = "fm"
 _PATTERN_STYLES = {
-        "fm": ExcludePattern,
-        "re": ExcludeRegex,
+        "fm": FnmatchPattern,
+        "re": RegexPattern,
         }
 
 

+ 47 - 102
borg/testsuite/helpers.py

@@ -9,8 +9,8 @@ import sys
 import msgpack
 import msgpack.fallback
 
-from ..helpers import adjust_patterns, exclude_path, Location, format_file_size, format_timedelta, IncludePattern, ExcludePattern, make_path_safe, \
-    prune_within, prune_split, get_cache_dir, Statistics, is_slow_msgpack, yes, ExcludeRegex, \
+from ..helpers import adjust_patterns, 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
 from . import BaseTestCase, environment_variable, FakeInputs
@@ -193,7 +193,7 @@ def test_patterns(paths, excludes, expected):
         '/var/log/messages', '/var/log/dmesg',
     ]
 
-    check_patterns(files, paths, [ExcludePattern(p) for p in excludes], expected)
+    check_patterns(files, paths, [FnmatchPattern(p) for p in excludes], expected)
 
 
 @pytest.mark.parametrize("paths, excludes, expected", [
@@ -218,7 +218,7 @@ def test_patterns_regex(paths, excludes, expected):
     patterns = []
 
     for i in excludes:
-        pat = ExcludeRegex(i)
+        pat = RegexPattern(i)
         assert str(pat) == i
         assert pat.pattern == i
         patterns.append(pat)
@@ -228,93 +228,38 @@ def test_patterns_regex(paths, excludes, expected):
 
 def test_regex_pattern():
     # The forward slash must match the platform-specific path separator
-    assert ExcludeRegex("^/$").match("/")
-    assert ExcludeRegex("^/$").match(os.path.sep)
-    assert not ExcludeRegex(r"^\\$").match("/")
-
-
-@pytest.mark.skipif(sys.platform in ('darwin',), reason='all but OS X test')
-class PatternNonAsciiTestCase(BaseTestCase):
-    def testComposedUnicode(self):
-        pattern = 'b\N{LATIN SMALL LETTER A WITH ACUTE}'
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert i.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert not i.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert e.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert not e.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert er.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert not er.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-
-    def testDecomposedUnicode(self):
-        pattern = 'ba\N{COMBINING ACUTE ACCENT}'
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert not i.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert i.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert not e.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert e.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert not er.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert er.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-
-    def testInvalidUnicode(self):
-        pattern = str(b'ba\x80', 'latin1')
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert not i.match("ba/foo")
-        assert i.match(str(b"ba\x80/foo", 'latin1'))
-        assert not e.match("ba/foo")
-        assert e.match(str(b"ba\x80/foo", 'latin1'))
-        assert not er.match("ba/foo")
-        assert er.match(str(b"ba\x80/foo", 'latin1'))
-
-
-@pytest.mark.skipif(sys.platform not in ('darwin',), reason='OS X test')
-class OSXPatternNormalizationTestCase(BaseTestCase):
-    def testComposedUnicode(self):
-        pattern = 'b\N{LATIN SMALL LETTER A WITH ACUTE}'
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert i.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert i.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert e.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert e.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert er.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert er.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-
-    def testDecomposedUnicode(self):
-        pattern = 'ba\N{COMBINING ACUTE ACCENT}'
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert i.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert i.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert e.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert e.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-        assert er.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
-        assert er.match("ba\N{COMBINING ACUTE ACCENT}/foo")
-
-    def testInvalidUnicode(self):
-        pattern = str(b'ba\x80', 'latin1')
-        i = IncludePattern(pattern)
-        e = ExcludePattern(pattern)
-        er = ExcludeRegex("^{}/foo$".format(pattern))
-
-        assert not i.match("ba/foo")
-        assert i.match(str(b"ba\x80/foo", 'latin1'))
-        assert not e.match("ba/foo")
-        assert e.match(str(b"ba\x80/foo", 'latin1'))
-        assert not er.match("ba/foo")
-        assert er.match(str(b"ba\x80/foo", 'latin1'))
+    assert RegexPattern("^/$").match("/")
+    assert RegexPattern("^/$").match(os.path.sep)
+    assert not RegexPattern(r"^\\$").match("/")
+
+
+def use_normalized_unicode():
+    return sys.platform in ("darwin",)
+
+
+def _make_test_patterns(pattern):
+    return [PathPrefixPattern(pattern),
+            FnmatchPattern(pattern),
+            RegexPattern("^{}/foo$".format(pattern)),
+            ]
+
+
+@pytest.mark.parametrize("pattern", _make_test_patterns("b\N{LATIN SMALL LETTER A WITH ACUTE}"))
+def test_composed_unicode_pattern(pattern):
+    assert pattern.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo")
+    assert pattern.match("ba\N{COMBINING ACUTE ACCENT}/foo") == use_normalized_unicode()
+
+
+@pytest.mark.parametrize("pattern", _make_test_patterns("ba\N{COMBINING ACUTE ACCENT}"))
+def test_decomposed_unicode_pattern(pattern):
+    assert pattern.match("b\N{LATIN SMALL LETTER A WITH ACUTE}/foo") == use_normalized_unicode()
+    assert pattern.match("ba\N{COMBINING ACUTE ACCENT}/foo")
+
+
+@pytest.mark.parametrize("pattern", _make_test_patterns(str(b"ba\x80", "latin1")))
+def test_invalid_unicode_pattern(pattern):
+    assert not pattern.match("ba/foo")
+    assert pattern.match(str(b"ba\x80/foo", "latin1"))
 
 
 @pytest.mark.parametrize("lines, expected", [
@@ -366,23 +311,23 @@ def test_patterns_from_file(tmpdir, lines, expected):
 
 
 @pytest.mark.parametrize("pattern, cls", [
-    ("", ExcludePattern),
+    ("", FnmatchPattern),
 
     # Default style
-    ("*", ExcludePattern),
-    ("/data/*", ExcludePattern),
+    ("*", FnmatchPattern),
+    ("/data/*", FnmatchPattern),
 
     # fnmatch style
-    ("fm:", ExcludePattern),
-    ("fm:*", ExcludePattern),
-    ("fm:/data/*", ExcludePattern),
-    ("fm:fm:/data/*", ExcludePattern),
+    ("fm:", FnmatchPattern),
+    ("fm:*", FnmatchPattern),
+    ("fm:/data/*", FnmatchPattern),
+    ("fm:fm:/data/*", FnmatchPattern),
 
     # Regular expression
-    ("re:", ExcludeRegex),
-    ("re:.*", ExcludeRegex),
-    ("re:^/something/", ExcludeRegex),
-    ("re:re:^/something/", ExcludeRegex),
+    ("re:", RegexPattern),
+    ("re:.*", RegexPattern),
+    ("re:^/something/", RegexPattern),
+    ("re:re:^/something/", RegexPattern),
     ])
 def test_parse_pattern(pattern, cls):
     assert isinstance(parse_pattern(pattern), cls)