From 5d390d7953e6d76dd2fd4b87f66a59c634121ae6 Mon Sep 17 00:00:00 2001
From: Pavel Andreev
Date: Thu, 23 Jan 2025 15:58:43 +0000
Subject: [PATCH 1/2] Fix patterns parsing
---
borgmatic/actions/create.py | 21 ++++++++-------------
tests/unit/actions/test_create.py | 8 ++------
2 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py
index 126f791d..bbf042d1 100644
--- a/borgmatic/actions/create.py
+++ b/borgmatic/actions/create.py
@@ -15,7 +15,7 @@ import borgmatic.hooks.dispatch
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
return it.
@@ -26,9 +26,10 @@ def parse_pattern(pattern_line):
raise ValueError(f'Invalid pattern: {pattern_line}')
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:
- pattern_style = ''
+ pattern_style = default_style
path = remainder
return borgmatic.borg.pattern.Pattern(
@@ -60,11 +61,8 @@ def collect_patterns(config):
if pattern_line.strip()
)
+ 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', ())
)
+ tuple(
@@ -75,11 +73,8 @@ def collect_patterns(config):
if pattern_line.strip()
)
+ 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 exclude_line in open(filename).readlines()
if not exclude_line.lstrip().startswith('#')
diff --git a/tests/unit/actions/test_create.py b/tests/unit/actions/test_create.py
index 523e3dbc..cda86e95 100644
--- a/tests/unit/actions/test_create.py
+++ b/tests/unit/actions/test_create.py
@@ -47,9 +47,10 @@ def test_collect_patterns_parses_config_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('/bar', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
+ Pattern('**/baz', Pattern_type.EXCLUDE, Pattern_style.SHELL),
)
@@ -88,11 +89,6 @@ def test_collect_patterns_reads_config_exclude_from_file():
builtins.should_receive('open').with_args('file2.txt').and_return(
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('# 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('/baz').and_return(Pattern('/baz'))
assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),
From 73c196aa70d49505809b50750cfa1152ea963aab Mon Sep 17 00:00:00 2001
From: Pavel Andreev
Date: Thu, 23 Jan 2025 19:49:10 +0000
Subject: [PATCH 2/2] Fix according to review comments
---
borgmatic/actions/create.py | 4 ++--
tests/unit/actions/test_create.py | 24 +++++++++++++++++++-----
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py
index bbf042d1..ffbecd2e 100644
--- a/borgmatic/actions/create.py
+++ b/borgmatic/actions/create.py
@@ -55,7 +55,7 @@ def collect_patterns(config):
for source_directory in config.get('source_directories', ())
)
+ tuple(
- parse_pattern(pattern_line.strip())
+ parse_pattern(pattern_line.strip(), borgmatic.borg.pattern.Pattern_style.SHELL)
for pattern_line in config.get('patterns', ())
if not pattern_line.lstrip().startswith('#')
if pattern_line.strip()
@@ -66,7 +66,7 @@ def collect_patterns(config):
for exclude_line in config.get('exclude_patterns', ())
)
+ 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 pattern_line in open(filename).readlines()
if not pattern_line.lstrip().startswith('#')
diff --git a/tests/unit/actions/test_create.py b/tests/unit/actions/test_create.py
index cda86e95..e8322876 100644
--- a/tests/unit/actions/test_create.py
+++ b/tests/unit/actions/test_create.py
@@ -34,11 +34,13 @@ def test_collect_patterns_converts_source_directories():
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('').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']}) == (
Pattern('/foo'),
@@ -60,12 +62,15 @@ def test_collect_patterns_reads_config_patterns_from_file():
builtins.should_receive('open').with_args('file2.txt').and_return(
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('').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']}) == (
Pattern('/foo'),
@@ -89,6 +94,15 @@ def test_collect_patterns_reads_config_exclude_from_file():
builtins.should_receive('open').with_args('file2.txt').and_return(
io.StringIO('/bar\n# comment\n\n \n/baz')
)
+ 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('').never()
+ flexmock(module).should_receive('parse_pattern').with_args(' ').never()
+ 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']}) == (
Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),