diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 1d22d5f1dc2ab5..5496ef8e112fc8 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -134,16 +134,9 @@ def splitdrive(p): """ p = os.fspath(p) if len(p) >= 2: - if isinstance(p, bytes): - sep = b'\\' - altsep = b'/' - colon = b':' - else: - sep = '\\' - altsep = '/' - colon = ':' + sep, altsep, colon = get_separator(p) normp = p.replace(altsep, sep) - if (normp[0:2] == sep*2) and (normp[2:3] != sep): + if is_unc_path(normp, sep): # is a UNC path: # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path # \\machine\mountpoint\directory\etc\... @@ -151,6 +144,11 @@ def splitdrive(p): index = normp.find(sep, 2) if index == -1: return p[:0], p + if is_extended_unc(normp, colon): + start = normp.find(sep, index + 1) + drive_colon = normp.find(colon, index + 1) + if drive_colon == -1 or drive_colon > start: + index = normp.find(sep, start + 1) index2 = normp.find(sep, index + 1) # a UNC path can't have two slashes in a row # (after the initial two) @@ -159,11 +157,30 @@ def splitdrive(p): if index2 == -1: index2 = len(p) return p[:index2], p[index2:] - if normp[1:2] == colon: + if is_drive_path(normp, colon): return p[:2], p[2:] return p[:0], p +def is_unc_path(path, sep): + return (path[0:2] == sep*2) and (path[2:3] != sep) + + +def is_drive_path(path, colon): + return path[1:2] == colon + + +def is_extended_unc(path, colon): + return path[2] in ['?', '.'] and path[-2] != colon and path[4:7] == 'UNC' + + +def get_separator(path): + sep, altsep, colon = ['\\', '/', ':'] + if isinstance(path, bytes): + sep, altsep, colon = [b'\\', b'/', b':'] + return sep, altsep, colon + + # Split a path in head (everything up to the last '/') and tail (the # rest). After the trailing '/' is stripped, the invariant # join(head, tail) == p holds. diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 285fb69dc1e88f..4a12efd5df5d54 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -84,6 +84,11 @@ def test_splitdrive(self): # Issue #19911: UNC part containing U+0130 self.assertEqual(ntpath.splitdrive('//conky/MOUNTPOİNT/foo/bar'), ('//conky/MOUNTPOİNT', '/foo/bar')) + # Issue #37609: UNC device path + self.assertEqual(ntpath.splitdrive('//?/UNC/localhost/C$/foo/bar'), + ('//?/UNC/localhost/C$', '/foo/bar')) + self.assertEqual(ntpath.splitdrive('//./UNC/localhost/C$/foo/bar'), + ('//./UNC/localhost/C$', '/foo/bar')) def test_split(self): tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar')) diff --git a/Misc/NEWS.d/next/Library/2019-07-18-14-39-59.bpo-37609.1ZYNbG.rst b/Misc/NEWS.d/next/Library/2019-07-18-14-39-59.bpo-37609.1ZYNbG.rst new file mode 100644 index 00000000000000..37430f1bb0f4e0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-18-14-39-59.bpo-37609.1ZYNbG.rst @@ -0,0 +1 @@ +Add support for unc device path in :func:`ntpath.splitdrive`