Browse Source

Fix exclude patterns parsing to support pattern styles (#977).

Reviewed-on: https://projects.torsion.org/borgmatic-collective/borgmatic/pulls/976
Dan Helfman 4 months ago
parent
commit
92b8c0230e
2 changed files with 28 additions and 23 deletions
  1. 10 15
      borgmatic/actions/create.py
  2. 18 8
      tests/unit/actions/test_create.py

+ 10 - 15
borgmatic/actions/create.py

@@ -15,7 +15,7 @@ import borgmatic.hooks.dispatch
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
-def parse_pattern(pattern_line):
+def parse_pattern(pattern_line, default_style=borgmatic.borg.pattern.Pattern_style.NONE):
     '''
     '''
     Given a Borg pattern as a string, parse it into a borgmatic.borg.pattern.Pattern instance and
     Given a Borg pattern as a string, parse it into a borgmatic.borg.pattern.Pattern instance and
     return it.
     return it.
@@ -26,9 +26,10 @@ def parse_pattern(pattern_line):
         raise ValueError(f'Invalid pattern: {pattern_line}')
         raise ValueError(f'Invalid pattern: {pattern_line}')
 
 
     try:
     try:
-        (pattern_style, path) = remainder.split(':', maxsplit=1)
+        (parsed_pattern_style, path) = remainder.split(':', maxsplit=1)
+        pattern_style = borgmatic.borg.pattern.Pattern_style(parsed_pattern_style)
     except ValueError:
     except ValueError:
-        pattern_style = ''
+        pattern_style = default_style
         path = remainder
         path = remainder
 
 
     return borgmatic.borg.pattern.Pattern(
     return borgmatic.borg.pattern.Pattern(
@@ -54,32 +55,26 @@ def collect_patterns(config):
                 for source_directory in config.get('source_directories', ())
                 for source_directory in config.get('source_directories', ())
             )
             )
             + tuple(
             + tuple(
-                parse_pattern(pattern_line.strip())
+                parse_pattern(pattern_line.strip(), borgmatic.borg.pattern.Pattern_style.SHELL)
                 for pattern_line in config.get('patterns', ())
                 for pattern_line in config.get('patterns', ())
                 if not pattern_line.lstrip().startswith('#')
                 if not pattern_line.lstrip().startswith('#')
                 if pattern_line.strip()
                 if pattern_line.strip()
             )
             )
             + tuple(
             + tuple(
-                borgmatic.borg.pattern.Pattern(
-                    exclude_line.strip(),
-                    borgmatic.borg.pattern.Pattern_type.EXCLUDE,
-                    borgmatic.borg.pattern.Pattern_style.FNMATCH,
-                )
+                parse_pattern(f'{borgmatic.borg.pattern.Pattern_type.EXCLUDE.value} {
+                              exclude_line.strip()}', borgmatic.borg.pattern.Pattern_style.FNMATCH)
                 for exclude_line in config.get('exclude_patterns', ())
                 for exclude_line in config.get('exclude_patterns', ())
             )
             )
             + tuple(
             + tuple(
-                parse_pattern(pattern_line.strip())
+                parse_pattern(pattern_line.strip(), borgmatic.borg.pattern.Pattern_style.SHELL)
                 for filename in config.get('patterns_from', ())
                 for filename in config.get('patterns_from', ())
                 for pattern_line in open(filename).readlines()
                 for pattern_line in open(filename).readlines()
                 if not pattern_line.lstrip().startswith('#')
                 if not pattern_line.lstrip().startswith('#')
                 if pattern_line.strip()
                 if pattern_line.strip()
             )
             )
             + tuple(
             + tuple(
-                borgmatic.borg.pattern.Pattern(
-                    exclude_line.strip(),
-                    borgmatic.borg.pattern.Pattern_type.EXCLUDE,
-                    borgmatic.borg.pattern.Pattern_style.FNMATCH,
-                )
+                parse_pattern(f'{borgmatic.borg.pattern.Pattern_type.EXCLUDE.value} {
+                    exclude_line.strip()}', borgmatic.borg.pattern.Pattern_style.FNMATCH)
                 for filename in config.get('exclude_from', ())
                 for filename in config.get('exclude_from', ())
                 for exclude_line in open(filename).readlines()
                 for exclude_line in open(filename).readlines()
                 if not exclude_line.lstrip().startswith('#')
                 if not exclude_line.lstrip().startswith('#')

+ 18 - 8
tests/unit/actions/test_create.py

@@ -34,11 +34,13 @@ def test_collect_patterns_converts_source_directories():
 
 
 
 
 def test_collect_patterns_parses_config_patterns():
 def test_collect_patterns_parses_config_patterns():
-    flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        'R /foo', Pattern_style.SHELL).and_return(Pattern('/foo'))
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
-    flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        'R /bar', Pattern_style.SHELL).and_return(Pattern('/bar'))
 
 
     assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', '   ', 'R /bar']}) == (
     assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', '   ', 'R /bar']}) == (
         Pattern('/foo'),
         Pattern('/foo'),
@@ -47,9 +49,10 @@ def test_collect_patterns_parses_config_patterns():
 
 
 
 
 def test_collect_patterns_converts_exclude_patterns():
 def test_collect_patterns_converts_exclude_patterns():
-    assert module.collect_patterns({'exclude_patterns': ['/foo', '/bar']}) == (
+    assert module.collect_patterns({'exclude_patterns': ['/foo', '/bar', 'sh:**/baz']}) == (
         Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
         Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
         Pattern('/bar', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
         Pattern('/bar', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
+        Pattern('**/baz', Pattern_type.EXCLUDE, Pattern_style.SHELL),
     )
     )
 
 
 
 
@@ -59,12 +62,15 @@ def test_collect_patterns_reads_config_patterns_from_file():
     builtins.should_receive('open').with_args('file2.txt').and_return(
     builtins.should_receive('open').with_args('file2.txt').and_return(
         io.StringIO('R /bar\n# comment\n\n   \nR /baz')
         io.StringIO('R /bar\n# comment\n\n   \nR /baz')
     )
     )
-    flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        'R /foo', Pattern_style.SHELL).and_return(Pattern('/foo'))
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
-    flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
-    flexmock(module).should_receive('parse_pattern').with_args('R /baz').and_return(Pattern('/baz'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        'R /bar', Pattern_style.SHELL).and_return(Pattern('/bar'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        'R /baz', Pattern_style.SHELL).and_return(Pattern('/baz'))
 
 
     assert module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']}) == (
     assert module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']}) == (
         Pattern('/foo'),
         Pattern('/foo'),
@@ -88,11 +94,15 @@ def test_collect_patterns_reads_config_exclude_from_file():
     builtins.should_receive('open').with_args('file2.txt').and_return(
     builtins.should_receive('open').with_args('file2.txt').and_return(
         io.StringIO('/bar\n# comment\n\n   \n/baz')
         io.StringIO('/bar\n# comment\n\n   \n/baz')
     )
     )
-    flexmock(module).should_receive('parse_pattern').with_args('/bar').and_return(Pattern('/bar'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        '- /foo', default_style=Pattern_style.FNMATCH).and_return(Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        '- /bar', default_style=Pattern_style.FNMATCH).and_return(Pattern('/bar', Pattern_type.EXCLUDE, Pattern_style.FNMATCH))
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
     flexmock(module).should_receive('parse_pattern').with_args('   ').never()
-    flexmock(module).should_receive('parse_pattern').with_args('/baz').and_return(Pattern('/baz'))
+    flexmock(module).should_receive('parse_pattern').with_args(
+        '- /baz', default_style=Pattern_style.FNMATCH).and_return(Pattern('/baz', Pattern_type.EXCLUDE, Pattern_style.FNMATCH))
 
 
     assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
     assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
         Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
         Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),