Index: Doc/library/os.path.rst =================================================================== --- Doc/library/os.path.rst (revision 67929) +++ Doc/library/os.path.rst (working copy) @@ -53,6 +53,16 @@ Note that this may return invalid paths because it works a character at a time. +.. function:: commonpathprefix(list, separator=None) + + Return the longest path prefix (taken component-by-component) that is a + prefix of all paths in *list*. If *list* is empty, return the empty + string (``''``). Unlike :func:`commonprefix`, this will only return the + empty string or complete paths. The optional second argument defaults to + os.sep on platforms other than Windows and OS/2. On those platforms it + defaults to either os.sep or os.altsep. + + .. function:: dirname(path) Return the directory name of pathname *path*. This is the first half of the Index: Lib/genericpath.py =================================================================== --- Lib/genericpath.py (revision 67929) +++ Lib/genericpath.py (working copy) @@ -66,7 +66,7 @@ # Return the longest prefix of all list elements. def commonprefix(m): - "Given a list of pathnames, returns the longest common leading component" + "Given a list of pathnames, returns the longest common leading string" if not m: return '' s1 = min(m) s2 = max(m) @@ -75,6 +75,26 @@ return s1[:i] return s1 +# Return the longest prefix of all list elements. +def commonpathprefix(m, separator=None): + """Given a list of pathnames, returns the longest common leading component. + + Unlike commonprefix(), commonpathprefix() considers path elements as + delimited by the defined separator (default: os.sep). + """ + if not m: + return '' + if separator is None: + import os + separator = os.sep + common = [] + s1 = min(m).split(separator) + s2 = max(m).split(separator) + while s1 and s2 and s1[0] == s2[0]: + common.append(s1[0]) + del s1[0], s2[0] + return separator.join(common) + # Split a path in root and extension. # The extension is everything starting at the last dot in the last # pathname component; the root is everything before that. Index: Lib/os2emxpath.py =================================================================== --- Lib/os2emxpath.py (revision 67929) +++ Lib/os2emxpath.py (working copy) @@ -9,7 +9,7 @@ import stat from genericpath import * from ntpath import (expanduser, expandvars, isabs, islink, splitdrive, - splitext, split) + splitext, split, commonpathprefix) __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", Index: Lib/ntpath.py =================================================================== --- Lib/ntpath.py (revision 67929) +++ Lib/ntpath.py (working copy) @@ -64,6 +64,26 @@ else: return ':' +# Return the longest prefix of all list elements. +def commonpathprefix(m, separator=None): + """Given a list of pathnames, returns the longest common leading component. + + Unlike commonprefix(), commonpathprefix() considers path elements as + delimited by the defined separator (default: os.sep). + """ + if not m: + return '' + if separator is None: + separator = r"\\|/" + import re + common = [] + s1 = re.split(separator, min(m)) + s2 = re.split(separator, max(m)) + while s1 and s2 and s1[0] == s2[0]: + common.append(s1[0]) + del s1[0], s2[0] + return "\\".join(common) + # Normalize the case of a pathname and map slashes to backslashes. # Other normalizations (such as optimizing '../' away) are not done # (this is done by normpath). Index: Lib/macpath.py =================================================================== --- Lib/macpath.py (revision 67929) +++ Lib/macpath.py (working copy) @@ -34,7 +34,6 @@ def normcase(path): return path.lower() - def isabs(s): """Return true if a path is absolute. On the Mac, relative paths begin with a colon, @@ -45,7 +44,16 @@ colon = _get_colon(s) return colon in s and s[:1] != colon +def commonpathprefix(m, separator=None): + """Given a list of pathnames, returns the longest common leading component. + Unlike commonprefix(), commonpathprefix() considers path elements as + delimited by the defined separator (default: os.sep). + """ + if separator is None: + separator = sep + return genericpath.commonpathprefix(m, separator) + def join(s, *p): colon = _get_colon(s) path = s Index: Lib/test/test_ntpath.py =================================================================== --- Lib/test/test_ntpath.py (revision 67929) +++ Lib/test/test_ntpath.py (working copy) @@ -73,6 +73,40 @@ tester('ntpath.isabs("\\foo")', 1) tester('ntpath.isabs("\\foo\\bar")', 1) + def test_commonpathprefix(self): + self.assertEqual(ntpath.commonpathprefix([]), "") + self.assertEqual(ntpath.commonpathprefix(["/home/swenson/spam", + "/home/swen/spam"]), + "\\home") + self.assertEqual(ntpath.commonpathprefix(["\\home\\swenson\\spam", + "\\home\\swen\\spam"]), + "\\home") + self.assertEqual( + ntpath.commonpathprefix(["\\home\\swen\\spam", + "\\home\\swen\\eggs"]), + "\\home\\swen" + ) + self.assertEqual( + ntpath.commonpathprefix(["\\home\\swen\\spam", + "\\home\\swen\\spam"]), + "\\home\\swen\\spam" + ) + self.assertEqual( + ntpath.commonpathprefix(["\\home\\swen\\spam", + "swen\\spam"]), + "" + ) + self.assertEqual( + ntpath.commonpathprefix(["swen\\eggs", + "swen\\spam"]), + "swen" + ) + self.assertEqual( + ntpath.commonpathprefix(["\\export\\home\\swen", + "\\etc\\passwd"]), + "" + ) + def test_commonprefix(self): tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])', "/home/swen") Index: Lib/test/test_genericpath.py =================================================================== --- Lib/test/test_genericpath.py (revision 67929) +++ Lib/test/test_genericpath.py (working copy) @@ -25,7 +25,56 @@ genericpath.commonprefix(["/home/swen/spam", "/home/swen/spam"]), "/home/swen/spam" ) + self.assertEqual( + genericpath.commonprefix(["/export/home/swen", "/etc/passwd"]), + "/e" + ) + def test_commonpathprefix(self): + self.assertEqual( + genericpath.commonpathprefix([]), + "" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swenson/spam", + "/home/swen/spam"]), + "/home" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swenson/spam", + "/home/swen/spam"], ":"), + "" + ) + self.assertEqual( + genericpath.commonpathprefix(["WhomeWswensonWspam", + "WhomeWswenWspam"], "W"), + "Whome" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "/home/swen/eggs"]), + "/home/swen" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "/home/swen/spam"]), + "/home/swen/spam" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "swen/spam"]), + "" + ) + self.assertEqual( + genericpath.commonpathprefix(["swen/eggs", + "swen/spam"]), + "swen" + ) + self.assertEqual( + genericpath.commonpathprefix(["/export/home/swen", "/etc/passwd"]), + "" + ) + def test_getsize(self): f = open(support.TESTFN, "wb") try: Index: Lib/test/test_macpath.py =================================================================== --- Lib/test/test_macpath.py (revision 67929) +++ Lib/test/test_macpath.py (working copy) @@ -43,6 +43,37 @@ self.assert_(commonprefix([b":home:swen:spam", b":home:swen:spam"]) == b":home:swen:spam") + def test_commonpathprefix(self): + commonpathprefix = macpath.commonpathprefix + self.assertEqual( + commonpathprefix([]), + "" + ) + self.assertEqual( + commonpathprefix([":home:swenson:spam", ":home:swen:spam"]), + ":home" + ) + self.assertEqual( + commonpathprefix([":home:swen:spam", ":home:swen:eggs"]), + ":home:swen" + ) + self.assertEqual( + commonpathprefix([":home:swen:spam", ":home:swen:spam"]), + ":home:swen:spam" + ) + self.assertEqual( + commonpathprefix([":home:swen:spam", "swen:spam"]), + "" + ) + self.assertEqual( + commonpathprefix(["swen:eggs", "swen:spam"]), + "swen" + ) + self.assertEqual( + commonpathprefix([":export:home:swen", ":etc:passwd"]), + "" + ) + def test_split(self): split = macpath.split self.assertEquals(split("foo:bar"), Index: Lib/test/test_posixpath.py =================================================================== --- Lib/test/test_posixpath.py (revision 67929) +++ Lib/test/test_posixpath.py (working copy) @@ -165,6 +165,41 @@ self.assertRaises(TypeError, posixpath.dirname) + def test_commonpathprefix(self): + self.assertEqual( + genericpath.commonpathprefix([]), + "" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swenson/spam", + "/home/swen/spam"]), + "/home" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "/home/swen/eggs"]), + "/home/swen" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "/home/swen/spam"]), + "/home/swen/spam" + ) + self.assertEqual( + genericpath.commonpathprefix(["/home/swen/spam", + "swen/spam"]), + "" + ) + self.assertEqual( + genericpath.commonpathprefix(["swen/eggs", + "swen/spam"]), + "swen" + ) + self.assertEqual( + genericpath.commonprefix(["/export/home/swen", "/etc/passwd"]), + "" + ) + def test_commonprefix(self): self.assertEqual( posixpath.commonprefix([]),