Implemented RsyncUrl.path_relative with tests
PEP8 fixes
Related-Bug: #1570260
Partial-Bug: #1575759
Change-Id: If3c1e5b4ad72b54f8585ab03588984fb5f8a4853
diff --git a/trsync/objects/rsync_url.py b/trsync/objects/rsync_url.py
index af15da0..5a1919a 100644
--- a/trsync/objects/rsync_url.py
+++ b/trsync/objects/rsync_url.py
@@ -1,5 +1,19 @@
# -*- coding: utf-8 -*-
+# Copyright (c) 2015-2016, Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
import os
import re
@@ -63,7 +77,7 @@
'rsync2': ('{protocol}://', '{user}@', '{host}', ':{port}',
'/{module}', '/{path}'),
# local/path/to/directory
- 'path': ('{path}', ),
+ 'path': ('{path}/', ),
}
self._match = self._get_matching_pattern()
@@ -191,7 +205,7 @@
# local/path/to/directory
'path': ('{rootpath}', ),
}
- return self.by_template(templates[self.url_type])
+ return self._by_template(templates[self.url_type])
@property
def netloc(self):
@@ -207,7 +221,7 @@
# local/path/to/directory
'path': ('', ),
}
- return self.by_template(templates[self.url_type])
+ return self._by_template(templates[self.url_type])
@property
def parsed_url(self):
@@ -223,7 +237,7 @@
parsed_dict[part] = value
return parsed_dict
- def by_template(self, template_list):
+ def _by_template(self, template_list):
template = ''
for part in ('protocol', 'user', 'host', 'port', 'module', 'path',
'rootpath'):
@@ -248,7 +262,7 @@
return True
def _fn_join(self, *parts):
- ''' Joins filenames with ignoring empty parts (None, '', etc)'''
+ '''Joins filenames with ignoring empty parts (None, '', etc)'''
parts = [_ for _ in parts if _]
@@ -280,7 +294,7 @@
return self._fn_join(*parts)
def urljoin(self, *parts):
- return self.join(self.by_template(self.templates[self.url_type]),
+ return self.join(self._by_template(self.templates[self.url_type]),
*parts)
def a_dir(self, *path):
@@ -290,7 +304,7 @@
return result
def url_dir(self, *path):
- return self.a_dir(self.by_template(self.templates[self.url_type]),
+ return self.a_dir(self._by_template(self.templates[self.url_type]),
*path)
def a_file(self, *path):
@@ -301,5 +315,48 @@
return result
def url_file(self, *path):
- return self.a_file(self.by_template(self.templates[self.url_type]),
+ return self.a_file(self._by_template(self.templates[self.url_type]),
*path)
+
+ def _split_path(self, path):
+ '''Returns list of path's parts, starting from '/' for absolute path'''
+ result = list()
+ if path != '/':
+ while path.endswith('/'):
+ path = path[:-1]
+ while True:
+ path, second = os.path.split(path)
+ if second:
+ result.insert(0, second)
+ else:
+ if path:
+ result.insert(0, path)
+ break
+ return result
+
+ def path_relative(self, path, relative=None):
+ '''Returns path evaluated as "path" relative "relative"
+
+ (relative self.path by default)
+ '''
+ if relative is None:
+ relative = self.path
+ path_dir = self._split_path(path)
+ relative_dir = self._split_path(relative)
+ if path.startswith('/'):
+ # path is absolute
+ return path
+ elif relative.startswith('/'):
+ # path relative absolute path
+ return '/' + '/'.join(relative_dir[1:] + path_dir)
+ else:
+ # path relative
+ common_index = 0
+ for i in xrange(min(len(path_dir), len(relative_dir))):
+ if path_dir[i] == relative_dir[i]:
+ common_index += 1
+ else:
+ break
+ updir_number = len(relative_dir[common_index:][:])
+ return '/'.join(['..' for _ in xrange(updir_number)] +
+ path_dir[common_index:])
diff --git a/trsync/tests/test_rsync_url.py b/trsync/tests/test_rsync_url.py
index 751ab77..e795ae3 100644
--- a/trsync/tests/test_rsync_url.py
+++ b/trsync/tests/test_rsync_url.py
@@ -1,5 +1,19 @@
# -*- coding: utf-8 -*-
+# Copyright (c) 2015-2016, Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
import os
import unittest
import yaml
@@ -130,6 +144,23 @@
logger.info('par = "{}", er = "{}"'.format(par, er))
self.assertEqual(url.url_file(par), er)
+ def split_path(self, remote, expected_result):
+ logger.info('For "{}" should be {}'.format(remote, expected_result))
+ url = rsync_url.RsyncUrl(remote)
+ self.log_locals(url)
+ self.assertEqual(url._split_path(remote), expected_result)
+
+ def path_relative(self, remote, expected_result):
+ logger.info('For "{}" should be {}'.format(remote, expected_result))
+ url = rsync_url.RsyncUrl(remote)
+ self.log_locals(url)
+ for par, er in expected_result.items():
+ logger.info('Test parameters\n%s',
+ yaml.dump({remote: expected_result},
+ default_flow_style=False))
+ self.assertEqual(url.path_relative(par), er)
+
+
cpath, cname = os.path.split(os.path.realpath(os.path.realpath(__file__)))
cname = cname.split('.')
cname[-1] = 'yaml'
diff --git a/trsync/tests/test_rsync_url.yaml b/trsync/tests/test_rsync_url.yaml
index 496f9bd..830becb 100644
--- a/trsync/tests/test_rsync_url.yaml
+++ b/trsync/tests/test_rsync_url.yaml
@@ -638,6 +638,8 @@
- null
- '/'
valid: True
+ split_path:
+ - '/'
'dir':
@@ -711,3 +713,142 @@
exact_match_num: 1
classed: 'rsync2'
valid: True
+
+
+'/path/to/some/file/in/some/directory':
+ classed: 'path'
+ split_path:
+ - '/'
+ - 'path'
+ - 'to'
+ - 'some'
+ - 'file'
+ - 'in'
+ - 'some'
+ - 'directory'
+
+
+'path/to/some/file/in/some/directory':
+ classed: 'path'
+ split_path:
+ - 'path'
+ - 'to'
+ - 'some'
+ - 'file'
+ - 'in'
+ - 'some'
+ - 'directory'
+
+
+'/path/to/some/file/in/some/directory/':
+ classed: 'path'
+ split_path:
+ - '/'
+ - 'path'
+ - 'to'
+ - 'some'
+ - 'file'
+ - 'in'
+ - 'some'
+ - 'directory'
+
+
+'path/to/some/file/in/some/directory/':
+ classed: 'path'
+ split_path:
+ - 'path'
+ - 'to'
+ - 'some'
+ - 'file'
+ - 'in'
+ - 'some'
+ - 'directory'
+
+
+'1/2/3/4/5':
+ classed: 'path'
+ path_relative:
+ '1/2/6/7/8':
+ '../../../6/7/8'
+ '1/2/6/7/8/9':
+ '../../../6/7/8/9'
+ '1/2/3/6/7/8/9':
+ '../../6/7/8/9'
+ '1/2':
+ '../../..'
+ '1/2/3':
+ '../..'
+ '':
+ '../../../../..'
+ '1/2/3/4/5/6/7':
+ '6/7'
+
+'1':
+ classed: 'path'
+ path_relative:
+ '1/2':
+ '2'
+ '1/2/3':
+ '2/3'
+
+'dir/to/snapshots/repo-timestamp':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ '../repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'/dir/to/snapshots/repo-timestamp':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-timestamp/dir/to/snapshots/repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'dir/to/snapshots':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ 'repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'dir/to/snapshots/':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ 'repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'/dir/to/snapshots':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/dir/to/snapshots/repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'/dir/to/snapshots/':
+ classed: 'path'
+ path_relative:
+ 'dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/dir/to/snapshots/repo-latest'
+ '/dir/to/snapshots/repo-latest':
+ '/dir/to/snapshots/repo-latest'
+
+
+'snapshots/repo-timestamp':
+ classed: 'path'
+ path_relative:
+ 'snapshots/repo-latest':
+ '../repo-latest'
+ 'repo-latest':
+ '../../repo-latest'