summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libposixpath.tex9
-rw-r--r--Lib/ntpath.py28
-rw-r--r--Lib/posixpath.py17
-rw-r--r--Lib/test/test_ntpath.py10
-rw-r--r--Lib/test/test_posixpath.py13
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS2
7 files changed, 77 insertions, 3 deletions
diff --git a/Doc/lib/libposixpath.tex b/Doc/lib/libposixpath.tex
index 3687c6d7b9..851e4595c7 100644
--- a/Doc/lib/libposixpath.tex
+++ b/Doc/lib/libposixpath.tex
@@ -189,6 +189,15 @@ operating system).
\versionadded{2.2}
\end{funcdesc}
+\begin{funcdesc}{relpath}{path\optional{, start}}
+Return a relative filepath to \var{path} either from the current
+directory or from an optional \var{start} point.
+
+\var{start} defaults to \member{os.curdir}.
+Availability: Windows, \UNIX.
+\versionadded{2.6}
+\end{funcdesc}
+
\begin{funcdesc}{samefile}{path1, path2}
Return \code{True} if both pathname arguments refer to the same file or
directory (as indicated by device number and i-node number).
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index 60c1fd0207..99d7a4a8cd 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -16,7 +16,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"getatime","getctime", "islink","exists","lexists","isdir","isfile",
"ismount","walk","expanduser","expandvars","normpath","abspath",
"splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
- "extsep","devnull","realpath","supports_unicode_filenames"]
+ "extsep","devnull","realpath","supports_unicode_filenames","relpath"]
# strings representing various path-related bits and pieces
curdir = '.'
@@ -465,3 +465,29 @@ realpath = abspath
# Win9x family and earlier have no Unicode filename support.
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
sys.getwindowsversion()[3] >= 2)
+
+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))
+ else:
+ raise ValueError("path is on drive %s, start on drive %s"
+ % (path_list[0], start_list[0]))
+ # 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():
+ break
+ else:
+ i += 1
+
+ rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+ return join(*rel_list)
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index 661d8dbb76..9117208292 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -21,7 +21,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"ismount","walk","expanduser","expandvars","normpath","abspath",
"samefile","sameopenfile","samestat",
"curdir","pardir","sep","pathsep","defpath","altsep","extsep",
- "devnull","realpath","supports_unicode_filenames"]
+ "devnull","realpath","supports_unicode_filenames","relpath"]
# strings representing various path-related bits and pieces
curdir = '.'
@@ -382,3 +382,18 @@ def _resolve_link(path):
return path
supports_unicode_filenames = False
+
+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)
+
+ # Work out how much of the filepath is shared by start and path.
+ i = len(commonprefix([start_list, path_list]))
+
+ rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
+ return join(*rel_list)
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
index 703e5c856f..6af9c85142 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -157,6 +157,16 @@ except ImportError:
else:
tester('ntpath.abspath("C:\\")', "C:\\")
+currentdir = os.path.split(os.getcwd())[-1]
+tester('ntpath.relpath("a")', 'a')
+tester('ntpath.relpath(os.path.abspath("a"))', 'a')
+tester('ntpath.relpath("a/b")', 'a\\b')
+tester('ntpath.relpath("../a/b")', '..\\a\\b')
+tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a')
+tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b')
+tester('ntpath.relpath("a", "b/c")', '..\\..\\a')
+tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a')
+
if errors:
raise TestFailed(str(errors) + " errors.")
elif verbose:
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index 5632dcc8cd..e2adb3422d 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -2,7 +2,7 @@ import unittest
from test import test_support
import posixpath, os
-from posixpath import realpath, abspath, join, dirname, basename
+from posixpath import realpath, abspath, join, dirname, basename, relpath
# An absolute path to a temporary filename for testing. We can't rely on TESTFN
# being an absolute path, so we need this.
@@ -479,6 +479,17 @@ class PosixPathTest(unittest.TestCase):
safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN)
+ def test_relpath(self):
+ currentdir = os.path.split(os.getcwd())[-1]
+ self.assertRaises(ValueError, posixpath.relpath, "")
+ self.assertEqual(posixpath.relpath("a"), "a")
+ self.assertEqual(posixpath.relpath(os.path.abspath("a")), "a")
+ self.assertEqual(posixpath.relpath("a/b"), "a/b")
+ self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
+ self.assertEqual(posixpath.relpath("a", "../b"), "../"+currentdir+"/a")
+ self.assertEqual(posixpath.relpath("a/b", "../c"), "../"+currentdir+"/a/b")
+ self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
+
def test_main():
test_support.run_unittest(PosixPathTest)
diff --git a/Misc/ACKS b/Misc/ACKS
index eeb266a1c8..dfe63d2d7d 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -36,6 +36,7 @@ Luigi Ballabio
Michael J. Barber
Chris Barker
Quentin Barnes
+Richard Barran
Cesar Eduardo Barros
Des Barry
Ulf Bartelt
diff --git a/Misc/NEWS b/Misc/NEWS
index 6e336b69d3..e7bd11f1a9 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -191,6 +191,8 @@ Library
of those present. Also, it tries the Windows default browser before
trying Mozilla variants.
+- Patch #1339796: add a relpath() function to os.path.
+
- Patch #1681153: the wave module now closes a file object it opened if
initialization failed.