Commit 3bef9355 authored by Benjamin Peterson's avatar Benjamin Peterson

fix ntpath.join on UNC-style paths by backporting py3k's splitdrive (closes #21672)

parent ecb4a1e4
...@@ -82,6 +82,10 @@ def join(path, *paths): ...@@ -82,6 +82,10 @@ def join(path, *paths):
if result_path and result_path[-1] not in '\\/': if result_path and result_path[-1] not in '\\/':
result_path = result_path + '\\' result_path = result_path + '\\'
result_path = result_path + p_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 return result_drive + result_path
...@@ -89,12 +93,45 @@ def join(path, *paths): ...@@ -89,12 +93,45 @@ def join(path, *paths):
# colon) and the path specification. # colon) and the path specification.
# It is always true that drivespec + pathspec == p # It is always true that drivespec + pathspec == p
def splitdrive(p): def splitdrive(p):
"""Split a pathname into drive and path specifiers. Returns a 2-tuple """Split a pathname into drive/UNC sharepoint and relative path specifiers.
"(drive,path)"; either part may be empty""" Returns a 2-tuple (drive_or_unc, path); either part may be empty.
if p[1:2] == ':':
return p[0:2], p[2:] If you assign
return '', p 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 # Parse UNC paths
def splitunc(p): def splitunc(p):
......
# coding: utf-8
import ntpath import ntpath
import os import os
import sys import sys
...@@ -34,6 +35,21 @@ class TestNtpath(unittest.TestCase): ...@@ -34,6 +35,21 @@ class TestNtpath(unittest.TestCase):
('c:', '\\foo\\bar')) ('c:', '\\foo\\bar'))
tester('ntpath.splitdrive("c:/foo/bar")', tester('ntpath.splitdrive("c:/foo/bar")',
('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): def test_splitunc(self):
tester('ntpath.splitunc("c:\\foo\\bar")', tester('ntpath.splitunc("c:\\foo\\bar")',
...@@ -62,10 +78,10 @@ class TestNtpath(unittest.TestCase): ...@@ -62,10 +78,10 @@ class TestNtpath(unittest.TestCase):
tester('ntpath.split("c:\\")', ('c:\\', '')) tester('ntpath.split("c:\\")', ('c:\\', ''))
tester('ntpath.split("\\\\conky\\mountpoint\\")', tester('ntpath.split("\\\\conky\\mountpoint\\")',
('\\\\conky\\mountpoint', '')) ('\\\\conky\\mountpoint\\', ''))
tester('ntpath.split("c:/")', ('c:/', '')) tester('ntpath.split("c:/")', ('c:/', ''))
tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint', '')) tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
def test_isabs(self): def test_isabs(self):
tester('ntpath.isabs("c:\\")', 1) tester('ntpath.isabs("c:\\")', 1)
...@@ -114,9 +130,9 @@ class TestNtpath(unittest.TestCase): ...@@ -114,9 +130,9 @@ class TestNtpath(unittest.TestCase):
tester("ntpath.join('c:/', 'x/y')", 'c:/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:/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/', '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/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('/a/b', '/x/y')", '/x/y') tester("ntpath.join('/a/b', '/x/y')", '/x/y')
...@@ -124,9 +140,9 @@ class TestNtpath(unittest.TestCase): ...@@ -124,9 +140,9 @@ class TestNtpath(unittest.TestCase):
tester("ntpath.join('c:a/b', '/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:/', '/x/y')", 'c:/x/y')
tester("ntpath.join('c:/a/b', '/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/', '/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/a', '/x/y')", '//computer/share/x/y')
tester("ntpath.join('c:', 'C:x/y')", 'C: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:a/b', 'C:x/y')", 'C:a/b\\x/y')
......
...@@ -29,6 +29,8 @@ Core and Builtins ...@@ -29,6 +29,8 @@ Core and Builtins
Library 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 #21491: SocketServer: Fix a race condition in child processes reaping.
- Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method - Issue #21635: The difflib SequenceMatcher.get_matching_blocks() method
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment