ipv6 address support

also: Location: more informative exception when parsing fails
This commit is contained in:
Benedikt Neuffer 2017-03-06 20:46:03 +01:00 committed by Thomas Waldmann
parent 5a667d6f6a
commit d99aff1276
2 changed files with 47 additions and 3 deletions

View File

@ -814,7 +814,7 @@ class Location:
ssh_re = re.compile(r""" ssh_re = re.compile(r"""
(?P<proto>ssh):// # ssh:// (?P<proto>ssh):// # ssh://
""" + optional_user_re + r""" # user@ (optional) """ + optional_user_re + r""" # user@ (optional)
(?P<host>[^:/]+)(?::(?P<port>\d+))? # host or host:port (?P<host>([^:/]+|\[[0-9a-fA-F:.]+\]))(?::(?P<port>\d+))? # host or host:port or [ipv6] or [ipv6]:port
""" + abs_path_re + optional_archive_re, re.VERBOSE) # path or path::archive """ + abs_path_re + optional_archive_re, re.VERBOSE) # path or path::archive
file_re = re.compile(r""" file_re = re.compile(r"""
@ -825,7 +825,7 @@ class Location:
scp_re = re.compile(r""" scp_re = re.compile(r"""
( (
""" + optional_user_re + r""" # user@ (optional) """ + optional_user_re + r""" # user@ (optional)
(?P<host>[^:/]+): # host: (don't match / in host to disambiguate from file:) (?P<host>([^:/]+|\[[0-9a-fA-F:.]+\])): # host: (don't match / or [ipv6] in host to disambiguate from file:)
)? # user@host: part is optional )? # user@host: part is optional
""" + scp_path_re + optional_archive_re, re.VERBOSE) # path with optional archive """ + scp_path_re + optional_archive_re, re.VERBOSE) # path with optional archive
@ -841,7 +841,7 @@ class Location:
def __init__(self, text=''): def __init__(self, text=''):
self.orig = text self.orig = text
if not self.parse(self.orig): if not self.parse(self.orig):
raise ValueError raise ValueError('Location: parse failed: %s' % self.orig)
def parse(self, text): def parse(self, text):
text = replace_placeholders(text) text = replace_placeholders(text)
@ -872,6 +872,8 @@ class Location:
self.proto = m.group('proto') self.proto = m.group('proto')
self.user = m.group('user') self.user = m.group('user')
self.host = m.group('host') self.host = m.group('host')
if self.host is not None:
self.host = self.host.lstrip('[').rstrip(']')
self.port = m.group('port') and int(m.group('port')) or None self.port = m.group('port') and int(m.group('port')) or None
self.path = normpath_special(m.group('path')) self.path = normpath_special(m.group('path'))
self.archive = m.group('archive') self.archive = m.group('archive')
@ -886,6 +888,8 @@ class Location:
if m: if m:
self.user = m.group('user') self.user = m.group('user')
self.host = m.group('host') self.host = m.group('host')
if isinstance(self.host, str):
self.host = self.host.lstrip('[').rstrip(']')
self.path = normpath_special(m.group('path')) self.path = normpath_special(m.group('path'))
self.archive = m.group('archive') self.archive = m.group('archive')
self.proto = self.host and 'ssh' or 'file' self.proto = self.host and 'ssh' or 'file'

View File

@ -58,6 +58,30 @@ class TestLocationWithoutEnv:
"Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)" "Location(proto='ssh', user='user', host='host', port=1234, path='/some/path', archive=None)"
assert repr(Location('ssh://user@host/some/path')) == \ assert repr(Location('ssh://user@host/some/path')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)" "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[::]:1234/some/path::archive')) == \
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive='archive')"
assert repr(Location('ssh://user@[::]:1234/some/path')) == \
"Location(proto='ssh', user='user', host='::', port=1234, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[::]/some/path')) == \
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::]:1234/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive='archive')"
assert repr(Location('ssh://user@[2001:db8::]:1234/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=1234, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::]/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive='archive')"
assert repr(Location('ssh://user@[2001:db8::c0:ffee]:1234/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=1234, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::c0:ffee]/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive='archive')"
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]:1234/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=1234, path='/some/path', archive=None)"
assert repr(Location('ssh://user@[2001:db8::192.0.2.1]/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
def test_file(self, monkeypatch): def test_file(self, monkeypatch):
monkeypatch.delenv('BORG_REPO', raising=False) monkeypatch.delenv('BORG_REPO', raising=False)
@ -72,6 +96,22 @@ class TestLocationWithoutEnv:
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')" "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@host:/some/path')) == \ assert repr(Location('user@host:/some/path')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)" "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[::]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[::]:/some/path')) == \
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::c0:ffee]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::c0:ffee]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
def test_smb(self, monkeypatch): def test_smb(self, monkeypatch):
monkeypatch.delenv('BORG_REPO', raising=False) monkeypatch.delenv('BORG_REPO', raising=False)