diff --git a/NEWS b/NEWS index 26c15f1d..f062e4d3 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +1.9.7 + * #970: Fix for an error when a blank line occurs in the configured patterns or excludes. + 1.9.6 * #959: Fix an error in the Btrfs hook when a subvolume mounted at "/" is configured in borgmatic's source directories. diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py index e26bca56..d0d88385 100644 --- a/borgmatic/actions/create.py +++ b/borgmatic/actions/create.py @@ -23,7 +23,7 @@ def parse_pattern(pattern_line): try: (pattern_type, remainder) = pattern_line.split(' ', maxsplit=1) except ValueError: - raise ValueError('Invalid pattern:', pattern_line) + raise ValueError(f'Invalid pattern: {pattern_line}') try: (pattern_style, path) = remainder.split(':', maxsplit=1) @@ -57,6 +57,7 @@ def collect_patterns(config): parse_pattern(pattern_line.strip()) for pattern_line in config.get('patterns', ()) if not pattern_line.lstrip().startswith('#') + if pattern_line.strip() ) + tuple( borgmatic.borg.pattern.Pattern( @@ -71,6 +72,7 @@ def collect_patterns(config): for filename in config.get('patterns_from', ()) for pattern_line in open(filename).readlines() if not pattern_line.lstrip().startswith('#') + if pattern_line.strip() ) + tuple( borgmatic.borg.pattern.Pattern( @@ -81,6 +83,7 @@ def collect_patterns(config): for filename in config.get('excludes_from', ()) for exclude_line in open(filename).readlines() if not exclude_line.lstrip().startswith('#') + if exclude_line.strip() ) ) except (FileNotFoundError, OSError) as error: diff --git a/pyproject.toml b/pyproject.toml index 9bb5e34e..9c429115 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "borgmatic" -version = "1.9.6" +version = "1.9.7.dev0" authors = [ { name="Dan Helfman", email="witten@torsion.org" }, ] diff --git a/tests/unit/actions/test_create.py b/tests/unit/actions/test_create.py index ae197c05..4c5ecf80 100644 --- a/tests/unit/actions/test_create.py +++ b/tests/unit/actions/test_create.py @@ -36,9 +36,11 @@ 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('# 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')) - assert module.collect_patterns({'patterns': ['R /foo', '# comment', 'R /bar']}) == ( + assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', ' ', 'R /bar']}) == ( Pattern('/foo'), Pattern('/bar'), ) @@ -55,10 +57,12 @@ def test_collect_patterns_reads_config_patterns_from_file(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('R /foo')) builtins.should_receive('open').with_args('file2.txt').and_return( - io.StringIO('R /bar\n# comment\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('# 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')) @@ -82,8 +86,13 @@ def test_collect_patterns_reads_config_excludes_from_file(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('file1.txt').and_return(io.StringIO('/foo')) builtins.should_receive('open').with_args('file2.txt').and_return( - io.StringIO('/bar\n# comment\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('# 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({'excludes_from': ['file1.txt', 'file2.txt']}) == ( Pattern('/foo', Pattern_type.EXCLUDE, Pattern_style.FNMATCH),