if --(other-)repo option is not given, use default from environment

remove tests composing a repo+archive location with repo from env
and location from cli.
This commit is contained in:
Thomas Waldmann 2022-06-15 17:44:38 +02:00
parent 1bf2a6a240
commit 0f0cd24354
3 changed files with 29 additions and 113 deletions

View File

@ -174,8 +174,10 @@ def with_repository(fake=False, invert_fake=False, create=False, lock=True,
def decorator(method):
@functools.wraps(method)
def wrapper(self, args, **kwargs):
location = getattr(args, 'location')
if not location.valid: # location always must be given
raise Error('missing repository, please use --repo or BORG_REPO env var!')
lock = getattr(args, 'lock', _lock)
location = args.location # note: 'location' must be always present in args
append_only = getattr(args, 'append_only', False)
storage_quota = getattr(args, 'storage_quota', None)
make_parent_dirs = getattr(args, 'make_parent_dirs', False)
@ -220,8 +222,8 @@ def with_other_repository(manifest=False, key=False, cache=False, compatibility=
def decorator(method):
@functools.wraps(method)
def wrapper(self, args, **kwargs):
location = getattr(args, 'other_location', None)
if location is None: # nothing to do
location = getattr(args, 'other_location')
if not location.valid: # nothing to do
return method(self, args, **kwargs)
repository = get_repository(location, create=False, exclusive=True,
@ -3203,8 +3205,9 @@ class Archiver:
'compatible file can be generated by suffixing FILE with ".pyprof".')
add_common_option('--rsh', metavar='RSH', dest='rsh',
help="Use this command to connect to the 'borg serve' process (default: 'ssh')")
add_common_option('--repo', metavar='REPO', dest='location', type=location_validator(),
help="repository to use") # XXXYYY
add_common_option('--repo', metavar='REPO', dest='location',
type=location_validator(other=False), default=Location(other=False),
help="repository to use")
def define_exclude_and_patterns(add_option, *, tag_files=False, strip_components=False):
add_option('-e', '--exclude', metavar='PATTERN', dest='patterns',
@ -4165,7 +4168,7 @@ class Archiver:
subparser.add_argument('-n', '--dry-run', dest='dry_run', action='store_true',
help='do not change repository, just check')
subparser.add_argument('--other-repo', metavar='SRC_REPOSITORY', dest='other_location',
type=location_validator(other=True),
type=location_validator(other=True), default=Location(other=True),
help='source repository')
define_archive_filters_group(subparser)
@ -4502,7 +4505,7 @@ class Archiver:
help='initialize empty repository')
subparser.set_defaults(func=self.do_init)
subparser.add_argument('--other-repo', metavar='SRC_REPOSITORY', dest='other_location',
type=location_validator(other=True),
type=location_validator(other=True), default=Location(other=True),
help='reuse the key material from the other repository')
subparser.add_argument('-e', '--encryption', metavar='MODE', dest='encryption', required=True,
choices=key_argument_names(),

View File

@ -365,15 +365,6 @@ class Location:
local_re = re.compile(
local_path_re + optional_archive_re, re.VERBOSE) # local path with optional archive
# get the repo from BORG_REPO env and the optional archive from param.
# if the syntax requires giving REPOSITORY (see "borg mount"),
# use "::" to let it use the env var.
# if REPOSITORY argument is optional, it'll automatically use the env.
env_re = re.compile(r""" # the repo part is fetched from BORG_REPO
(?:::$) # just "::" is ok (when a pos. arg is required, no archive)
| # or
""" + optional_archive_re, re.VERBOSE) # archive name (optional, may be empty)
win_file_re = re.compile(r"""
(?:file://)? # optional file protocol
(?P<path>
@ -384,27 +375,29 @@ class Location:
def __init__(self, text='', overrides={}, other=False):
self.repo_env_var = 'BORG_OTHER_REPO' if other else 'BORG_REPO'
if not self.parse(text, overrides):
raise ValueError('Invalid location format: "%s"' % self.processed)
self.valid = False
self.proto = None
self.user = None
self._host = None
self.port = None
self.path = None
self.archive = None
self.parse(text, overrides)
def parse(self, text, overrides={}):
if not text:
# we did not get a text to parse, so we try to fetch from the environment
text = os.environ.get(self.repo_env_var)
if text is None:
return
self.raw = text # as given by user, might contain placeholders
self.processed = text = replace_placeholders(text, overrides) # after placeholder replacement
valid = self._parse(text)
self.processed = replace_placeholders(self.raw, overrides) # after placeholder replacement
valid = self._parse(self.processed)
if valid:
return True
m = self.env_re.match(text)
if not m:
return False
repo_raw = os.environ.get(self.repo_env_var)
if repo_raw is None:
return False
repo = replace_placeholders(repo_raw, overrides)
valid = self._parse(repo)
self.archive = m.group('archive')
self.raw = repo_raw if not self.archive else repo_raw + self.raw
self.processed = repo if not self.archive else f'{repo}::{self.archive}'
return valid
self.valid = True
else:
raise ValueError('Invalid location format: "%s"' % self.processed)
def _parse(self, text):
def normpath_special(p):

View File

@ -166,15 +166,6 @@ class TestLocationWithoutEnv:
assert repr(Location('path::archive-{utcnow}').with_timestamp(datetime(2002, 9, 19, tzinfo=timezone.utc))) == \
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive-2002-09-19T00:00:00')"
def test_underspecified(self, monkeypatch):
monkeypatch.delenv('BORG_REPO', raising=False)
with pytest.raises(ValueError):
Location('::archive')
with pytest.raises(ValueError):
Location('::')
with pytest.raises(ValueError):
Location()
def test_no_slashes(self, monkeypatch):
monkeypatch.delenv('BORG_REPO', raising=False)
with pytest.raises(ValueError):
@ -213,77 +204,6 @@ class TestLocationWithoutEnv:
assert loc_without_archive.processed == "ssh://user@host:1234/repos/%s" % hostname
class TestLocationWithEnv:
def test_ssh(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'ssh://user@host:1234/some/path')
assert repr(Location('::archive')) == \
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
assert repr(Location()) == \
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
def test_ssh_placeholder(self, monkeypatch):
from borg.platform import hostname
monkeypatch.setenv('BORG_REPO', 'ssh://user@host:1234/{hostname}')
assert repr(Location('::archive')) == \
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive='archive')"
assert repr(Location('::')) == \
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive=None)"
assert repr(Location()) == \
f"Location(proto='ssh', user='user', host='host', port=1234, path='/{hostname}', archive=None)"
def test_file(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'file:///some/path')
assert repr(Location('::archive')) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
def test_folder(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'path')
assert repr(Location('::archive')) == \
"Location(proto='file', user=None, host=None, port=None, path='path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='file', user=None, host=None, port=None, path='path', archive=None)"
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='path', archive=None)"
def test_abspath(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', '/some/absolute/path')
assert repr(Location('::archive')) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)"
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)"
def test_relpath(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'some/relative/path')
assert repr(Location('::archive')) == \
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)"
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)"
def test_with_colons(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', '/abs/path:w:cols')
assert repr(Location('::arch:col')) == \
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive='arch:col')"
assert repr(Location('::')) == \
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive=None)"
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='/abs/path:w:cols', archive=None)"
def test_no_slashes(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', '/some/absolute/path')
with pytest.raises(ValueError):
Location('::archive_name_with/slashes/is_invalid')
class FormatTimedeltaTestCase(BaseTestCase):
def test(self):