mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-26 01:37:20 +00:00
Merge pull request #2288 from ThomasWaldmann/patterns-style-default
support setting the pattern style default in patterns file
This commit is contained in:
commit
16e6e3d989
3 changed files with 56 additions and 12 deletions
|
@ -1663,7 +1663,7 @@ def do_break_lock(self, args, repository):
|
|||
|
||||
Note that the default pattern style for `--pattern` and `--patterns-from` is
|
||||
shell style (`sh:`), so those patterns behave similar to rsync include/exclude
|
||||
patterns.
|
||||
patterns. The pattern style can be set via the `P` prefix.
|
||||
|
||||
Patterns (`--pattern`) and excludes (`--exclude`) from the command line are
|
||||
considered first (in the order of appearance). Then patterns from `--patterns-from`
|
||||
|
@ -1671,6 +1671,8 @@ def do_break_lock(self, args, repository):
|
|||
|
||||
An example `--patterns-from` file could look like that::
|
||||
|
||||
# "sh:" pattern style is the default, so the following line is not needed:
|
||||
P sh
|
||||
R /
|
||||
# can be rebuild
|
||||
- /home/*/.cache
|
||||
|
|
|
@ -391,18 +391,23 @@ def parse_timestamp(timestamp):
|
|||
return datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S').replace(tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def parse_add_pattern(patternstr, roots, patterns):
|
||||
def parse_add_pattern(patternstr, roots, patterns, fallback):
|
||||
"""Parse a pattern string and add it to roots or patterns depending on the pattern type."""
|
||||
pattern = parse_inclexcl_pattern(patternstr)
|
||||
pattern = parse_inclexcl_pattern(patternstr, fallback=fallback)
|
||||
if pattern.ptype is RootPath:
|
||||
roots.append(pattern.pattern)
|
||||
elif pattern.ptype is PatternStyle:
|
||||
fallback = pattern.pattern
|
||||
else:
|
||||
patterns.append(pattern)
|
||||
return fallback
|
||||
|
||||
|
||||
def load_pattern_file(fileobj, roots, patterns):
|
||||
def load_pattern_file(fileobj, roots, patterns, fallback=None):
|
||||
if fallback is None:
|
||||
fallback = ShellPattern # ShellPattern is defined later in this module
|
||||
for patternstr in clean_lines(fileobj):
|
||||
parse_add_pattern(patternstr, roots, patterns)
|
||||
fallback = parse_add_pattern(patternstr, roots, patterns, fallback)
|
||||
|
||||
|
||||
def load_exclude_file(fileobj, patterns):
|
||||
|
@ -415,7 +420,7 @@ def __init__(self, nargs=1, **kw):
|
|||
super().__init__(nargs=nargs, **kw)
|
||||
|
||||
def __call__(self, parser, args, values, option_string=None):
|
||||
parse_add_pattern(values[0], args.paths, args.patterns)
|
||||
parse_add_pattern(values[0], args.paths, args.patterns, ShellPattern)
|
||||
|
||||
|
||||
class ArgparsePatternFileAction(argparse.Action):
|
||||
|
@ -614,6 +619,14 @@ def _match(self, path):
|
|||
|
||||
InclExclPattern = namedtuple('InclExclPattern', 'pattern ptype')
|
||||
RootPath = object()
|
||||
PatternStyle = object()
|
||||
|
||||
|
||||
def get_pattern_style(prefix):
|
||||
try:
|
||||
return _PATTERN_STYLE_BY_PREFIX[prefix]
|
||||
except KeyError:
|
||||
raise ValueError("Unknown pattern style: {}".format(prefix)) from None
|
||||
|
||||
|
||||
def parse_pattern(pattern, fallback=FnmatchPattern):
|
||||
|
@ -621,14 +634,9 @@ def parse_pattern(pattern, fallback=FnmatchPattern):
|
|||
"""
|
||||
if len(pattern) > 2 and pattern[2] == ":" and pattern[:2].isalnum():
|
||||
(style, pattern) = (pattern[:2], pattern[3:])
|
||||
|
||||
cls = _PATTERN_STYLE_BY_PREFIX.get(style, None)
|
||||
|
||||
if cls is None:
|
||||
raise ValueError("Unknown pattern style: {}".format(style))
|
||||
cls = get_pattern_style(style)
|
||||
else:
|
||||
cls = fallback
|
||||
|
||||
return cls(pattern)
|
||||
|
||||
|
||||
|
@ -646,6 +654,8 @@ def parse_inclexcl_pattern(pattern, fallback=ShellPattern):
|
|||
'+': True,
|
||||
'R': RootPath,
|
||||
'r': RootPath,
|
||||
'P': PatternStyle,
|
||||
'p': PatternStyle,
|
||||
}
|
||||
try:
|
||||
ptype = type_prefix_map[pattern[0]]
|
||||
|
@ -656,6 +666,11 @@ def parse_inclexcl_pattern(pattern, fallback=ShellPattern):
|
|||
raise argparse.ArgumentTypeError("Unable to parse pattern: {}".format(pattern))
|
||||
if ptype is RootPath:
|
||||
pobj = pattern
|
||||
elif ptype is PatternStyle:
|
||||
try:
|
||||
pobj = get_pattern_style(pattern)
|
||||
except ValueError:
|
||||
raise argparse.ArgumentTypeError("Unable to parse pattern: {}".format(pattern))
|
||||
else:
|
||||
pobj = parse_pattern(pattern, fallback)
|
||||
return InclExclPattern(pobj, ptype)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import argparse
|
||||
import hashlib
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
@ -497,6 +498,32 @@ def evaluate(filename):
|
|||
assert numpatterns == expected_numpatterns
|
||||
|
||||
|
||||
def test_switch_patterns_style():
|
||||
patterns = """\
|
||||
+0_initial_default_is_shell
|
||||
p fm
|
||||
+1_fnmatch
|
||||
P re
|
||||
+2_regex
|
||||
+3_more_regex
|
||||
P pp
|
||||
+4_pathprefix
|
||||
p fm
|
||||
p sh
|
||||
+5_shell
|
||||
"""
|
||||
pattern_file = io.StringIO(patterns)
|
||||
roots, patterns = [], []
|
||||
load_pattern_file(pattern_file, roots, patterns)
|
||||
assert len(patterns) == 6
|
||||
assert isinstance(patterns[0].pattern, ShellPattern)
|
||||
assert isinstance(patterns[1].pattern, FnmatchPattern)
|
||||
assert isinstance(patterns[2].pattern, RegexPattern)
|
||||
assert isinstance(patterns[3].pattern, RegexPattern)
|
||||
assert isinstance(patterns[4].pattern, PathPrefixPattern)
|
||||
assert isinstance(patterns[5].pattern, ShellPattern)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lines", [
|
||||
(["X /data"]), # illegal pattern type prefix
|
||||
(["/data"]), # need a pattern type prefix
|
||||
|
|
Loading…
Reference in a new issue