diff -r 2181c37977d3 Doc/library/os.path.rst --- a/Doc/library/os.path.rst Sun Dec 02 11:21:02 2012 -0500 +++ b/Doc/library/os.path.rst Sun Dec 02 22:34:49 2012 +0200 @@ -196,7 +196,7 @@ path, all previous components (on Windows, including the previous drive letter, if there was one) are thrown away, and joining continues. The return value is the concatenation of *path1*, and optionally *path2*, etc., with exactly one - directory separator (``os.sep``) following each non-empty part except the last. + directory separator (:attr:``os.sep``) following each non-empty part except the last. (This means that an empty last part will result in a path that ends with a separator.) Note that on Windows, since there is a current directory for each drive, ``os.path.join("c:", "foo")`` represents a path relative to the @@ -283,8 +283,8 @@ *path* is empty, both *head* and *tail* are empty. Trailing slashes are stripped from *head* unless it is the root (one or more slashes only). In all cases, ``join(head, tail)`` returns a path to the same location as *path* - (but the strings may differ). Also see the functions :func:`dirname` and - :func:`basename`. + (but the strings may differ). Also see the functions :func:`dirname`, + :func:`basename` and :func:`splitpath`. .. function:: splitdrive(path) @@ -313,6 +313,23 @@ returns ``('.cshrc', '')``. +.. function:: splitpath(path) + + Split the pathname *path* into a list of components using the same + algorithm as :func:`split`. Equivalent to:: + + def splitpath(path): + head, tail = split(path) + if head == path: + return [head] + return splitpath(head) + [tail] + + ``join(*splitpath(path))`` returns a path to the same location as *path* + (but the strings may differ). + + .. versionadded:: 3.4 + + .. function:: splitunc(path) .. deprecated:: 3.1 diff -r 2181c37977d3 Lib/macpath.py --- a/Lib/macpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/macpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -10,7 +10,7 @@ "getatime","getctime", "islink","exists","lexists","isdir","isfile", "expanduser","expandvars","normpath","abspath", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames"] + "devnull","realpath","supports_unicode_filenames", "splitpath"] # strings representing various path-related bits and pieces # These are primarily for export; internally, they are hardcoded. @@ -82,6 +82,20 @@ return path, file +def splitpath(p): + """Split the pathname into a list of components.""" + # Noneffective generic implementation + result = [] + while True: + head, tail = split(p) + if head == p: + result.append(head) + break + result.append(tail) + p = head + return result[::-1] + + def splitext(p): if isinstance(p, bytes): return genericpath._splitext(p, b':', altsep, b'.') diff -r 2181c37977d3 Lib/ntpath.py --- a/Lib/ntpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/ntpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -17,7 +17,7 @@ "ismount", "expanduser","expandvars","normpath","abspath", "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", "extsep","devnull","realpath","supports_unicode_filenames","relpath", - "samefile", "sameopenfile",] + "samefile", "sameopenfile", "splitpath"] # strings representing various path-related bits and pieces # These are primarily for export; internally, they are hardcoded. @@ -288,6 +288,28 @@ return d + head, tail +def splitpath(p): + """Split the pathname into a list of components.""" + seps = _get_bothseps(p) + d, p = splitdrive(p) + s = None + for i, c in enumerate(p): + if c in seps: + if s: + result.append(p[j:i]) + s = False + elif not s: + if s is None: + result = [d + p[:i]] + j = i + s = True + if s is None: + result = [d + p] + else: + result.append(p[j:] if s else p[:0]) + return result + + # 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. diff -r 2181c37977d3 Lib/posixpath.py --- a/Lib/posixpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/posixpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -22,7 +22,8 @@ "ismount", "expanduser","expandvars","normpath","abspath", "samefile","sameopenfile","samestat", "curdir","pardir","sep","pathsep","defpath","altsep","extsep", - "devnull","realpath","supports_unicode_filenames","relpath"] + "devnull","realpath","supports_unicode_filenames","relpath", + "splitpath"] # Strings representing various path-related bits and pieces. # These are primarily for export; internally, they are hardcoded. @@ -110,6 +111,21 @@ return head, tail +def splitpath(p): + """Split the pathname into a list of components.""" + sep = _get_sep(p) + sepc = sep[0] + for i, c in enumerate(p): + if c != sepc: + break + else: + return [p] + result = [p[:i]] + list(filter(None, p[i:].split(sep))) + if p[-1] == sepc: + result.append(p[:0]) + return result + + # 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. diff -r 2181c37977d3 Lib/test/test_macpath.py --- a/Lib/test/test_macpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/test/test_macpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -46,6 +46,34 @@ self.assertEqual(split(b":conky:mountpoint:"), (b':conky:mountpoint', b'')) + def test_splitpath(self): + splitpath = macpath.splitpath + self.assertEqual(splitpath("foo"), ['', 'foo']) + self.assertEqual(splitpath(":foo"), ['', 'foo']) + self.assertEqual(splitpath("foo:"), ['foo:']) + self.assertEqual(splitpath("foo:bar"), ['foo:', 'bar']) + self.assertEqual(splitpath("foo::bar"), ['foo:', 'bar']) + self.assertEqual(splitpath("foo:::bar"), ['foo:', '', 'bar']) + self.assertEqual(splitpath("foo:bar:"), ['foo:', 'bar', '']) + self.assertEqual(splitpath("conky:mountpoint:foo:bar"), + ['conky:', 'mountpoint', 'foo', 'bar']) + self.assertEqual(splitpath(":"), ['', '']) + self.assertEqual(splitpath(":conky:mountpoint:"), + ['', 'conky', 'mountpoint', '']) + + self.assertEqual(splitpath(b"foo"), [b'', b'foo']) + self.assertEqual(splitpath(b":foo"), [b'', b'foo']) + self.assertEqual(splitpath(b"foo:"), [b'foo:']) + self.assertEqual(splitpath(b"foo:bar"), [b'foo:', b'bar']) + self.assertEqual(splitpath(b"foo::bar"), [b'foo:', b'bar']) + self.assertEqual(splitpath(b"foo:::bar"), [b'foo:', b'', b'bar']) + self.assertEqual(splitpath(b"foo:bar:"), [b'foo:', b'bar', b'']) + self.assertEqual(splitpath(b"conky:mountpoint:foo:bar"), + [b'conky:', b'mountpoint', b'foo', b'bar']) + self.assertEqual(splitpath(b":"), [b'', b'']) + self.assertEqual(splitpath(b":conky:mountpoint:"), + [b'', b'conky', b'mountpoint', b'']) + def test_join(self): join = macpath.join self.assertEqual(join('a', 'b'), ':a:b') diff -r 2181c37977d3 Lib/test/test_ntpath.py --- a/Lib/test/test_ntpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/test/test_ntpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -29,6 +29,8 @@ wantResult = wantResult.encode('ascii') elif isinstance(wantResult, tuple): wantResult = tuple(r.encode('ascii') for r in wantResult) + elif isinstance(wantResult, list): + wantResult = [r.encode('ascii') for r in wantResult] gotResult = eval(fn) if wantResult != gotResult: @@ -79,6 +81,44 @@ tester('ntpath.split("c:/")', ('c:/', '')) tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', '')) + def test_splitpath(self): + tester('ntpath.splitpath("foo\\bar")', ['', 'foo', 'bar']) + tester('ntpath.splitpath("foo/bar")', ['', 'foo', 'bar']) + tester('ntpath.splitpath("")', ['']) + tester('ntpath.splitpath("foo\\bar\\")', ['', 'foo', 'bar', '']) + tester('ntpath.splitpath("foo/bar/")', ['', 'foo', 'bar', '']) + tester('ntpath.splitpath("c:foo\\bar")', ['c:', 'foo', 'bar']) + tester('ntpath.splitpath("c:foo/bar")', ['c:', 'foo', 'bar']) + tester('ntpath.splitpath("c:")', ['c:']) + + tester('ntpath.splitpath("\\foo\\bar")', ['\\', 'foo', 'bar']) + tester('ntpath.splitpath("/foo/bar")', ['/', 'foo', 'bar']) + tester('ntpath.splitpath("\\")', ['\\']) + tester('ntpath.splitpath("/")', ['/']) + tester('ntpath.splitpath("c:\\foo\\bar")', ['c:\\', 'foo', 'bar']) + tester('ntpath.splitpath("c:/foo/bar")', ['c:/', 'foo', 'bar']) + tester('ntpath.splitpath("c:\\")', ['c:\\']) + tester('ntpath.splitpath("c:/")', ['c:/']) + tester('ntpath.splitpath("c:\\\\foo\\bar")', ['c:\\\\', 'foo', 'bar']) + tester('ntpath.splitpath("c://foo/bar")', ['c://', 'foo', 'bar']) + + tester('ntpath.splitpath("\\\\conky\\mountpoint\\foo\\bar")', + ['\\\\conky\\mountpoint\\', 'foo', 'bar']) + tester('ntpath.splitpath("//conky/mountpoint/foo/bar")', + ['//conky/mountpoint/', 'foo', 'bar']) + tester('ntpath.splitpath("\\\\conky\\mountpoint\\")', + ['\\\\conky\\mountpoint\\']) + tester('ntpath.splitpath("//conky/mountpoint/")', + ['//conky/mountpoint/']) + tester('ntpath.splitpath("\\\\\\conky\\mountpoint\\foo\\bar")', + ['\\\\\\', 'conky', 'mountpoint', 'foo', 'bar']) + tester('ntpath.splitpath("///conky/mountpoint/foo/bar")', + ['///', 'conky', 'mountpoint', 'foo', 'bar']) + tester('ntpath.splitpath("\\\\conky\\\\mountpoint\\foo\\bar")', + ['\\\\', 'conky', 'mountpoint', 'foo', 'bar']) + tester('ntpath.splitpath("//conky//mountpoint/foo/bar")', + ['//', 'conky', 'mountpoint', 'foo', 'bar']) + def test_isabs(self): tester('ntpath.isabs("c:\\")', 1) tester('ntpath.isabs("\\\\conky\\mountpoint\\")', 1) diff -r 2181c37977d3 Lib/test/test_posixpath.py --- a/Lib/test/test_posixpath.py Sun Dec 02 11:21:02 2012 -0500 +++ b/Lib/test/test_posixpath.py Sun Dec 02 22:34:49 2012 +0200 @@ -86,6 +86,26 @@ self.assertEqual(posixpath.split(b"////foo"), (b"////", b"foo")) self.assertEqual(posixpath.split(b"//foo//bar"), (b"//foo", b"bar")) + def test_splitpath(self): + splitpath = posixpath.splitpath + self.assertEqual(splitpath('foo/bar'), ['', 'foo', 'bar']) + self.assertEqual(splitpath('foo/bar/'), ['', 'foo', 'bar', '']) + self.assertEqual(splitpath(''), ['']) + self.assertEqual(splitpath('/foo/bar'), ['/', 'foo', 'bar']) + self.assertEqual(splitpath('/'), ['/']) + self.assertEqual(splitpath('////foo/bar'), ['////', 'foo', 'bar']) + self.assertEqual(splitpath('//foo//bar'), ['//', 'foo', 'bar']) + + self.assertEqual(splitpath(b'foo/bar'), [b'', b'foo', b'bar']) + self.assertEqual(splitpath(b'foo/bar/'), [b'', b'foo', b'bar', b'']) + self.assertEqual(splitpath(b''), [b'']) + self.assertEqual(splitpath(b'/foo/bar'), [b'/', b'foo', b'bar']) + self.assertEqual(splitpath(b'/'), [b'/']) + self.assertEqual(splitpath(b'////foo/bar'), [b'////', b'foo', b'bar']) + self.assertEqual(splitpath(b'//foo//bar'), [b'//', b'foo', b'bar']) + + #self.assertEqual(splitpath('/spam' * 10**6), ['/'] + ['spam'] * 10**6) + def splitextTest(self, path, filename, ext): self.assertEqual(posixpath.splitext(path), (filename, ext)) self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext))