blob: 0b85b0b9be46fc0391bd6d4785b2dec33f091835 [file] [log] [blame]
Guido van Rossum15e22e11997-12-05 19:03:01 +00001# Module 'ntpath' -- common operations on WinNT/Win95 pathnames
Tim Peters2344fae2001-01-15 00:50:52 +00002"""Common pathname manipulations, WindowsNT/95 version.
Guido van Rossum534972b1999-02-03 17:20:50 +00003
4Instead of importing this module directly, import os and refer to this
5module as os.path.
Guido van Rossum15e22e11997-12-05 19:03:01 +00006"""
Guido van Rossum555915a1994-02-24 11:32:59 +00007
8import os
Mark Hammond8696ebc2002-10-08 02:44:31 +00009import sys
Christian Heimesc5f05e42008-02-23 17:40:11 +000010import stat
Martin v. Löwis05c075d2007-03-07 11:04:33 +000011import genericpath
Benjamin Peterson0893a0a2008-05-09 00:27:01 +000012import warnings
13
Jack Diederich7b604642006-08-26 18:42:06 +000014from genericpath import *
Serhiy Storchaka2bd8b222015-02-13 12:02:05 +020015from genericpath import _unicode
Skip Montanaro4d5d5bf2000-07-13 01:01:03 +000016
Skip Montanaro269b83b2001-02-06 01:07:02 +000017__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
18 "basename","dirname","commonprefix","getsize","getmtime",
Georg Brandlf0de6a12005-08-22 18:02:59 +000019 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
20 "ismount","walk","expanduser","expandvars","normpath","abspath",
21 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
Collin Winter6f187742007-03-16 22:16:08 +000022 "extsep","devnull","realpath","supports_unicode_filenames","relpath"]
Guido van Rossum555915a1994-02-24 11:32:59 +000023
Skip Montanaro117910d2003-02-14 19:35:31 +000024# strings representing various path-related bits and pieces
25curdir = '.'
26pardir = '..'
27extsep = '.'
28sep = '\\'
29pathsep = ';'
Skip Montanaro9ddac3e2003-03-28 22:23:24 +000030altsep = '/'
Andrew MacIntyre437966c2003-02-17 09:17:50 +000031defpath = '.;C:\\bin'
Skip Montanaro117910d2003-02-14 19:35:31 +000032if 'ce' in sys.builtin_module_names:
33 defpath = '\\Windows'
34elif 'os2' in sys.builtin_module_names:
Andrew MacIntyre437966c2003-02-17 09:17:50 +000035 # OS/2 w/ VACPP
Skip Montanaro117910d2003-02-14 19:35:31 +000036 altsep = '/'
Martin v. Löwisbdec50f2004-06-08 08:29:33 +000037devnull = 'nul'
Skip Montanaro117910d2003-02-14 19:35:31 +000038
Guido van Rossume2ad88c1997-08-12 14:46:58 +000039# Normalize the case of a pathname and map slashes to backslashes.
40# Other normalizations (such as optimizing '../' away) are not done
Guido van Rossum555915a1994-02-24 11:32:59 +000041# (this is done by normpath).
Guido van Rossume2ad88c1997-08-12 14:46:58 +000042
Guido van Rossum555915a1994-02-24 11:32:59 +000043def normcase(s):
Guido van Rossum16a0bc21998-02-18 13:48:31 +000044 """Normalize case of pathname.
45
Guido van Rossum534972b1999-02-03 17:20:50 +000046 Makes all characters lowercase and all slashes into backslashes."""
Fred Drakeb4e460a2000-09-28 16:25:20 +000047 return s.replace("/", "\\").lower()
Guido van Rossum555915a1994-02-24 11:32:59 +000048
Guido van Rossum77e1db31997-06-02 23:11:57 +000049
Fred Drakeef0b5dd2000-02-17 17:30:40 +000050# Return whether a path is absolute.
Guido van Rossum555915a1994-02-24 11:32:59 +000051# Trivial in Posix, harder on the Mac or MS-DOS.
52# For DOS it is absolute if it starts with a slash or backslash (current
Guido van Rossum534972b1999-02-03 17:20:50 +000053# volume), or if a pathname after the volume letter and colon / UNC resource
54# starts with a slash or backslash.
Guido van Rossum555915a1994-02-24 11:32:59 +000055
56def isabs(s):
Guido van Rossum15e22e11997-12-05 19:03:01 +000057 """Test whether a path is absolute"""
58 s = splitdrive(s)[1]
59 return s != '' and s[:1] in '/\\'
Guido van Rossum555915a1994-02-24 11:32:59 +000060
61
Guido van Rossum77e1db31997-06-02 23:11:57 +000062# Join two (or more) paths.
Serhiy Storchaka31f51212014-01-27 23:14:51 +020063def join(path, *paths):
64 """Join two or more pathname components, inserting "\\" as needed."""
65 result_drive, result_path = splitdrive(path)
66 for p in paths:
67 p_drive, p_path = splitdrive(p)
68 if p_path and p_path[0] in '\\/':
69 # Second path is absolute
70 if p_drive or not result_drive:
71 result_drive = p_drive
72 result_path = p_path
73 continue
74 elif p_drive and p_drive != result_drive:
75 if p_drive.lower() != result_drive.lower():
76 # Different drives => ignore the first path entirely
77 result_drive = p_drive
78 result_path = p_path
79 continue
80 # Same drive in different case
81 result_drive = p_drive
82 # Second path is relative to the first
83 if result_path and result_path[-1] not in '\\/':
84 result_path = result_path + '\\'
85 result_path = result_path + p_path
Benjamin Peterson3bef9352014-06-22 19:07:38 -070086 ## add separator between UNC and non-absolute path
87 if (result_path and result_path[0] not in '\\/' and
88 result_drive and result_drive[-1:] != ':'):
89 return result_drive + sep + result_path
Serhiy Storchaka31f51212014-01-27 23:14:51 +020090 return result_drive + result_path
Guido van Rossum555915a1994-02-24 11:32:59 +000091
92
93# Split a path in a drive specification (a drive letter followed by a
Guido van Rossumf3c695c1999-04-06 19:32:19 +000094# colon) and the path specification.
Guido van Rossum555915a1994-02-24 11:32:59 +000095# It is always true that drivespec + pathspec == p
96def splitdrive(p):
Benjamin Peterson3bef9352014-06-22 19:07:38 -070097 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
98 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
Guido van Rossumf3c695c1999-04-06 19:32:19 +000099
Benjamin Peterson3bef9352014-06-22 19:07:38 -0700100 If you assign
101 result = splitdrive(p)
102 It is always true that:
103 result[0] + result[1] == p
104
105 If the path contained a drive letter, drive_or_unc will contain everything
106 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
107
108 If the path contained a UNC path, the drive_or_unc will contain the host name
109 and share up to but not including the fourth directory separator character.
110 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
111
112 Paths cannot contain both a drive letter and a UNC path.
113
114 """
115 if len(p) > 1:
116 normp = p.replace(altsep, sep)
Benjamin Peterson2ad54212014-08-31 09:34:09 -0400117 if (normp[0:2] == sep*2) and (normp[2:3] != sep):
Benjamin Peterson3bef9352014-06-22 19:07:38 -0700118 # is a UNC path:
119 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
120 # \\machine\mountpoint\directory\etc\...
121 # directory ^^^^^^^^^^^^^^^
122 index = normp.find(sep, 2)
123 if index == -1:
124 return '', p
125 index2 = normp.find(sep, index + 1)
126 # a UNC path can't have two slashes in a row
127 # (after the initial two)
128 if index2 == index + 1:
129 return '', p
130 if index2 == -1:
131 index2 = len(p)
132 return p[:index2], p[index2:]
133 if normp[1] == ':':
134 return p[:2], p[2:]
135 return '', p
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000136
137# Parse UNC paths
138def splitunc(p):
139 """Split a pathname into UNC mount point and relative path specifiers.
140
141 Return a 2-tuple (unc, rest); either part may be empty.
142 If unc is not empty, it has the form '//host/mount' (or similar
143 using backslashes). unc+rest is always the input path.
Martin Panter6a8163a2016-04-15 02:14:19 +0000144 Paths containing drive letters never have a UNC part.
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000145 """
146 if p[1:2] == ':':
147 return '', p # Drive letter present
Guido van Rossum534972b1999-02-03 17:20:50 +0000148 firstTwo = p[0:2]
149 if firstTwo == '//' or firstTwo == '\\\\':
150 # is a UNC path:
151 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
152 # \\machine\mountpoint\directories...
153 # directory ^^^^^^^^^^^^^^^
Serhiy Storchakadd5a46c2013-12-16 15:15:29 +0200154 normp = p.replace('\\', '/')
155 index = normp.find('/', 2)
156 if index <= 2:
157 return '', p
158 index2 = normp.find('/', index + 1)
159 # a UNC path can't have two slashes in a row
160 # (after the initial two)
161 if index2 == index + 1:
162 return '', p
163 if index2 == -1:
164 index2 = len(p)
165 return p[:index2], p[index2:]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000166 return '', p
Guido van Rossum555915a1994-02-24 11:32:59 +0000167
168
169# Split a path in head (everything up to the last '/') and tail (the
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000170# rest). After the trailing '/' is stripped, the invariant
Guido van Rossum555915a1994-02-24 11:32:59 +0000171# join(head, tail) == p holds.
172# The resulting head won't end in '/' unless it is the root.
173
174def split(p):
Guido van Rossum534972b1999-02-03 17:20:50 +0000175 """Split a pathname.
176
177 Return tuple (head, tail) where tail is everything after the final slash.
178 Either part may be empty."""
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000179
Guido van Rossum15e22e11997-12-05 19:03:01 +0000180 d, p = splitdrive(p)
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000181 # set i to index beyond p's last slash
182 i = len(p)
183 while i and p[i-1] not in '/\\':
184 i = i - 1
185 head, tail = p[:i], p[i:] # now tail has no slashes
186 # remove trailing slashes from head, unless it's all slashes
187 head2 = head
188 while head2 and head2[-1] in '/\\':
189 head2 = head2[:-1]
190 head = head2 or head
Guido van Rossum15e22e11997-12-05 19:03:01 +0000191 return d + head, tail
Guido van Rossum555915a1994-02-24 11:32:59 +0000192
193
194# Split a path in root and extension.
Guido van Rossum73e122f1997-01-22 00:17:26 +0000195# The extension is everything starting at the last dot in the last
Guido van Rossum555915a1994-02-24 11:32:59 +0000196# pathname component; the root is everything before that.
197# It is always true that root + ext == p.
198
199def splitext(p):
Martin v. Löwis05c075d2007-03-07 11:04:33 +0000200 return genericpath._splitext(p, sep, altsep, extsep)
201splitext.__doc__ = genericpath._splitext.__doc__
Guido van Rossum555915a1994-02-24 11:32:59 +0000202
203
204# Return the tail (basename) part of a path.
205
206def basename(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000207 """Returns the final component of a pathname"""
208 return split(p)[1]
Guido van Rossum555915a1994-02-24 11:32:59 +0000209
210
211# Return the head (dirname) part of a path.
212
213def dirname(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000214 """Returns the directory component of a pathname"""
215 return split(p)[0]
Guido van Rossum555915a1994-02-24 11:32:59 +0000216
Guido van Rossum555915a1994-02-24 11:32:59 +0000217# Is a path a symbolic link?
218# This will always return false on systems where posix.lstat doesn't exist.
219
220def islink(path):
Jack Diederich7b604642006-08-26 18:42:06 +0000221 """Test for symbolic link.
222 On WindowsNT/95 and OS/2 always returns false
223 """
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000224 return False
Guido van Rossum555915a1994-02-24 11:32:59 +0000225
Jack Diederich7b604642006-08-26 18:42:06 +0000226# alias exists to lexists
Johannes Gijsbersae882f72004-08-30 10:19:56 +0000227lexists = exists
228
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000229# Is a path a mount point? Either a root (with or without drive letter)
Martin Panter6a8163a2016-04-15 02:14:19 +0000230# or a UNC path with at most a / or \ after the mount point.
Guido van Rossum555915a1994-02-24 11:32:59 +0000231
232def ismount(path):
Guido van Rossumca99c2c1998-01-19 22:25:59 +0000233 """Test whether a path is a mount point (defined as root of drive)"""
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000234 unc, rest = splitunc(path)
235 if unc:
236 return rest in ("", "/", "\\")
Guido van Rossumca99c2c1998-01-19 22:25:59 +0000237 p = splitdrive(path)[1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000238 return len(p) == 1 and p[0] in '/\\'
Guido van Rossum555915a1994-02-24 11:32:59 +0000239
240
241# Directory tree walk.
242# For each directory under top (including top itself, but excluding
243# '.' and '..'), func(arg, dirname, filenames) is called, where
244# dirname is the name of the directory and filenames is the list
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +0000245# of files (and subdirectories etc.) in the directory.
Guido van Rossum555915a1994-02-24 11:32:59 +0000246# The func may modify the filenames list, to implement a filter,
247# or to impose a different order of visiting.
248
249def walk(top, func, arg):
Tim Peterscf5e6a42001-10-10 04:16:20 +0000250 """Directory tree walk with callback function.
Guido van Rossum534972b1999-02-03 17:20:50 +0000251
Tim Peterscf5e6a42001-10-10 04:16:20 +0000252 For each directory in the directory tree rooted at top (including top
253 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
254 dirname is the name of the directory, and fnames a list of the names of
255 the files and subdirectories in dirname (excluding '.' and '..'). func
256 may modify the fnames list in-place (e.g. via del or slice assignment),
257 and walk will only recurse into the subdirectories whose names remain in
258 fnames; this can be used to implement a filter, or to impose a specific
259 order of visiting. No semantics are defined for, or required of, arg,
260 beyond that arg is always passed to func. It can be used, e.g., to pass
261 a filename pattern, or a mutable object designed to accumulate
262 statistics. Passing None for arg is common."""
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000263 warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
264 stacklevel=2)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000265 try:
266 names = os.listdir(top)
267 except os.error:
268 return
269 func(arg, top, names)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000270 for name in names:
Georg Brandle2a902c2008-01-06 14:17:36 +0000271 name = join(top, name)
272 if isdir(name):
273 walk(name, func, arg)
Guido van Rossum555915a1994-02-24 11:32:59 +0000274
275
276# Expand paths beginning with '~' or '~user'.
277# '~' means $HOME; '~user' means that user's home directory.
278# If the path doesn't begin with '~', or if the user or $HOME is unknown,
279# the path is returned unchanged (leaving error reporting to whatever
280# function is called with the expanded path as argument).
281# See also module 'glob' for expansion of *, ? and [...] in pathnames.
282# (A function should also be defined to do full *sh-style environment
283# variable expansion.)
284
285def expanduser(path):
Guido van Rossum534972b1999-02-03 17:20:50 +0000286 """Expand ~ and ~user constructs.
287
288 If user or $HOME is unknown, do nothing."""
Fred Drake8152d322000-12-12 23:20:45 +0000289 if path[:1] != '~':
Guido van Rossum15e22e11997-12-05 19:03:01 +0000290 return path
291 i, n = 1, len(path)
292 while i < n and path[i] not in '/\\':
Fred Drakeb4e460a2000-09-28 16:25:20 +0000293 i = i + 1
Georg Brandl03b90d82007-03-13 22:07:36 +0000294
295 if 'HOME' in os.environ:
296 userhome = os.environ['HOME']
297 elif 'USERPROFILE' in os.environ:
298 userhome = os.environ['USERPROFILE']
299 elif not 'HOMEPATH' in os.environ:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000300 return path
Georg Brandl03b90d82007-03-13 22:07:36 +0000301 else:
302 try:
303 drive = os.environ['HOMEDRIVE']
304 except KeyError:
305 drive = ''
306 userhome = join(drive, os.environ['HOMEPATH'])
307
308 if i != 1: #~user
309 userhome = join(dirname(userhome), path[1:i])
310
Guido van Rossum15e22e11997-12-05 19:03:01 +0000311 return userhome + path[i:]
Guido van Rossum555915a1994-02-24 11:32:59 +0000312
313
314# Expand paths containing shell variable substitutions.
315# The following rules apply:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000316# - no expansion within single quotes
Georg Brandl03b90d82007-03-13 22:07:36 +0000317# - '$$' is translated into '$'
318# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
Guido van Rossum15e22e11997-12-05 19:03:01 +0000319# - ${varname} is accepted.
Georg Brandl03b90d82007-03-13 22:07:36 +0000320# - $varname is accepted.
321# - %varname% is accepted.
322# - varnames can be made out of letters, digits and the characters '_-'
Ezio Melottic2077b02011-03-16 12:34:31 +0200323# (though is not verified in the ${varname} and %varname% cases)
Guido van Rossum555915a1994-02-24 11:32:59 +0000324# XXX With COMMAND.COM you can use any characters in a variable name,
325# XXX except '^|<>='.
326
Tim Peters2344fae2001-01-15 00:50:52 +0000327def expandvars(path):
Georg Brandl03b90d82007-03-13 22:07:36 +0000328 """Expand shell variables of the forms $var, ${var} and %var%.
Guido van Rossum534972b1999-02-03 17:20:50 +0000329
330 Unknown variables are left unchanged."""
Georg Brandl03b90d82007-03-13 22:07:36 +0000331 if '$' not in path and '%' not in path:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000332 return path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000333 import string
Fred Drake79e75e12001-07-20 19:05:50 +0000334 varchars = string.ascii_letters + string.digits + '_-'
Serhiy Storchaka2bd8b222015-02-13 12:02:05 +0200335 if isinstance(path, _unicode):
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200336 encoding = sys.getfilesystemencoding()
337 def getenv(var):
338 return os.environ[var.encode(encoding)].decode(encoding)
339 else:
340 def getenv(var):
341 return os.environ[var]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000342 res = ''
343 index = 0
344 pathlen = len(path)
345 while index < pathlen:
346 c = path[index]
347 if c == '\'': # no expansion within single quotes
348 path = path[index + 1:]
349 pathlen = len(path)
350 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000351 index = path.index('\'')
Guido van Rossum15e22e11997-12-05 19:03:01 +0000352 res = res + '\'' + path[:index + 1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000353 except ValueError:
Serhiy Storchaka53b542f2015-03-25 16:39:58 +0200354 res = res + c + path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000355 index = pathlen - 1
Georg Brandl03b90d82007-03-13 22:07:36 +0000356 elif c == '%': # variable or '%'
357 if path[index + 1:index + 2] == '%':
358 res = res + c
359 index = index + 1
360 else:
361 path = path[index+1:]
362 pathlen = len(path)
363 try:
364 index = path.index('%')
365 except ValueError:
366 res = res + '%' + path
367 index = pathlen - 1
368 else:
369 var = path[:index]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200370 try:
371 res = res + getenv(var)
372 except KeyError:
Georg Brandl03b90d82007-03-13 22:07:36 +0000373 res = res + '%' + var + '%'
Guido van Rossum15e22e11997-12-05 19:03:01 +0000374 elif c == '$': # variable or '$$'
375 if path[index + 1:index + 2] == '$':
376 res = res + c
377 index = index + 1
378 elif path[index + 1:index + 2] == '{':
379 path = path[index+2:]
380 pathlen = len(path)
381 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000382 index = path.index('}')
Guido van Rossum15e22e11997-12-05 19:03:01 +0000383 var = path[:index]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200384 try:
385 res = res + getenv(var)
386 except KeyError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000387 res = res + '${' + var + '}'
Fred Drakeb4e460a2000-09-28 16:25:20 +0000388 except ValueError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000389 res = res + '${' + path
Guido van Rossum15e22e11997-12-05 19:03:01 +0000390 index = pathlen - 1
391 else:
392 var = ''
393 index = index + 1
394 c = path[index:index + 1]
395 while c != '' and c in varchars:
396 var = var + c
397 index = index + 1
398 c = path[index:index + 1]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200399 try:
400 res = res + getenv(var)
401 except KeyError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000402 res = res + '$' + var
Guido van Rossum15e22e11997-12-05 19:03:01 +0000403 if c != '':
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000404 index = index - 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000405 else:
406 res = res + c
407 index = index + 1
408 return res
Guido van Rossum555915a1994-02-24 11:32:59 +0000409
410
Tim Peters54a14a32001-08-30 22:05:26 +0000411# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
Guido van Rossum3df7b5a1996-08-26 16:35:26 +0000412# Previously, this function also truncated pathnames to 8+3 format,
413# but as this module is called "ntpath", that's obviously wrong!
Guido van Rossum555915a1994-02-24 11:32:59 +0000414
415def normpath(path):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000416 """Normalize path, eliminating double slashes, etc."""
Ezio Melottib5689de2010-01-12 03:32:05 +0000417 # Preserve unicode (if path is unicode)
Serhiy Storchaka2bd8b222015-02-13 12:02:05 +0200418 backslash, dot = (u'\\', u'.') if isinstance(path, _unicode) else ('\\', '.')
Georg Brandle2773252010-08-01 19:14:56 +0000419 if path.startswith(('\\\\.\\', '\\\\?\\')):
420 # in the case of paths with these prefixes:
421 # \\.\ -> device names
422 # \\?\ -> literal paths
423 # do not do any normalization, but return the path unchanged
424 return path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000425 path = path.replace("/", "\\")
Guido van Rossum15e22e11997-12-05 19:03:01 +0000426 prefix, path = splitdrive(path)
Brett Cannonbdc36272004-07-10 20:42:22 +0000427 # We need to be careful here. If the prefix is empty, and the path starts
428 # with a backslash, it could either be an absolute path on the current
429 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
430 # is therefore imperative NOT to collapse multiple backslashes blindly in
431 # that case.
432 # The code below preserves multiple backslashes when there is no drive
433 # letter. This means that the invalid filename \\\a\b is preserved
434 # unchanged, where a\\\b is normalised to a\b. It's not clear that there
435 # is any better behaviour for such edge cases.
436 if prefix == '':
437 # No drive letter - preserve initial backslashes
438 while path[:1] == "\\":
Ezio Melottib5689de2010-01-12 03:32:05 +0000439 prefix = prefix + backslash
Brett Cannonbdc36272004-07-10 20:42:22 +0000440 path = path[1:]
441 else:
442 # We have a drive letter - collapse initial backslashes
443 if path.startswith("\\"):
Ezio Melottib5689de2010-01-12 03:32:05 +0000444 prefix = prefix + backslash
Brett Cannonbdc36272004-07-10 20:42:22 +0000445 path = path.lstrip("\\")
Fred Drakeb4e460a2000-09-28 16:25:20 +0000446 comps = path.split("\\")
Guido van Rossum15e22e11997-12-05 19:03:01 +0000447 i = 0
448 while i < len(comps):
Tim Peters54a14a32001-08-30 22:05:26 +0000449 if comps[i] in ('.', ''):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000450 del comps[i]
Tim Peters54a14a32001-08-30 22:05:26 +0000451 elif comps[i] == '..':
452 if i > 0 and comps[i-1] != '..':
453 del comps[i-1:i+1]
454 i -= 1
455 elif i == 0 and prefix.endswith("\\"):
456 del comps[i]
457 else:
458 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000459 else:
Tim Peters54a14a32001-08-30 22:05:26 +0000460 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000461 # If the path is now empty, substitute '.'
462 if not prefix and not comps:
Ezio Melottib5689de2010-01-12 03:32:05 +0000463 comps.append(dot)
464 return prefix + backslash.join(comps)
Guido van Rossume294cf61999-01-29 18:05:18 +0000465
466
467# Return an absolute path.
Tim Peters21fbd572006-04-21 21:18:10 +0000468try:
469 from nt import _getfullpathname
Mark Hammondf717f052002-01-17 00:44:26 +0000470
Tim Peters21fbd572006-04-21 21:18:10 +0000471except ImportError: # not running on Windows - mock up something sensible
472 def abspath(path):
473 """Return the absolute version of a path."""
474 if not isabs(path):
Serhiy Storchaka2bd8b222015-02-13 12:02:05 +0200475 if isinstance(path, _unicode):
Ezio Melotti4cc80ca2010-02-20 08:09:39 +0000476 cwd = os.getcwdu()
477 else:
478 cwd = os.getcwd()
479 path = join(cwd, path)
Tim Peters21fbd572006-04-21 21:18:10 +0000480 return normpath(path)
481
482else: # use native Windows method on Windows
483 def abspath(path):
484 """Return the absolute version of a path."""
485
486 if path: # Empty path must return current working directory.
487 try:
488 path = _getfullpathname(path)
489 except WindowsError:
490 pass # Bad path - return unchanged.
Serhiy Storchaka2bd8b222015-02-13 12:02:05 +0200491 elif isinstance(path, _unicode):
Ezio Melotti4cc80ca2010-02-20 08:09:39 +0000492 path = os.getcwdu()
Tim Peters21fbd572006-04-21 21:18:10 +0000493 else:
494 path = os.getcwd()
495 return normpath(path)
Guido van Rossum83eeef42001-09-17 15:16:09 +0000496
497# realpath is a no-op on systems without islink support
498realpath = abspath
Mark Hammond8696ebc2002-10-08 02:44:31 +0000499# Win9x family and earlier have no Unicode filename support.
Tim Peters26bc25a2002-10-09 07:56:04 +0000500supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
501 sys.getwindowsversion()[3] >= 2)
Collin Winter6f187742007-03-16 22:16:08 +0000502
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000503def _abspath_split(path):
504 abs = abspath(normpath(path))
505 prefix, rest = splitunc(abs)
506 is_unc = bool(prefix)
507 if not is_unc:
508 prefix, rest = splitdrive(abs)
509 return is_unc, prefix, [x for x in rest.split(sep) if x]
510
Collin Winter6f187742007-03-16 22:16:08 +0000511def relpath(path, start=curdir):
512 """Return a relative version of a path"""
513
514 if not path:
515 raise ValueError("no path specified")
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000516
517 start_is_unc, start_prefix, start_list = _abspath_split(start)
518 path_is_unc, path_prefix, path_list = _abspath_split(path)
519
520 if path_is_unc ^ start_is_unc:
521 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
522 % (path, start))
523 if path_prefix.lower() != start_prefix.lower():
524 if path_is_unc:
525 raise ValueError("path is on UNC root %s, start on UNC root %s"
526 % (path_prefix, start_prefix))
Collin Winter6f187742007-03-16 22:16:08 +0000527 else:
528 raise ValueError("path is on drive %s, start on drive %s"
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000529 % (path_prefix, start_prefix))
Collin Winter6f187742007-03-16 22:16:08 +0000530 # Work out how much of the filepath is shared by start and path.
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000531 i = 0
532 for e1, e2 in zip(start_list, path_list):
533 if e1.lower() != e2.lower():
Collin Winter6f187742007-03-16 22:16:08 +0000534 break
Collin Winter6f187742007-03-16 22:16:08 +0000535 i += 1
536
537 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
Georg Brandl183a0842008-01-06 14:27:15 +0000538 if not rel_list:
539 return curdir
Collin Winter6f187742007-03-16 22:16:08 +0000540 return join(*rel_list)
Brian Curtincaea7e82011-06-08 19:29:53 -0500541
542try:
543 # The genericpath.isdir implementation uses os.stat and checks the mode
544 # attribute to tell whether or not the path is a directory.
545 # This is overkill on Windows - just pass the path to GetFileAttributes
546 # and check the attribute from there.
Brian Curtin5446f082011-06-09 10:00:42 -0500547 from nt import _isdir as isdir
Brian Curtincaea7e82011-06-08 19:29:53 -0500548except ImportError:
Brian Curtin5446f082011-06-09 10:00:42 -0500549 # Use genericpath.isdir as imported above.
550 pass