From 3bef9355123e6facb866fa5964acdbeb2f11f7d2 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 22 Jun 2014 19:07:38 -0700 Subject: fix ntpath.join on UNC-style paths by backporting py3k's splitdrive (closes #21672) --- Lib/ntpath.py | 47 ++++++++++++++++++++++++++++++++++++++++++----- Lib/test/test_ntpath.py | 32 ++++++++++++++++++++++++-------- Misc/NEWS | 2 ++ 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 42469fe..228bbb3 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -82,6 +82,10 @@ def join(path, *paths): if result_path and result_path[-1] not in '\\/': result_path = result_path + '\\' result_path = result_path + p_path + ## add separator between UNC and non-absolute path + if (result_path and result_path[0] not in '\\/' and + result_drive and result_drive[-1:] != ':'): + return result_drive + sep + result_path return result_drive + result_path @@ -89,12 +93,45 @@ def join(path, *paths): # colon) and the path specification. # It is always true that drivespec + pathspec == p def splitdrive(p): - """Split a pathname into drive and path specifiers. Returns a 2-tuple -"(drive,path)"; either part may be empty""" - if p[1:2] == ':': - return p[0:2], p[2:] - return '', p + """Split a pathname into drive/UNC sharepoint and relative path specifiers. + Returns a 2-tuple (drive_or_unc, path); either part may be empty. + + If you assign + result = splitdrive(p) + It is always true that: + result[0] + result[1] == p + + If the path contained a drive letter, drive_or_unc will contain everything + up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir") + If the path contained a UNC path, the drive_or_unc will contain the host name + and share up to but not including the fourth directory separator character. + e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") + + Paths cannot contain both a drive letter and a UNC path. + + """ + if len(p) > 1: + normp = p.replace(altsep, sep) + if (normp[0:2] == sep*2) and (normp[2] != sep): + # is a UNC path: + # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path + # \\machine\mountpoint\directory\etc\... + # directory ^^^^^^^^^^^^^^^ + index = normp.find(sep, 2) + if index == -1: + return '', p + index2 = normp.find(sep, index + 1) + # a UNC path can't have two slashes in a row + # (after the initial two) + if index2 == index + 1: + return '', p + if index2 == -1: + index2 = len(p) + return p[:index2], p[index2:] + if normp[1] == ':': + return p[:2], p[2:] + return '', p # Parse UNC paths def splitunc(p): diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 6c16caf..78af18c 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1,3 +1,4 @@ +# coding: utf-8 import ntpath import os import sys @@ -34,6 +35,21 @@ class TestNtpath(unittest.TestCase): ('c:', '\\foo\\bar')) tester('ntpath.splitdrive("c:/foo/bar")', ('c:', '/foo/bar')) + tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")', + ('\\\\conky\\mountpoint', '\\foo\\bar')) + tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")', + ('//conky/mountpoint', '/foo/bar')) + tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")', + ('', '\\\\\\conky\\mountpoint\\foo\\bar')) + tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")', + ('', '///conky/mountpoint/foo/bar')) + tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")', + ('', '\\\\conky\\\\mountpoint\\foo\\bar')) + tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")', + ('', '//conky//mountpoint/foo/bar')) + # Issue #19911: UNC part containing U+0130 + self.assertEqual(ntpath.splitdrive(u'//conky/MOUNTPOİNT/foo/bar'), + (u'//conky/MOUNTPOİNT', '/foo/bar')) def test_splitunc(self): tester('ntpath.splitunc("c:\\foo\\bar")', @@ -62,10 +78,10 @@ class TestNtpath(unittest.TestCase): tester('ntpath.split("c:\\")', ('c:\\', '')) tester('ntpath.split("\\\\conky\\mountpoint\\")', - ('\\\\conky\\mountpoint', '')) + ('\\\\conky\\mountpoint\\', '')) tester('ntpath.split("c:/")', ('c:/', '')) - tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint', '')) + tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', '')) def test_isabs(self): tester('ntpath.isabs("c:\\")', 1) @@ -114,9 +130,9 @@ class TestNtpath(unittest.TestCase): 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('//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') @@ -124,9 +140,9 @@ class TestNtpath(unittest.TestCase): 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('//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') diff --git a/Misc/NEWS b/Misc/NEWS index 664eba7..aa05403 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,8 @@ Core and Builtins Library ------- +- Issue #21672: Fix the behavior of ntpath.join on UNC-style paths. + - Issue #21491: SocketServer: Fix a race condition in child processes reaping. - Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method -- cgit v0.12