mirror of
https://github.com/borgbackup/borg.git
synced 2025-03-15 00:21:56 +00:00
Merge pull request #33 from jdchristensen/exclude
fix include/exclude bugs and add more tests
This commit is contained in:
commit
c22bc30a06
2 changed files with 37 additions and 17 deletions
|
@ -9,7 +9,7 @@ import stat
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from fnmatch import fnmatchcase
|
from fnmatch import translate
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ def adjust_patterns(paths, excludes):
|
||||||
|
|
||||||
def exclude_path(path, patterns):
|
def exclude_path(path, patterns):
|
||||||
"""Used by create and extract sub-commands to determine
|
"""Used by create and extract sub-commands to determine
|
||||||
if an item should be processed or not
|
whether or not an item should be processed.
|
||||||
"""
|
"""
|
||||||
for pattern in (patterns or []):
|
for pattern in (patterns or []):
|
||||||
if pattern.match(path):
|
if pattern.match(path):
|
||||||
|
@ -158,32 +158,43 @@ def exclude_path(path, patterns):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# For both IncludePattern and ExcludePattern, 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:
|
class IncludePattern:
|
||||||
"""--include PATTERN
|
"""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
|
||||||
|
path match as well. A trailing slash makes no difference.
|
||||||
"""
|
"""
|
||||||
def __init__(self, pattern):
|
def __init__(self, pattern):
|
||||||
self.pattern = pattern
|
self.pattern = pattern.rstrip(os.path.sep)+os.path.sep
|
||||||
|
|
||||||
def match(self, path):
|
def match(self, path):
|
||||||
dir, name = os.path.split(path)
|
return (path+os.path.sep).startswith(self.pattern)
|
||||||
return (path == self.pattern
|
|
||||||
or (dir + os.path.sep).startswith(self.pattern))
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (type(self), self.pattern)
|
return '%s(%s)' % (type(self), self.pattern)
|
||||||
|
|
||||||
|
|
||||||
class ExcludePattern(IncludePattern):
|
class ExcludePattern(IncludePattern):
|
||||||
"""
|
"""Shell glob patterns to exclude. A trailing slash means to
|
||||||
|
exclude the contents of a directory, but not the directory itself.
|
||||||
"""
|
"""
|
||||||
def __init__(self, pattern):
|
def __init__(self, pattern):
|
||||||
self.pattern = self.dirpattern = pattern
|
if pattern.endswith(os.path.sep):
|
||||||
if not pattern.endswith('/'):
|
self.pattern = pattern+'*'+os.path.sep
|
||||||
self.dirpattern += '/*'
|
else:
|
||||||
|
self.pattern = pattern+os.path.sep+'*'
|
||||||
|
# fnmatch and re.match both cache compiled regular expressions.
|
||||||
|
# Nevertheless, this is about 10 times faster.
|
||||||
|
self.regex = re.compile(translate(self.pattern))
|
||||||
|
|
||||||
def match(self, path):
|
def match(self, path):
|
||||||
dir, name = os.path.split(path)
|
return self.regex.match(path+os.path.sep) is not None
|
||||||
return fnmatchcase(path, self.pattern) or fnmatchcase(dir + '/', self.dirpattern)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%s)' % (type(self), self.pattern)
|
return '%s(%s)' % (type(self), self.pattern)
|
||||||
|
|
|
@ -50,7 +50,7 @@ class FormatTimedeltaTestCase(AtticTestCase):
|
||||||
class PatternTestCase(AtticTestCase):
|
class PatternTestCase(AtticTestCase):
|
||||||
|
|
||||||
files = [
|
files = [
|
||||||
'/etc/passwd', '/etc/hosts',
|
'/etc/passwd', '/etc/hosts', '/home',
|
||||||
'/home/user/.profile', '/home/user/.bashrc',
|
'/home/user/.profile', '/home/user/.bashrc',
|
||||||
'/home/user2/.profile', '/home/user2/public_html/index.html',
|
'/home/user2/.profile', '/home/user2/public_html/index.html',
|
||||||
'/var/log/messages', '/var/log/dmesg',
|
'/var/log/messages', '/var/log/dmesg',
|
||||||
|
@ -61,13 +61,22 @@ class PatternTestCase(AtticTestCase):
|
||||||
return [path for path in self.files if not exclude_path(path, patterns)]
|
return [path for path in self.files if not exclude_path(path, patterns)]
|
||||||
|
|
||||||
def test(self):
|
def test(self):
|
||||||
|
self.assert_equal(self.evaluate(['/'], []), self.files)
|
||||||
|
self.assert_equal(self.evaluate([], []), self.files)
|
||||||
|
self.assert_equal(self.evaluate(['/'], ['/h']), self.files)
|
||||||
self.assert_equal(self.evaluate(['/'], ['/home']),
|
self.assert_equal(self.evaluate(['/'], ['/home']),
|
||||||
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])
|
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])
|
||||||
|
self.assert_equal(self.evaluate(['/'], ['/home/']),
|
||||||
|
['/etc/passwd', '/etc/hosts', '/home', '/var/log/messages', '/var/log/dmesg'])
|
||||||
|
self.assert_equal(self.evaluate(['/home/u'], []), [])
|
||||||
|
self.assert_equal(self.evaluate(['/', '/home', '/etc/hosts'], ['/']), [])
|
||||||
|
self.assert_equal(self.evaluate(['/home/'], ['/home/user2']),
|
||||||
|
['/home', '/home/user/.profile', '/home/user/.bashrc'])
|
||||||
self.assert_equal(self.evaluate(['/'], ['*.profile', '/var/log']),
|
self.assert_equal(self.evaluate(['/'], ['*.profile', '/var/log']),
|
||||||
['/etc/passwd', '/etc/hosts', '/home/user/.bashrc', '/home/user2/public_html/index.html'])
|
['/etc/passwd', '/etc/hosts', '/home', '/home/user/.bashrc', '/home/user2/public_html/index.html'])
|
||||||
self.assert_equal(self.evaluate(['/'], ['/home/*/public_html', '*.profile', '*/log/*']),
|
self.assert_equal(self.evaluate(['/'], ['/home/*/public_html', '*.profile', '*/log/*']),
|
||||||
['/etc/passwd', '/etc/hosts', '/home/user/.bashrc'])
|
['/etc/passwd', '/etc/hosts', '/home', '/home/user/.bashrc'])
|
||||||
self.assert_equal(self.evaluate(['/etc', '/var'], ['dmesg']),
|
self.assert_equal(self.evaluate(['/etc/', '/var'], ['dmesg']),
|
||||||
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])
|
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue