Index: Lib/ntpath.py =================================================================== --- Lib/ntpath.py (revision 69123) +++ Lib/ntpath.py (working copy) @@ -468,22 +468,38 @@ supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and sys.getwindowsversion()[3] >= 2) +def _do_split(path): + if path.endswith(sep): + # Should return [''] for '\\' instead of ['', ''] + return path.split(sep)[:-1] + else: + return path.split(sep) + +def _abspath_split(path): + unc, rest = splitunc(abspath(path)) + if unc: + return True, unc, _do_split(rest) + drive, tail = splitdrive(rest) + return False, drive, _do_split(tail) + def relpath(path, start=curdir): """Return a relative version of a path""" if not path: raise ValueError("no path specified") - start_list = abspath(start).split(sep) - path_list = abspath(path).split(sep) - if start_list[0].lower() != path_list[0].lower(): - unc_path, rest = splitunc(path) - unc_start, rest = splitunc(start) - if bool(unc_path) ^ bool(unc_start): - raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" - % (path, start)) + start_is_unc, start_prefix, start_list = _abspath_split(start) + path_is_unc, path_prefix, path_list = _abspath_split(path) + + if path_is_unc ^ start_is_unc: + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + if path_prefix.lower() != start_prefix.lower(): + if path_is_unc: + raise ValueError("path is on UNC name %s, start on UNC name %s" + % (path_prefix, start_prefix)) else: raise ValueError("path is on drive %s, start on drive %s" - % (path_list[0], start_list[0])) + % (path_prefix, start_prefix)) # Work out how much of the filepath is shared by start and path. for i in range(min(len(start_list), len(path_list))): if start_list[i].lower() != path_list[i].lower(): Index: Lib/posixpath.py =================================================================== --- Lib/posixpath.py (revision 69123) +++ Lib/posixpath.py (working copy) @@ -386,14 +386,22 @@ supports_unicode_filenames = False +def _abspath_split(path): + path = abspath(path) + if path.endswith(sep): + # Should return [''] for '/' instead of ['', ''] + return path.split(sep)[:-1] + else: + return path.split(sep) + def relpath(path, start=curdir): """Return a relative version of a path""" if not path: raise ValueError("no path specified") - start_list = abspath(start).split(sep) - path_list = abspath(path).split(sep) + start_list = _abspath_split(start) + path_list = _abspath_split(path) # Work out how much of the filepath is shared by start and path. i = len(commonprefix([start_list, path_list])) Index: Lib/test/test_ntpath.py =================================================================== --- Lib/test/test_ntpath.py (revision 69123) +++ Lib/test/test_ntpath.py (working copy) @@ -177,8 +177,11 @@ tester('ntpath.relpath("a", "b/c")', '..\\..\\a') tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a') tester('ntpath.relpath("a", "a")', '.') + tester('ntpath.relpath("d:/a", "d:/")', 'a') + tester('ntpath.relpath("d:/a", "d:/b")', '..\\a') + tester('ntpath.relpath("d:/a", "d:/a/b")', '..') + tester('ntpath.relpath("d:/a/b", "d:/a")', 'b') - def test_main(): test_support.run_unittest(TestNtpath) Index: Lib/test/test_posixpath.py =================================================================== --- Lib/test/test_posixpath.py (revision 69123) +++ Lib/test/test_posixpath.py (working copy) @@ -502,6 +502,10 @@ self.assertEqual(posixpath.relpath("a/b", "../c"), "../"+curdir+"/a/b") self.assertEqual(posixpath.relpath("a", "b/c"), "../../a") self.assertEqual(posixpath.relpath("a", "a"), ".") + self.assertEqual(posixpath.relpath("/a", "/"), "a") + self.assertEqual(posixpath.relpath("/a", "/b"), "../a") + self.assertEqual(posixpath.relpath("/a", "/a/b"), "..") + self.assertEqual(posixpath.relpath("/a/b", "/a"), "b") finally: os.getcwd = real_getcwd