From 6f187743ffbce522c9d686ae5393272c1eb85dcc Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Fri, 16 Mar 2007 22:16:08 +0000 Subject: [PATCH] Patch 1339796: add a relpath() function to os.path. --- Doc/lib/libposixpath.tex | 9 +++++++++ Lib/ntpath.py | 28 +++++++++++++++++++++++++++- Lib/posixpath.py | 17 ++++++++++++++++- Lib/test/test_ntpath.py | 10 ++++++++++ Lib/test/test_posixpath.py | 13 ++++++++++++- Misc/ACKS | 1 + Misc/NEWS | 2 ++ 7 files changed, 77 insertions(+), 3 deletions(-) diff --git a/Doc/lib/libposixpath.tex b/Doc/lib/libposixpath.tex index 3687c6d7b94..851e4595c70 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 60c1fd02070..99d7a4a8cdc 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 661d8dbb76c..91172082929 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 703e5c856fb..6af9c851423 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 5632dcc8cd4..e2adb3422d3 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 eeb266a1c85..dfe63d2d7df 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 6e336b69d34..e7bd11f1a9f 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.