blob: 228bbb3f52f7a6ae43b12b788e171797e02a52c5 [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 *
Skip Montanaro4d5d5bf2000-07-13 01:01:03 +000015
Skip Montanaro269b83b2001-02-06 01:07:02 +000016__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
17 "basename","dirname","commonprefix","getsize","getmtime",
Georg Brandlf0de6a12005-08-22 18:02:59 +000018 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
19 "ismount","walk","expanduser","expandvars","normpath","abspath",
20 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
Collin Winter6f187742007-03-16 22:16:08 +000021 "extsep","devnull","realpath","supports_unicode_filenames","relpath"]
Guido van Rossum555915a1994-02-24 11:32:59 +000022
Skip Montanaro117910d2003-02-14 19:35:31 +000023# strings representing various path-related bits and pieces
24curdir = '.'
25pardir = '..'
26extsep = '.'
27sep = '\\'
28pathsep = ';'
Skip Montanaro9ddac3e2003-03-28 22:23:24 +000029altsep = '/'
Andrew MacIntyre437966c2003-02-17 09:17:50 +000030defpath = '.;C:\\bin'
Skip Montanaro117910d2003-02-14 19:35:31 +000031if 'ce' in sys.builtin_module_names:
32 defpath = '\\Windows'
33elif 'os2' in sys.builtin_module_names:
Andrew MacIntyre437966c2003-02-17 09:17:50 +000034 # OS/2 w/ VACPP
Skip Montanaro117910d2003-02-14 19:35:31 +000035 altsep = '/'
Martin v. Löwisbdec50f2004-06-08 08:29:33 +000036devnull = 'nul'
Skip Montanaro117910d2003-02-14 19:35:31 +000037
Guido van Rossume2ad88c1997-08-12 14:46:58 +000038# Normalize the case of a pathname and map slashes to backslashes.
39# Other normalizations (such as optimizing '../' away) are not done
Guido van Rossum555915a1994-02-24 11:32:59 +000040# (this is done by normpath).
Guido van Rossume2ad88c1997-08-12 14:46:58 +000041
Guido van Rossum555915a1994-02-24 11:32:59 +000042def normcase(s):
Guido van Rossum16a0bc21998-02-18 13:48:31 +000043 """Normalize case of pathname.
44
Guido van Rossum534972b1999-02-03 17:20:50 +000045 Makes all characters lowercase and all slashes into backslashes."""
Fred Drakeb4e460a2000-09-28 16:25:20 +000046 return s.replace("/", "\\").lower()
Guido van Rossum555915a1994-02-24 11:32:59 +000047
Guido van Rossum77e1db31997-06-02 23:11:57 +000048
Fred Drakeef0b5dd2000-02-17 17:30:40 +000049# Return whether a path is absolute.
Guido van Rossum555915a1994-02-24 11:32:59 +000050# Trivial in Posix, harder on the Mac or MS-DOS.
51# For DOS it is absolute if it starts with a slash or backslash (current
Guido van Rossum534972b1999-02-03 17:20:50 +000052# volume), or if a pathname after the volume letter and colon / UNC resource
53# starts with a slash or backslash.
Guido van Rossum555915a1994-02-24 11:32:59 +000054
55def isabs(s):
Guido van Rossum15e22e11997-12-05 19:03:01 +000056 """Test whether a path is absolute"""
57 s = splitdrive(s)[1]
58 return s != '' and s[:1] in '/\\'
Guido van Rossum555915a1994-02-24 11:32:59 +000059
60
Guido van Rossum77e1db31997-06-02 23:11:57 +000061# Join two (or more) paths.
Serhiy Storchaka31f51212014-01-27 23:14:51 +020062def join(path, *paths):
63 """Join two or more pathname components, inserting "\\" as needed."""
64 result_drive, result_path = splitdrive(path)
65 for p in paths:
66 p_drive, p_path = splitdrive(p)
67 if p_path and p_path[0] in '\\/':
68 # Second path is absolute
69 if p_drive or not result_drive:
70 result_drive = p_drive
71 result_path = p_path
72 continue
73 elif p_drive and p_drive != result_drive:
74 if p_drive.lower() != result_drive.lower():
75 # Different drives => ignore the first path entirely
76 result_drive = p_drive
77 result_path = p_path
78 continue
79 # Same drive in different case
80 result_drive = p_drive
81 # Second path is relative to the first
82 if result_path and result_path[-1] not in '\\/':
83 result_path = result_path + '\\'
84 result_path = result_path + p_path
Benjamin Peterson3bef9352014-06-22 19:07:38 -070085 ## add separator between UNC and non-absolute path
86 if (result_path and result_path[0] not in '\\/' and
87 result_drive and result_drive[-1:] != ':'):
88 return result_drive + sep + result_path
Serhiy Storchaka31f51212014-01-27 23:14:51 +020089 return result_drive + result_path
Guido van Rossum555915a1994-02-24 11:32:59 +000090
91
92# Split a path in a drive specification (a drive letter followed by a
Guido van Rossumf3c695c1999-04-06 19:32:19 +000093# colon) and the path specification.
Guido van Rossum555915a1994-02-24 11:32:59 +000094# It is always true that drivespec + pathspec == p
95def splitdrive(p):
Benjamin Peterson3bef9352014-06-22 19:07:38 -070096 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
97 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
Guido van Rossumf3c695c1999-04-06 19:32:19 +000098
Benjamin Peterson3bef9352014-06-22 19:07:38 -070099 If you assign
100 result = splitdrive(p)
101 It is always true that:
102 result[0] + result[1] == p
103
104 If the path contained a drive letter, drive_or_unc will contain everything
105 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
106
107 If the path contained a UNC path, the drive_or_unc will contain the host name
108 and share up to but not including the fourth directory separator character.
109 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
110
111 Paths cannot contain both a drive letter and a UNC path.
112
113 """
114 if len(p) > 1:
115 normp = p.replace(altsep, sep)
116 if (normp[0:2] == sep*2) and (normp[2] != sep):
117 # is a UNC path:
118 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
119 # \\machine\mountpoint\directory\etc\...
120 # directory ^^^^^^^^^^^^^^^
121 index = normp.find(sep, 2)
122 if index == -1:
123 return '', p
124 index2 = normp.find(sep, index + 1)
125 # a UNC path can't have two slashes in a row
126 # (after the initial two)
127 if index2 == index + 1:
128 return '', p
129 if index2 == -1:
130 index2 = len(p)
131 return p[:index2], p[index2:]
132 if normp[1] == ':':
133 return p[:2], p[2:]
134 return '', p
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000135
136# Parse UNC paths
137def splitunc(p):
138 """Split a pathname into UNC mount point and relative path specifiers.
139
140 Return a 2-tuple (unc, rest); either part may be empty.
141 If unc is not empty, it has the form '//host/mount' (or similar
142 using backslashes). unc+rest is always the input path.
143 Paths containing drive letters never have an UNC part.
144 """
145 if p[1:2] == ':':
146 return '', p # Drive letter present
Guido van Rossum534972b1999-02-03 17:20:50 +0000147 firstTwo = p[0:2]
148 if firstTwo == '//' or firstTwo == '\\\\':
149 # is a UNC path:
150 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
151 # \\machine\mountpoint\directories...
152 # directory ^^^^^^^^^^^^^^^
Serhiy Storchakadd5a46c2013-12-16 15:15:29 +0200153 normp = p.replace('\\', '/')
154 index = normp.find('/', 2)
155 if index <= 2:
156 return '', p
157 index2 = normp.find('/', index + 1)
158 # a UNC path can't have two slashes in a row
159 # (after the initial two)
160 if index2 == index + 1:
161 return '', p
162 if index2 == -1:
163 index2 = len(p)
164 return p[:index2], p[index2:]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000165 return '', p
Guido van Rossum555915a1994-02-24 11:32:59 +0000166
167
168# Split a path in head (everything up to the last '/') and tail (the
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000169# rest). After the trailing '/' is stripped, the invariant
Guido van Rossum555915a1994-02-24 11:32:59 +0000170# join(head, tail) == p holds.
171# The resulting head won't end in '/' unless it is the root.
172
173def split(p):
Guido van Rossum534972b1999-02-03 17:20:50 +0000174 """Split a pathname.
175
176 Return tuple (head, tail) where tail is everything after the final slash.
177 Either part may be empty."""
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000178
Guido van Rossum15e22e11997-12-05 19:03:01 +0000179 d, p = splitdrive(p)
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000180 # set i to index beyond p's last slash
181 i = len(p)
182 while i and p[i-1] not in '/\\':
183 i = i - 1
184 head, tail = p[:i], p[i:] # now tail has no slashes
185 # remove trailing slashes from head, unless it's all slashes
186 head2 = head
187 while head2 and head2[-1] in '/\\':
188 head2 = head2[:-1]
189 head = head2 or head
Guido van Rossum15e22e11997-12-05 19:03:01 +0000190 return d + head, tail
Guido van Rossum555915a1994-02-24 11:32:59 +0000191
192
193# Split a path in root and extension.
Guido van Rossum73e122f1997-01-22 00:17:26 +0000194# The extension is everything starting at the last dot in the last
Guido van Rossum555915a1994-02-24 11:32:59 +0000195# pathname component; the root is everything before that.
196# It is always true that root + ext == p.
197
198def splitext(p):
Martin v. Löwis05c075d2007-03-07 11:04:33 +0000199 return genericpath._splitext(p, sep, altsep, extsep)
200splitext.__doc__ = genericpath._splitext.__doc__
Guido van Rossum555915a1994-02-24 11:32:59 +0000201
202
203# Return the tail (basename) part of a path.
204
205def basename(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000206 """Returns the final component of a pathname"""
207 return split(p)[1]
Guido van Rossum555915a1994-02-24 11:32:59 +0000208
209
210# Return the head (dirname) part of a path.
211
212def dirname(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000213 """Returns the directory component of a pathname"""
214 return split(p)[0]
Guido van Rossum555915a1994-02-24 11:32:59 +0000215
Guido van Rossum555915a1994-02-24 11:32:59 +0000216# Is a path a symbolic link?
217# This will always return false on systems where posix.lstat doesn't exist.
218
219def islink(path):
Jack Diederich7b604642006-08-26 18:42:06 +0000220 """Test for symbolic link.
221 On WindowsNT/95 and OS/2 always returns false
222 """
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000223 return False
Guido van Rossum555915a1994-02-24 11:32:59 +0000224
Jack Diederich7b604642006-08-26 18:42:06 +0000225# alias exists to lexists
Johannes Gijsbersae882f72004-08-30 10:19:56 +0000226lexists = exists
227
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000228# Is a path a mount point? Either a root (with or without drive letter)
229# or an UNC path with at most a / or \ after the mount point.
Guido van Rossum555915a1994-02-24 11:32:59 +0000230
231def ismount(path):
Guido van Rossumca99c2c1998-01-19 22:25:59 +0000232 """Test whether a path is a mount point (defined as root of drive)"""
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000233 unc, rest = splitunc(path)
234 if unc:
235 return rest in ("", "/", "\\")
Guido van Rossumca99c2c1998-01-19 22:25:59 +0000236 p = splitdrive(path)[1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000237 return len(p) == 1 and p[0] in '/\\'
Guido van Rossum555915a1994-02-24 11:32:59 +0000238
239
240# Directory tree walk.
241# For each directory under top (including top itself, but excluding
242# '.' and '..'), func(arg, dirname, filenames) is called, where
243# dirname is the name of the directory and filenames is the list
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +0000244# of files (and subdirectories etc.) in the directory.
Guido van Rossum555915a1994-02-24 11:32:59 +0000245# The func may modify the filenames list, to implement a filter,
246# or to impose a different order of visiting.
247
248def walk(top, func, arg):
Tim Peterscf5e6a42001-10-10 04:16:20 +0000249 """Directory tree walk with callback function.
Guido van Rossum534972b1999-02-03 17:20:50 +0000250
Tim Peterscf5e6a42001-10-10 04:16:20 +0000251 For each directory in the directory tree rooted at top (including top
252 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
253 dirname is the name of the directory, and fnames a list of the names of
254 the files and subdirectories in dirname (excluding '.' and '..'). func
255 may modify the fnames list in-place (e.g. via del or slice assignment),
256 and walk will only recurse into the subdirectories whose names remain in
257 fnames; this can be used to implement a filter, or to impose a specific
258 order of visiting. No semantics are defined for, or required of, arg,
259 beyond that arg is always passed to func. It can be used, e.g., to pass
260 a filename pattern, or a mutable object designed to accumulate
261 statistics. Passing None for arg is common."""
Philip Jenveyd846f1d2009-05-08 02:28:39 +0000262 warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
263 stacklevel=2)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000264 try:
265 names = os.listdir(top)
266 except os.error:
267 return
268 func(arg, top, names)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000269 for name in names:
Georg Brandle2a902c2008-01-06 14:17:36 +0000270 name = join(top, name)
271 if isdir(name):
272 walk(name, func, arg)
Guido van Rossum555915a1994-02-24 11:32:59 +0000273
274
275# Expand paths beginning with '~' or '~user'.
276# '~' means $HOME; '~user' means that user's home directory.
277# If the path doesn't begin with '~', or if the user or $HOME is unknown,
278# the path is returned unchanged (leaving error reporting to whatever
279# function is called with the expanded path as argument).
280# See also module 'glob' for expansion of *, ? and [...] in pathnames.
281# (A function should also be defined to do full *sh-style environment
282# variable expansion.)
283
284def expanduser(path):
Guido van Rossum534972b1999-02-03 17:20:50 +0000285 """Expand ~ and ~user constructs.
286
287 If user or $HOME is unknown, do nothing."""
Fred Drake8152d322000-12-12 23:20:45 +0000288 if path[:1] != '~':
Guido van Rossum15e22e11997-12-05 19:03:01 +0000289 return path
290 i, n = 1, len(path)
291 while i < n and path[i] not in '/\\':
Fred Drakeb4e460a2000-09-28 16:25:20 +0000292 i = i + 1
Georg Brandl03b90d82007-03-13 22:07:36 +0000293
294 if 'HOME' in os.environ:
295 userhome = os.environ['HOME']
296 elif 'USERPROFILE' in os.environ:
297 userhome = os.environ['USERPROFILE']
298 elif not 'HOMEPATH' in os.environ:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000299 return path
Georg Brandl03b90d82007-03-13 22:07:36 +0000300 else:
301 try:
302 drive = os.environ['HOMEDRIVE']
303 except KeyError:
304 drive = ''
305 userhome = join(drive, os.environ['HOMEPATH'])
306
307 if i != 1: #~user
308 userhome = join(dirname(userhome), path[1:i])
309
Guido van Rossum15e22e11997-12-05 19:03:01 +0000310 return userhome + path[i:]
Guido van Rossum555915a1994-02-24 11:32:59 +0000311
312
313# Expand paths containing shell variable substitutions.
314# The following rules apply:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000315# - no expansion within single quotes
Georg Brandl03b90d82007-03-13 22:07:36 +0000316# - '$$' is translated into '$'
317# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
Guido van Rossum15e22e11997-12-05 19:03:01 +0000318# - ${varname} is accepted.
Georg Brandl03b90d82007-03-13 22:07:36 +0000319# - $varname is accepted.
320# - %varname% is accepted.
321# - varnames can be made out of letters, digits and the characters '_-'
Ezio Melottic2077b02011-03-16 12:34:31 +0200322# (though is not verified in the ${varname} and %varname% cases)
Guido van Rossum555915a1994-02-24 11:32:59 +0000323# XXX With COMMAND.COM you can use any characters in a variable name,
324# XXX except '^|<>='.
325
Tim Peters2344fae2001-01-15 00:50:52 +0000326def expandvars(path):
Georg Brandl03b90d82007-03-13 22:07:36 +0000327 """Expand shell variables of the forms $var, ${var} and %var%.
Guido van Rossum534972b1999-02-03 17:20:50 +0000328
329 Unknown variables are left unchanged."""
Georg Brandl03b90d82007-03-13 22:07:36 +0000330 if '$' not in path and '%' not in path:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000331 return path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000332 import string
Fred Drake79e75e12001-07-20 19:05:50 +0000333 varchars = string.ascii_letters + string.digits + '_-'
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200334 if isinstance(path, unicode):
335 encoding = sys.getfilesystemencoding()
336 def getenv(var):
337 return os.environ[var.encode(encoding)].decode(encoding)
338 else:
339 def getenv(var):
340 return os.environ[var]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000341 res = ''
342 index = 0
343 pathlen = len(path)
344 while index < pathlen:
345 c = path[index]
346 if c == '\'': # no expansion within single quotes
347 path = path[index + 1:]
348 pathlen = len(path)
349 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000350 index = path.index('\'')
Guido van Rossum15e22e11997-12-05 19:03:01 +0000351 res = res + '\'' + path[:index + 1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000352 except ValueError:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000353 res = res + path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000354 index = pathlen - 1
Georg Brandl03b90d82007-03-13 22:07:36 +0000355 elif c == '%': # variable or '%'
356 if path[index + 1:index + 2] == '%':
357 res = res + c
358 index = index + 1
359 else:
360 path = path[index+1:]
361 pathlen = len(path)
362 try:
363 index = path.index('%')
364 except ValueError:
365 res = res + '%' + path
366 index = pathlen - 1
367 else:
368 var = path[:index]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200369 try:
370 res = res + getenv(var)
371 except KeyError:
Georg Brandl03b90d82007-03-13 22:07:36 +0000372 res = res + '%' + var + '%'
Guido van Rossum15e22e11997-12-05 19:03:01 +0000373 elif c == '$': # variable or '$$'
374 if path[index + 1:index + 2] == '$':
375 res = res + c
376 index = index + 1
377 elif path[index + 1:index + 2] == '{':
378 path = path[index+2:]
379 pathlen = len(path)
380 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000381 index = path.index('}')
Guido van Rossum15e22e11997-12-05 19:03:01 +0000382 var = path[:index]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200383 try:
384 res = res + getenv(var)
385 except KeyError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000386 res = res + '${' + var + '}'
Fred Drakeb4e460a2000-09-28 16:25:20 +0000387 except ValueError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000388 res = res + '${' + path
Guido van Rossum15e22e11997-12-05 19:03:01 +0000389 index = pathlen - 1
390 else:
391 var = ''
392 index = index + 1
393 c = path[index:index + 1]
394 while c != '' and c in varchars:
395 var = var + c
396 index = index + 1
397 c = path[index:index + 1]
Serhiy Storchaka2ac9d312014-02-19 23:27:37 +0200398 try:
399 res = res + getenv(var)
400 except KeyError:
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000401 res = res + '$' + var
Guido van Rossum15e22e11997-12-05 19:03:01 +0000402 if c != '':
Sjoerd Mullender33a0a062007-01-16 16:42:38 +0000403 index = index - 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000404 else:
405 res = res + c
406 index = index + 1
407 return res
Guido van Rossum555915a1994-02-24 11:32:59 +0000408
409
Tim Peters54a14a32001-08-30 22:05:26 +0000410# 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 +0000411# Previously, this function also truncated pathnames to 8+3 format,
412# but as this module is called "ntpath", that's obviously wrong!
Guido van Rossum555915a1994-02-24 11:32:59 +0000413
414def normpath(path):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000415 """Normalize path, eliminating double slashes, etc."""
Ezio Melottib5689de2010-01-12 03:32:05 +0000416 # Preserve unicode (if path is unicode)
417 backslash, dot = (u'\\', u'.') if isinstance(path, unicode) else ('\\', '.')
Georg Brandle2773252010-08-01 19:14:56 +0000418 if path.startswith(('\\\\.\\', '\\\\?\\')):
419 # in the case of paths with these prefixes:
420 # \\.\ -> device names
421 # \\?\ -> literal paths
422 # do not do any normalization, but return the path unchanged
423 return path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000424 path = path.replace("/", "\\")
Guido van Rossum15e22e11997-12-05 19:03:01 +0000425 prefix, path = splitdrive(path)
Brett Cannonbdc36272004-07-10 20:42:22 +0000426 # We need to be careful here. If the prefix is empty, and the path starts
427 # with a backslash, it could either be an absolute path on the current
428 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
429 # is therefore imperative NOT to collapse multiple backslashes blindly in
430 # that case.
431 # The code below preserves multiple backslashes when there is no drive
432 # letter. This means that the invalid filename \\\a\b is preserved
433 # unchanged, where a\\\b is normalised to a\b. It's not clear that there
434 # is any better behaviour for such edge cases.
435 if prefix == '':
436 # No drive letter - preserve initial backslashes
437 while path[:1] == "\\":
Ezio Melottib5689de2010-01-12 03:32:05 +0000438 prefix = prefix + backslash
Brett Cannonbdc36272004-07-10 20:42:22 +0000439 path = path[1:]
440 else:
441 # We have a drive letter - collapse initial backslashes
442 if path.startswith("\\"):
Ezio Melottib5689de2010-01-12 03:32:05 +0000443 prefix = prefix + backslash
Brett Cannonbdc36272004-07-10 20:42:22 +0000444 path = path.lstrip("\\")
Fred Drakeb4e460a2000-09-28 16:25:20 +0000445 comps = path.split("\\")
Guido van Rossum15e22e11997-12-05 19:03:01 +0000446 i = 0
447 while i < len(comps):
Tim Peters54a14a32001-08-30 22:05:26 +0000448 if comps[i] in ('.', ''):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000449 del comps[i]
Tim Peters54a14a32001-08-30 22:05:26 +0000450 elif comps[i] == '..':
451 if i > 0 and comps[i-1] != '..':
452 del comps[i-1:i+1]
453 i -= 1
454 elif i == 0 and prefix.endswith("\\"):
455 del comps[i]
456 else:
457 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000458 else:
Tim Peters54a14a32001-08-30 22:05:26 +0000459 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000460 # If the path is now empty, substitute '.'
461 if not prefix and not comps:
Ezio Melottib5689de2010-01-12 03:32:05 +0000462 comps.append(dot)
463 return prefix + backslash.join(comps)
Guido van Rossume294cf61999-01-29 18:05:18 +0000464
465
466# Return an absolute path.
Tim Peters21fbd572006-04-21 21:18:10 +0000467try:
468 from nt import _getfullpathname
Mark Hammondf717f052002-01-17 00:44:26 +0000469
Tim Peters21fbd572006-04-21 21:18:10 +0000470except ImportError: # not running on Windows - mock up something sensible
471 def abspath(path):
472 """Return the absolute version of a path."""
473 if not isabs(path):
Ezio Melotti4cc80ca2010-02-20 08:09:39 +0000474 if isinstance(path, unicode):
475 cwd = os.getcwdu()
476 else:
477 cwd = os.getcwd()
478 path = join(cwd, path)
Tim Peters21fbd572006-04-21 21:18:10 +0000479 return normpath(path)
480
481else: # use native Windows method on Windows
482 def abspath(path):
483 """Return the absolute version of a path."""
484
485 if path: # Empty path must return current working directory.
486 try:
487 path = _getfullpathname(path)
488 except WindowsError:
489 pass # Bad path - return unchanged.
Ezio Melotti4cc80ca2010-02-20 08:09:39 +0000490 elif isinstance(path, unicode):
491 path = os.getcwdu()
Tim Peters21fbd572006-04-21 21:18:10 +0000492 else:
493 path = os.getcwd()
494 return normpath(path)
Guido van Rossum83eeef42001-09-17 15:16:09 +0000495
496# realpath is a no-op on systems without islink support
497realpath = abspath
Mark Hammond8696ebc2002-10-08 02:44:31 +0000498# Win9x family and earlier have no Unicode filename support.
Tim Peters26bc25a2002-10-09 07:56:04 +0000499supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
500 sys.getwindowsversion()[3] >= 2)
Collin Winter6f187742007-03-16 22:16:08 +0000501
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000502def _abspath_split(path):
503 abs = abspath(normpath(path))
504 prefix, rest = splitunc(abs)
505 is_unc = bool(prefix)
506 if not is_unc:
507 prefix, rest = splitdrive(abs)
508 return is_unc, prefix, [x for x in rest.split(sep) if x]
509
Collin Winter6f187742007-03-16 22:16:08 +0000510def relpath(path, start=curdir):
511 """Return a relative version of a path"""
512
513 if not path:
514 raise ValueError("no path specified")
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000515
516 start_is_unc, start_prefix, start_list = _abspath_split(start)
517 path_is_unc, path_prefix, path_list = _abspath_split(path)
518
519 if path_is_unc ^ start_is_unc:
520 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
521 % (path, start))
522 if path_prefix.lower() != start_prefix.lower():
523 if path_is_unc:
524 raise ValueError("path is on UNC root %s, start on UNC root %s"
525 % (path_prefix, start_prefix))
Collin Winter6f187742007-03-16 22:16:08 +0000526 else:
527 raise ValueError("path is on drive %s, start on drive %s"
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000528 % (path_prefix, start_prefix))
Collin Winter6f187742007-03-16 22:16:08 +0000529 # Work out how much of the filepath is shared by start and path.
Hirokazu Yamamoto8596ef72010-10-19 01:23:51 +0000530 i = 0
531 for e1, e2 in zip(start_list, path_list):
532 if e1.lower() != e2.lower():
Collin Winter6f187742007-03-16 22:16:08 +0000533 break
Collin Winter6f187742007-03-16 22:16:08 +0000534 i += 1
535
536 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
Georg Brandl183a0842008-01-06 14:27:15 +0000537 if not rel_list:
538 return curdir
Collin Winter6f187742007-03-16 22:16:08 +0000539 return join(*rel_list)
Brian Curtincaea7e82011-06-08 19:29:53 -0500540
541try:
542 # The genericpath.isdir implementation uses os.stat and checks the mode
543 # attribute to tell whether or not the path is a directory.
544 # This is overkill on Windows - just pass the path to GetFileAttributes
545 # and check the attribute from there.
Brian Curtin5446f082011-06-09 10:00:42 -0500546 from nt import _isdir as isdir
Brian Curtincaea7e82011-06-08 19:29:53 -0500547except ImportError:
Brian Curtin5446f082011-06-09 10:00:42 -0500548 # Use genericpath.isdir as imported above.
549 pass