Skip to content

Commit 5a80ee3

Browse files
adapt path handling to latest python
1 parent 7ca7d1f commit 5a80ee3

1 file changed

Lines changed: 122 additions & 85 deletions

File tree

Lib/os2knixpath.py

Lines changed: 122 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
"ismount","expanduser","expandvars","normpath","abspath",
1818
"curdir","pardir","sep","pathsep","defpath","altsep",
1919
"extsep","devnull","realpath","supports_unicode_filenames","relpath",
20-
"samefile","sameopenfile","samestat", "commonpath", "splitunc"]
20+
"samefile","sameopenfile","samestat", "commonpath"]
2121

2222
# strings representing various path-related bits and pieces
23+
# These are primarily for export; internally, they are hardcoded.
24+
# Should be set before imports for resolving cyclic dependency.
2325
curdir = '.'
2426
pardir = '..'
2527
extsep = '.'
@@ -29,62 +31,62 @@
2931
defpath = '.;' + (os.environ['UNIXROOT'] + '\\usr\\bin' if 'UNIXROOT' in os.environ else 'C:\\bin')
3032
devnull = 'nul'
3133

32-
# Normalize the case of a pathname and map slashes to backslashes.
34+
def _get_bothseps(path):
35+
if isinstance(path, bytes):
36+
return b'\\/'
37+
else:
38+
return '\\/'
39+
40+
# Normalize the case of a pathname and map altseps to seps.
3341
# Other normalizations (such as optimizing '../' away) are not done
3442
# (this is done by normpath).
3543

3644
def normcase(s):
3745
"""Normalize case of pathname.
3846
3947
Makes all characters lowercase and all altseps into seps."""
48+
s = os.fspath(s)
49+
if isinstance(s, bytes):
50+
sep = b'/'
51+
altsep = b'\\'
52+
else:
53+
sep = '/'
54+
altsep = '\\'
4055
return s.replace(altsep, sep).lower()
4156

4257

4358
# Join two (or more) paths.
4459

4560
def join(a, *p):
4661
"""Join two or more pathname components, inserting sep as needed.
47-
4862
Also replaces all altsep chars with sep in the returned string
4963
to make it consistent."""
64+
a = os.fspath(a)
5065
path = a
51-
for b in p:
52-
if isabs(b):
53-
path = b
54-
elif path == '' or path[-1:] in '/\\:':
55-
path = path + b
56-
else:
57-
path = path + '/' + b
58-
return path.replace(altsep, sep)
5966

67+
if isinstance(path, bytes):
68+
sep = b'/'
69+
seps = b'\\/;'
70+
altsep = b'\\'
71+
else:
72+
sep = '/'
73+
seps = '\\/:'
74+
altsep = '\\'
6075

61-
# Parse UNC paths
62-
def splitunc(p):
63-
"""Split a pathname into UNC mount point and relative path specifiers.
64-
65-
Return a 2-tuple (unc, rest); either part may be empty.
66-
If unc is not empty, it has the form '//host/mount' (or similar
67-
using backslashes). unc+rest is always the input path.
68-
Paths containing drive letters never have an UNC part.
69-
"""
70-
if p[1:2] == ':':
71-
return '', p # Drive letter present
72-
firstTwo = p[0:2]
73-
if firstTwo == '/' * 2 or firstTwo == '\\' * 2:
74-
# is a UNC path:
75-
# vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
76-
# \\machine\mountpoint\directories...
77-
# directory ^^^^^^^^^^^^^^^
78-
normp = normcase(p)
79-
index = normp.find('/', 2)
80-
if index == -1:
81-
##raise RuntimeError, 'illegal UNC path: "' + p + '"'
82-
return ("", p)
83-
index = normp.find('/', index + 1)
84-
if index == -1:
85-
index = len(p)
86-
return p[:index], p[index:]
87-
return '', p
76+
try:
77+
if not p:
78+
path[:0] + sep #23780: Ensure compatible data type even if p is null.
79+
for b in map(os.fspath, p):
80+
if isabs(b):
81+
path = b
82+
elif not path or path.endswith(seps):
83+
path = path + b
84+
else:
85+
path = path + sep + b
86+
except (TypeError, AttributeError, BytesWarning):
87+
genericpath._check_arg_types('join', a, *p)
88+
raise
89+
return path.replace(altsep, sep)
8890

8991

9092
# Return the tail (basename) part of a path.
@@ -107,39 +109,61 @@ def dirname(p):
107109
# or an UNC path with at most a / or \ after the mount point.
108110

109111
def ismount(path):
110-
"""Test whether a path is a mount point (defined as root of drive)"""
111-
unc, rest = splitunc(path)
112-
if unc:
113-
return rest in ("", "/", "\\")
114-
p = splitdrive(path)[1]
115-
return len(p) == 1 and p[0] in '/\\'
116-
112+
"""Test whether a path is a mount point (a drive root, the root of a
113+
share, or a mounted volume)"""
114+
path = os.fspath(path)
115+
seps = _get_bothseps(path)
116+
path = abspath(path)
117+
root, rest = splitdrive(path)
118+
if root and root[0] in seps:
119+
return (not rest) or (rest in seps)
120+
if rest in seps:
121+
return True
122+
123+
return False
117124

