]> granicus.if.org Git - python/commitdiff
Issue #19456: ntpath.join() now joins relative paths correctly when a drive
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 27 Jan 2014 21:14:51 +0000 (23:14 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Mon, 27 Jan 2014 21:14:51 +0000 (23:14 +0200)
is present.

Lib/ntpath.py
Lib/test/test_ntpath.py
Misc/NEWS

index f3b3111896814e6edc7f91036ce7fc7bc553716f..fc9463c48e4f399d96d5aa4248538571ccb5b6fd 100644 (file)
@@ -59,61 +59,30 @@ def isabs(s):
 
 
 # Join two (or more) paths.
-
-def join(a, *p):
-    """Join two or more pathname components, inserting "\\" as needed.
-    If any component is an absolute path, all previous path components
-    will be discarded."""
-    path = a
-    for b in p:
-        b_wins = 0  # set to 1 iff b makes path irrelevant
-        if path == "":
-            b_wins = 1
-
-        elif isabs(b):
-            # This probably wipes out path so far.  However, it's more
-            # complicated if path begins with a drive letter:
-            #     1. join('c:', '/a') == 'c:/a'
-            #     2. join('c:/', '/a') == 'c:/a'
-            # But
-            #     3. join('c:/a', '/b') == '/b'
-            #     4. join('c:', 'd:/') = 'd:/'
-            #     5. join('c:/', 'd:/') = 'd:/'
-            if path[1:2] != ":" or b[1:2] == ":":
-                # Path doesn't start with a drive letter, or cases 4 and 5.
-                b_wins = 1
-
-            # Else path has a drive letter, and b doesn't but is absolute.
-            elif len(path) > 3 or (len(path) == 3 and
-                                   path[-1] not in "/\\"):
-                # case 3
-                b_wins = 1
-
-        if b_wins:
-            path = b
-        else:
-            # Join, and ensure there's a separator.
-            assert len(path) > 0
-            if path[-1] in "/\\":
-                if b and b[0] in "/\\":
-                    path += b[1:]
-                else:
-                    path += b
-            elif path[-1] == ":":
-                path += b
-            elif b:
-                if b[0] in "/\\":
-                    path += b
-                else:
-                    path += "\\" + b
-            else:
-                # path is not empty and does not end with a backslash,
-                # but b is empty; since, e.g., split('a/') produces
-                # ('a', ''), it's best if join() adds a backslash in
-                # this case.
-                path += '\\'
-
-    return path
+def join(path, *paths):
+    """Join two or more pathname components, inserting "\\" as needed."""
+    result_drive, result_path = splitdrive(path)
+    for p in paths:
+        p_drive, p_path = splitdrive(p)
+        if p_path and p_path[0] in '\\/':
+            # Second path is absolute
+            if p_drive or not result_drive:
+                result_drive = p_drive
+            result_path = p_path
+            continue
+        elif p_drive and p_drive != result_drive:
+            if p_drive.lower() != result_drive.lower():
+                # Different drives => ignore the first path entirely
+                result_drive = p_drive
+                result_path = p_path
+                continue
+            # Same drive in different case
+            result_drive = p_drive
+        # Second path is relative to the first
+        if result_path and result_path[-1] not in '\\/':
+            result_path = result_path + '\\'
+        result_path = result_path + p_path
+    return result_drive + result_path
 
 
 # Split a path in a drive specification (a drive letter followed by a
index 749afbce8734881e08fa76bc90092beb8b03536a..11e66fb0a7464c5bcf59a418b3f0251d54ad8e5f 100644 (file)
@@ -85,10 +85,7 @@ class TestNtpath(unittest.TestCase):
         tester('ntpath.join("/a")', '/a')
         tester('ntpath.join("\\a")', '\\a')
         tester('ntpath.join("a:")', 'a:')
-        tester('ntpath.join("a:", "b")', 'a:b')
-        tester('ntpath.join("a:", "/b")', 'a:/b')
         tester('ntpath.join("a:", "\\b")', 'a:\\b')
-        tester('ntpath.join("a", "/b")', '/b')
         tester('ntpath.join("a", "\\b")', '\\b')
         tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
         tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
@@ -96,22 +93,46 @@ class TestNtpath(unittest.TestCase):
         tester('ntpath.join("a", "b", "\\c")', '\\c')
         tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
         tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
-        tester("ntpath.join('c:', '/a')", 'c:/a')
-        tester("ntpath.join('c:/', '/a')", 'c:/a')
-        tester("ntpath.join('c:/a', '/b')", '/b')
-        tester("ntpath.join('c:', 'd:/')", 'd:/')
-        tester("ntpath.join('c:/', 'd:/')", 'd:/')
-        tester("ntpath.join('c:/', 'd:/a/b')", 'd:/a/b')
-
-        tester("ntpath.join('')", '')
-        tester("ntpath.join('', '', '', '', '')", '')
-        tester("ntpath.join('a')", 'a')
+
         tester("ntpath.join('', 'a')", 'a')
         tester("ntpath.join('', '', '', '', 'a')", 'a')
         tester("ntpath.join('a', '')", 'a\\')
         tester("ntpath.join('a', '', '', '', '')", 'a\\')
         tester("ntpath.join('a\\', '')", 'a\\')
         tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
+        tester("ntpath.join('a/', '')", 'a/')
+
+        tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
+        tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
+        tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
+        tester("ntpath.join('c:', 'x/y')", 'c:x/y')
+        tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
+        tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
+        tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
+        tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
+        tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
+        #tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
+        #tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
+        #tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
+
+        tester("ntpath.join('a/b', '/x/y')", '/x/y')
+        tester("ntpath.join('/a/b', '/x/y')", '/x/y')
+        tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
+        tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
+        tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
+        tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
+        #tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
+        #tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
+        #tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
+
+        tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
+        tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
+        tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
+        tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
+
+        for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b'):
+            for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y'):
+                tester("ntpath.join(%r, %r)" % (x, y), y)
 
     def test_normpath(self):
         tester("ntpath.normpath('A//////././//.//B')", r'A\B')
index 7bfa991b72a23024856341f900c82eb923642c99..67423709d98fe7f6d4342ac976ffbc39a0fead78 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -38,6 +38,9 @@ Core and Builtins
 Library
 -------
 
+- Issue #19456: ntpath.join() now joins relative paths correctly when a drive
+  is present.
+
 - Issue #8260: The read(), readline() and readlines() methods of
   codecs.StreamReader returned incomplete data when were called after
   readline() or read(size).  Based on patch by Amaury Forgeot d'Arc.