118125
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
119126

120127
def normpath(path):
121128
"""Normalize path, eliminating double slashes, etc."""
122-
path = str(path).replace('\\', '/')
129+
path = os.fspath(path)
130+
if isinstance(path, bytes):
131+
sep = b'/'
132+
altsep = b'\\'
133+
curdir = b'.'
134+
pardir = b'..'
135+
else:
136+
sep = '/'
137+
altsep = '\\'
138+
curdir = '.'
139+
pardir = '..'
140+
path = path.replace(altsep, sep)
123141
prefix, path = splitdrive(path)
124-
while path[:1] == '/':
125-
prefix = prefix + '/'
126-
path = path[1:]
127-
comps = path.split('/')
142+
143+
# collapse initial backslashes
144+
if path.startswith(sep):
145+
prefix += sep
146+
path = path.lstrip(sep)
147+
148+
comps = path.split(sep)
128149
i = 0
129150
while i < len(comps):
130-
if comps[i] == '.':
131-
del comps[i]
132-
elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
133-
del comps[i-1:i+1]
134-
i = i - 1
135-
elif comps[i] == '' and i > 0 and comps[i-1] != '':
151+
if not comps[i] or comps[i] == curdir:
136152
del comps[i]
153+
elif comps[i] == pardir:
154+
if i > 0 and comps[i-1] != pardir:
155+
del comps[i-1:i+1]
156+
i -= 1
157+
elif i == 0 and prefix.endswith(sep):
158+
del comps[i]
159+
else:
160+
i += 1
137161
else:
138-
i = i + 1
162+
i += 1
139163
# If the path is now empty, substitute '.'
140164
if not prefix and not comps:
141-
comps.append('.')
142-
return prefix + '/'.join(comps)
165+
comps.append(curdir)
166+
return prefix + sep.join(comps)
143167

144168

145169
# Return an absolute path.
@@ -250,38 +274,51 @@ def samestat(s1, s2):
250274
supports_unicode_filenames = False
251275

252276

253-
def relpath(path, start=curdir):
277+
def relpath(path, start=None):
254278
"""Return a relative version of a path"""
279+
path = os.fspath(path)
280+
if isinstance(path, bytes):
281+
sep = b'\\'
282+
curdir = b'.'
283+
pardir = b'..'
284+
else:
285+
sep = '\\'
286+
curdir = '.'
287+
pardir = '..'
255288

256289
if not path:
257290
raise ValueError("no path specified")
258-
start_list = abspath(start).split(sep)
259-
path_list = abspath(path).split(sep)
260-
# Remove empty components after trailing slashes
261-
if (start_list[-1] == ''):
262-
start_list.pop()
263-
if (path_list[-1] == ''):
264-
path_list.pop()
265-
if start_list[0].lower() != path_list[0].lower():
266-
unc_path, rest = splitunc(path)
267-
unc_start, rest = splitunc(start)
268-
if bool(unc_path) ^ bool(unc_start):
269-
raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
270-
% (path, start))
271-
else:
272-
raise ValueError("path is on drive %s, start on drive %s"
273-
% (path_list[0], start_list[0]))
274-
# Work out how much of the filepath is shared by start and path.
275-
for i in range(min(len(start_list), len(path_list))):
276-
if start_list[i].lower() != path_list[i].lower():
277-
break
291+
292+
if start is None:
293+
start = curdir
278294
else:
279-
i += 1
295+
start = os.fspath(start)
280296

281-
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
282-
if not rel_list:
283-
return curdir
284-
return join(*rel_list)
297+
try:
298+
start_abs = abspath(start)
299+
path_abs = abspath(path)
300+
start_drive, start_rest = splitdrive(start_abs)
301+
path_drive, path_rest = splitdrive(path_abs)
302+
if normcase(start_drive) != normcase(path_drive):
303+
raise ValueError("path is on mount %r, start on mount %r" % (
304+
path_drive, start_drive))
305+
306+
start_list = start_rest.split(sep) if start_rest else []
307+
path_list = path_rest.split(sep) if path_list else []
308+
# Work out how much of the filepath is shared by start and path.
309+
i = 0
310+
for e1, e2 in zip(start_list, path_list):
311+
if normcase(e1) != normcase(e2):
312+
break
313+
i += 1
314+
315+
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
316+
if not rel_list:
317+
return curdir
318+
return join(*rel_list)
319+
except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
320+
genericpath._check_arg_types('relpath', path, start)
321+
raise
285322

286323
# Return the longest common sub-path of the sequence of paths given as input.
287324
# The function is case-insensitive and 'separator-insensitive', i.e. if the

0 commit comments

Comments
 (0)