blob: a8f4b37f6487e2aaa6cabad7a6f8a8e72affc7e5 [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 Heimes05e8be12008-02-23 18:30:17 +000010import stat
Guido van Rossumd8faa362007-04-27 19:54:29 +000011import genericpath
Thomas Wouters89f507f2006-12-13 04:49:30 +000012from genericpath import *
Skip Montanaro4d5d5bf2000-07-13 01:01:03 +000013
Skip Montanaro269b83b2001-02-06 01:07:02 +000014__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
15 "basename","dirname","commonprefix","getsize","getmtime",
Georg Brandlf0de6a12005-08-22 18:02:59 +000016 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
Benjamin Petersond71ca412008-05-08 23:44:58 +000017 "ismount", "expanduser","expandvars","normpath","abspath",
Georg Brandlf0de6a12005-08-22 18:02:59 +000018 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
Brian Curtind40e6f72010-07-08 21:39:08 +000019 "extsep","devnull","realpath","supports_unicode_filenames","relpath",
Serhiy Storchaka38220932015-03-31 15:31:53 +030020 "samefile", "sameopenfile", "samestat", "commonpath"]
Guido van Rossum555915a1994-02-24 11:32:59 +000021
Skip Montanaro117910d2003-02-14 19:35:31 +000022# strings representing various path-related bits and pieces
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +000023# These are primarily for export; internally, they are hardcoded.
Skip Montanaro117910d2003-02-14 19:35:31 +000024curdir = '.'
25pardir = '..'
26extsep = '.'
27sep = '\\'
28pathsep = ';'
Skip Montanaro9ddac3e2003-03-28 22:23:24 +000029altsep = '/'
Andrew MacIntyre437966c2003-02-17 09:17:50 +000030defpath = '.;C:\\bin'
Martin v. Löwisbdec50f2004-06-08 08:29:33 +000031devnull = 'nul'
Skip Montanaro117910d2003-02-14 19:35:31 +000032
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +000033def _get_bothseps(path):
34 if isinstance(path, bytes):
35 return b'\\/'
36 else:
37 return '\\/'
38
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."""
Brett Cannon3f9183b2016-08-26 14:44:48 -070047 s = os.fspath(s)
Serhiy Storchaka8518b792014-07-23 20:43:13 +030048 try:
49 if isinstance(s, bytes):
50 return s.replace(b'/', b'\\').lower()
51 else:
52 return s.replace('/', '\\').lower()
53 except (TypeError, AttributeError):
54 if not isinstance(s, (bytes, str)):
55 raise TypeError("normcase() argument must be str or bytes, "
56 "not %r" % s.__class__.__name__) from None
57 raise
Guido van Rossum555915a1994-02-24 11:32:59 +000058
Guido van Rossum77e1db31997-06-02 23:11:57 +000059
Fred Drakeef0b5dd2000-02-17 17:30:40 +000060# Return whether a path is absolute.
Mark Hammond5a607a32009-05-06 08:04:54 +000061# Trivial in Posix, harder on Windows.
62# For Windows it is absolute if it starts with a slash or backslash (current
63# volume), or if a pathname after the volume-letter-and-colon or UNC-resource
Guido van Rossum534972b1999-02-03 17:20:50 +000064# starts with a slash or backslash.
Guido van Rossum555915a1994-02-24 11:32:59 +000065
66def isabs(s):
Guido van Rossum15e22e11997-12-05 19:03:01 +000067 """Test whether a path is absolute"""
Brett Cannon3f9183b2016-08-26 14:44:48 -070068 s = os.fspath(s)
Guido van Rossum15e22e11997-12-05 19:03:01 +000069 s = splitdrive(s)[1]
Serhiy Storchaka8518b792014-07-23 20:43:13 +030070 return len(s) > 0 and s[0] in _get_bothseps(s)
Guido van Rossum555915a1994-02-24 11:32:59 +000071
72
Guido van Rossum77e1db31997-06-02 23:11:57 +000073# Join two (or more) paths.
Serhiy Storchakac369c2c2014-01-27 23:15:14 +020074def join(path, *paths):
Brett Cannon3f9183b2016-08-26 14:44:48 -070075 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +030076 if isinstance(path, bytes):
77 sep = b'\\'
78 seps = b'\\/'
79 colon = b':'
80 else:
81 sep = '\\'
82 seps = '\\/'
83 colon = ':'
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030084 try:
Serhiy Storchaka5bfc03f2015-05-19 11:00:07 +030085 if not paths:
86 path[:0] + sep #23780: Ensure compatible data type even if p is null.
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030087 result_drive, result_path = splitdrive(path)
Brett Cannon3f9183b2016-08-26 14:44:48 -070088 for p in map(os.fspath, paths):
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030089 p_drive, p_path = splitdrive(p)
90 if p_path and p_path[0] in seps:
91 # Second path is absolute
92 if p_drive or not result_drive:
93 result_drive = p_drive
Serhiy Storchakac369c2c2014-01-27 23:15:14 +020094 result_path = p_path
95 continue
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030096 elif p_drive and p_drive != result_drive:
97 if p_drive.lower() != result_drive.lower():
98 # Different drives => ignore the first path entirely
99 result_drive = p_drive
100 result_path = p_path
101 continue
102 # Same drive in different case
103 result_drive = p_drive
104 # Second path is relative to the first
105 if result_path and result_path[-1] not in seps:
106 result_path = result_path + sep
107 result_path = result_path + p_path
108 ## add separator between UNC and non-absolute path
109 if (result_path and result_path[0] not in seps and
110 result_drive and result_drive[-1:] != colon):
111 return result_drive + sep + result_path
112 return result_drive + result_path
113 except (TypeError, AttributeError, BytesWarning):
114 genericpath._check_arg_types('join', path, *paths)
115 raise
Guido van Rossum555915a1994-02-24 11:32:59 +0000116
117
118# Split a path in a drive specification (a drive letter followed by a
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000119# colon) and the path specification.
Guido van Rossum555915a1994-02-24 11:32:59 +0000120# It is always true that drivespec + pathspec == p
121def splitdrive(p):
Mark Hammond5a607a32009-05-06 08:04:54 +0000122 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
123 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
124
125 If you assign
126 result = splitdrive(p)
127 It is always true that:
128 result[0] + result[1] == p
129
130 If the path contained a drive letter, drive_or_unc will contain everything
131 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
132
133 If the path contained a UNC path, the drive_or_unc will contain the host name
134 and share up to but not including the fourth directory separator character.
135 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
136
137 Paths cannot contain both a drive letter and a UNC path.
138
139 """
Brett Cannon3f9183b2016-08-26 14:44:48 -0700140 p = os.fspath(p)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300141 if len(p) >= 2:
142 if isinstance(p, bytes):
143 sep = b'\\'
144 altsep = b'/'
145 colon = b':'
146 else:
147 sep = '\\'
148 altsep = '/'
149 colon = ':'
150 normp = p.replace(altsep, sep)
Mark Hammond5a607a32009-05-06 08:04:54 +0000151 if (normp[0:2] == sep*2) and (normp[2:3] != sep):
152 # is a UNC path:
153 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
154 # \\machine\mountpoint\directory\etc\...
155 # directory ^^^^^^^^^^^^^^^
156 index = normp.find(sep, 2)
157 if index == -1:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300158 return p[:0], p
Mark Hammond5a607a32009-05-06 08:04:54 +0000159 index2 = normp.find(sep, index + 1)
160 # a UNC path can't have two slashes in a row
161 # (after the initial two)
162 if index2 == index + 1:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300163 return p[:0], p
Mark Hammond5a607a32009-05-06 08:04:54 +0000164 if index2 == -1:
165 index2 = len(p)
166 return p[:index2], p[index2:]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300167 if normp[1:2] == colon:
Mark Hammond5a607a32009-05-06 08:04:54 +0000168 return p[:2], p[2:]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300169 return p[:0], p
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000170
171
172# Parse UNC paths
173def splitunc(p):
Mark Hammond5a607a32009-05-06 08:04:54 +0000174 """Deprecated since Python 3.1. Please use splitdrive() instead;
175 it now handles UNC paths.
176
177 Split a pathname into UNC mount point and relative path specifiers.
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000178
179 Return a 2-tuple (unc, rest); either part may be empty.
180 If unc is not empty, it has the form '//host/mount' (or similar
181 using backslashes). unc+rest is always the input path.
Martin Panter6245cb32016-04-15 02:14:19 +0000182 Paths containing drive letters never have a UNC part.
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000183 """
Mark Hammond5a607a32009-05-06 08:04:54 +0000184 import warnings
185 warnings.warn("ntpath.splitunc is deprecated, use ntpath.splitdrive instead",
Serhiy Storchaka593568b2013-12-16 15:13:28 +0200186 DeprecationWarning, 2)
187 drive, path = splitdrive(p)
188 if len(drive) == 2:
189 # Drive letter present
190 return p[:0], p
191 return drive, path
Guido van Rossum555915a1994-02-24 11:32:59 +0000192
193
194# Split a path in head (everything up to the last '/') and tail (the
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000195# rest). After the trailing '/' is stripped, the invariant
Guido van Rossum555915a1994-02-24 11:32:59 +0000196# join(head, tail) == p holds.
197# The resulting head won't end in '/' unless it is the root.
198
199def split(p):
Guido van Rossum534972b1999-02-03 17:20:50 +0000200 """Split a pathname.
201
202 Return tuple (head, tail) where tail is everything after the final slash.
203 Either part may be empty."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700204 p = os.fspath(p)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000205 seps = _get_bothseps(p)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000206 d, p = splitdrive(p)
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000207 # set i to index beyond p's last slash
208 i = len(p)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000209 while i and p[i-1] not in seps:
Georg Brandl599b65d2010-07-23 08:46:35 +0000210 i -= 1
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000211 head, tail = p[:i], p[i:] # now tail has no slashes
212 # remove trailing slashes from head, unless it's all slashes
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300213 head = head.rstrip(seps) or head
Guido van Rossum15e22e11997-12-05 19:03:01 +0000214 return d + head, tail
Guido van Rossum555915a1994-02-24 11:32:59 +0000215
216
217# Split a path in root and extension.
Guido van Rossum73e122f1997-01-22 00:17:26 +0000218# The extension is everything starting at the last dot in the last
Guido van Rossum555915a1994-02-24 11:32:59 +0000219# pathname component; the root is everything before that.
220# It is always true that root + ext == p.
221
222def splitext(p):
Brett Cannon3f9183b2016-08-26 14:44:48 -0700223 p = os.fspath(p)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300224 if isinstance(p, bytes):
225 return genericpath._splitext(p, b'\\', b'/', b'.')
226 else:
227 return genericpath._splitext(p, '\\', '/', '.')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000228splitext.__doc__ = genericpath._splitext.__doc__
Guido van Rossum555915a1994-02-24 11:32:59 +0000229
230
231# Return the tail (basename) part of a path.
232
233def basename(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000234 """Returns the final component of a pathname"""
235 return split(p)[1]
Guido van Rossum555915a1994-02-24 11:32:59 +0000236
237
238# Return the head (dirname) part of a path.
239
240def dirname(p):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000241 """Returns the directory component of a pathname"""
242 return split(p)[0]
Guido van Rossum555915a1994-02-24 11:32:59 +0000243
Guido van Rossum555915a1994-02-24 11:32:59 +0000244# Is a path a symbolic link?
Brian Curtind40e6f72010-07-08 21:39:08 +0000245# This will always return false on systems where os.lstat doesn't exist.
Guido van Rossum555915a1994-02-24 11:32:59 +0000246
247def islink(path):
Brian Curtind40e6f72010-07-08 21:39:08 +0000248 """Test whether a path is a symbolic link.
Jesus Ceaf1af7052012-10-05 02:48:46 +0200249 This will always return false for Windows prior to 6.0.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000250 """
Brian Curtind40e6f72010-07-08 21:39:08 +0000251 try:
252 st = os.lstat(path)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +0200253 except (OSError, AttributeError):
Brian Curtind40e6f72010-07-08 21:39:08 +0000254 return False
255 return stat.S_ISLNK(st.st_mode)
Guido van Rossum555915a1994-02-24 11:32:59 +0000256
Brian Curtind40e6f72010-07-08 21:39:08 +0000257# Being true for dangling symbolic links is also useful.
258
259def lexists(path):
260 """Test whether a path exists. Returns True for broken symbolic links"""
261 try:
262 st = os.lstat(path)
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200263 except OSError:
Brian Curtind40e6f72010-07-08 21:39:08 +0000264 return False
265 return True
Johannes Gijsbersae882f72004-08-30 10:19:56 +0000266
Tim Golden6b528062013-08-01 12:44:00 +0100267# Is a path a mount point?
268# Any drive letter root (eg c:\)
269# Any share UNC (eg \\server\share)
270# Any volume mounted on a filesystem folder
271#
272# No one method detects all three situations. Historically we've lexically
273# detected drive letter roots and share UNCs. The canonical approach to
274# detecting mounted volumes (querying the reparse tag) fails for the most
275# common case: drive letter roots. The alternative which uses GetVolumePathName
276# fails if the drive letter is the result of a SUBST.
277try:
278 from nt import _getvolumepathname
279except ImportError:
280 _getvolumepathname = None
Guido van Rossum555915a1994-02-24 11:32:59 +0000281def ismount(path):
Tim Golden6b528062013-08-01 12:44:00 +0100282 """Test whether a path is a mount point (a drive root, the root of a
283 share, or a mounted volume)"""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700284 path = os.fspath(path)
Benjamin Peterson48e24782009-03-29 13:02:52 +0000285 seps = _get_bothseps(path)
Tim Golden6b528062013-08-01 12:44:00 +0100286 path = abspath(path)
Mark Hammond5a607a32009-05-06 08:04:54 +0000287 root, rest = splitdrive(path)
288 if root and root[0] in seps:
289 return (not rest) or (rest in seps)
Tim Golden6b528062013-08-01 12:44:00 +0100290 if rest in seps:
291 return True
292
293 if _getvolumepathname:
294 return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
295 else:
296 return False
Guido van Rossum555915a1994-02-24 11:32:59 +0000297
298
Guido van Rossum555915a1994-02-24 11:32:59 +0000299# Expand paths beginning with '~' or '~user'.
300# '~' means $HOME; '~user' means that user's home directory.
301# If the path doesn't begin with '~', or if the user or $HOME is unknown,
302# the path is returned unchanged (leaving error reporting to whatever
303# function is called with the expanded path as argument).
304# See also module 'glob' for expansion of *, ? and [...] in pathnames.
305# (A function should also be defined to do full *sh-style environment
306# variable expansion.)
307
308def expanduser(path):
Guido van Rossum534972b1999-02-03 17:20:50 +0000309 """Expand ~ and ~user constructs.
310
311 If user or $HOME is unknown, do nothing."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700312 path = os.fspath(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000313 if isinstance(path, bytes):
314 tilde = b'~'
315 else:
316 tilde = '~'
317 if not path.startswith(tilde):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000318 return path
319 i, n = 1, len(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000320 while i < n and path[i] not in _get_bothseps(path):
Georg Brandl599b65d2010-07-23 08:46:35 +0000321 i += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000322
323 if 'HOME' in os.environ:
324 userhome = os.environ['HOME']
325 elif 'USERPROFILE' in os.environ:
326 userhome = os.environ['USERPROFILE']
327 elif not 'HOMEPATH' in os.environ:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000328 return path
Guido van Rossumd8faa362007-04-27 19:54:29 +0000329 else:
330 try:
331 drive = os.environ['HOMEDRIVE']
332 except KeyError:
333 drive = ''
334 userhome = join(drive, os.environ['HOMEPATH'])
335
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000336 if isinstance(path, bytes):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300337 userhome = os.fsencode(userhome)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000338
Guido van Rossumd8faa362007-04-27 19:54:29 +0000339 if i != 1: #~user
340 userhome = join(dirname(userhome), path[1:i])
341
Guido van Rossum15e22e11997-12-05 19:03:01 +0000342 return userhome + path[i:]
Guido van Rossum555915a1994-02-24 11:32:59 +0000343
344
345# Expand paths containing shell variable substitutions.
346# The following rules apply:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000347# - no expansion within single quotes
Guido van Rossumd8faa362007-04-27 19:54:29 +0000348# - '$$' is translated into '$'
349# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
Guido van Rossum15e22e11997-12-05 19:03:01 +0000350# - ${varname} is accepted.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000351# - $varname is accepted.
352# - %varname% is accepted.
353# - varnames can be made out of letters, digits and the characters '_-'
Ezio Melotti13925002011-03-16 11:05:33 +0200354# (though is not verified in the ${varname} and %varname% cases)
Guido van Rossum555915a1994-02-24 11:32:59 +0000355# XXX With COMMAND.COM you can use any characters in a variable name,
356# XXX except '^|<>='.
357
Tim Peters2344fae2001-01-15 00:50:52 +0000358def expandvars(path):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000359 """Expand shell variables of the forms $var, ${var} and %var%.
Guido van Rossum534972b1999-02-03 17:20:50 +0000360
361 Unknown variables are left unchanged."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700362 path = os.fspath(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000363 if isinstance(path, bytes):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300364 if b'$' not in path and b'%' not in path:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000365 return path
366 import string
367 varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000368 quote = b'\''
369 percent = b'%'
370 brace = b'{'
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300371 rbrace = b'}'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000372 dollar = b'$'
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200373 environ = getattr(os, 'environb', None)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000374 else:
375 if '$' not in path and '%' not in path:
376 return path
377 import string
378 varchars = string.ascii_letters + string.digits + '_-'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000379 quote = '\''
380 percent = '%'
381 brace = '{'
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300382 rbrace = '}'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000383 dollar = '$'
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200384 environ = os.environ
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000385 res = path[:0]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000386 index = 0
387 pathlen = len(path)
388 while index < pathlen:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000389 c = path[index:index+1]
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000390 if c == quote: # no expansion within single quotes
Guido van Rossum15e22e11997-12-05 19:03:01 +0000391 path = path[index + 1:]
392 pathlen = len(path)
393 try:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000394 index = path.index(c)
Georg Brandl599b65d2010-07-23 08:46:35 +0000395 res += c + path[:index + 1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000396 except ValueError:
Serhiy Storchaka1b87ae02015-03-25 16:40:15 +0200397 res += c + path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000398 index = pathlen - 1
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000399 elif c == percent: # variable or '%'
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000400 if path[index + 1:index + 2] == percent:
Georg Brandl599b65d2010-07-23 08:46:35 +0000401 res += c
402 index += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000403 else:
404 path = path[index+1:]
405 pathlen = len(path)
406 try:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000407 index = path.index(percent)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000408 except ValueError:
Georg Brandl599b65d2010-07-23 08:46:35 +0000409 res += percent + path
Guido van Rossumd8faa362007-04-27 19:54:29 +0000410 index = pathlen - 1
411 else:
412 var = path[:index]
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200413 try:
414 if environ is None:
415 value = os.fsencode(os.environ[os.fsdecode(var)])
416 else:
417 value = environ[var]
418 except KeyError:
419 value = percent + var + percent
Georg Brandl599b65d2010-07-23 08:46:35 +0000420 res += value
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000421 elif c == dollar: # variable or '$$'
422 if path[index + 1:index + 2] == dollar:
Georg Brandl599b65d2010-07-23 08:46:35 +0000423 res += c
424 index += 1
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000425 elif path[index + 1:index + 2] == brace:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000426 path = path[index+2:]
427 pathlen = len(path)
428 try:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300429 index = path.index(rbrace)
Fred Drakeb4e460a2000-09-28 16:25:20 +0000430 except ValueError:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300431 res += dollar + brace + path
Guido van Rossum15e22e11997-12-05 19:03:01 +0000432 index = pathlen - 1
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200433 else:
434 var = path[:index]
435 try:
436 if environ is None:
437 value = os.fsencode(os.environ[os.fsdecode(var)])
438 else:
439 value = environ[var]
440 except KeyError:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300441 value = dollar + brace + var + rbrace
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200442 res += value
Guido van Rossum15e22e11997-12-05 19:03:01 +0000443 else:
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200444 var = path[:0]
Georg Brandl599b65d2010-07-23 08:46:35 +0000445 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000446 c = path[index:index + 1]
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000447 while c and c in varchars:
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200448 var += c
Georg Brandl599b65d2010-07-23 08:46:35 +0000449 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000450 c = path[index:index + 1]
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200451 try:
452 if environ is None:
453 value = os.fsencode(os.environ[os.fsdecode(var)])
454 else:
455 value = environ[var]
456 except KeyError:
457 value = dollar + var
Georg Brandl599b65d2010-07-23 08:46:35 +0000458 res += value
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000459 if c:
Georg Brandl599b65d2010-07-23 08:46:35 +0000460 index -= 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000461 else:
Georg Brandl599b65d2010-07-23 08:46:35 +0000462 res += c
463 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000464 return res
Guido van Rossum555915a1994-02-24 11:32:59 +0000465
466
Tim Peters54a14a32001-08-30 22:05:26 +0000467# 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 +0000468# Previously, this function also truncated pathnames to 8+3 format,
469# but as this module is called "ntpath", that's obviously wrong!
Guido van Rossum555915a1994-02-24 11:32:59 +0000470
471def normpath(path):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000472 """Normalize path, eliminating double slashes, etc."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700473 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300474 if isinstance(path, bytes):
475 sep = b'\\'
476 altsep = b'/'
477 curdir = b'.'
478 pardir = b'..'
479 special_prefixes = (b'\\\\.\\', b'\\\\?\\')
480 else:
481 sep = '\\'
482 altsep = '/'
483 curdir = '.'
484 pardir = '..'
485 special_prefixes = ('\\\\.\\', '\\\\?\\')
Georg Brandlcfb68212010-07-31 21:40:15 +0000486 if path.startswith(special_prefixes):
487 # in the case of paths with these prefixes:
488 # \\.\ -> device names
489 # \\?\ -> literal paths
490 # do not do any normalization, but return the path unchanged
491 return path
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300492 path = path.replace(altsep, sep)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000493 prefix, path = splitdrive(path)
Mark Hammond5a607a32009-05-06 08:04:54 +0000494
495 # collapse initial backslashes
496 if path.startswith(sep):
Georg Brandl599b65d2010-07-23 08:46:35 +0000497 prefix += sep
Mark Hammond5a607a32009-05-06 08:04:54 +0000498 path = path.lstrip(sep)
499
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000500 comps = path.split(sep)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000501 i = 0
502 while i < len(comps):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300503 if not comps[i] or comps[i] == curdir:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000504 del comps[i]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300505 elif comps[i] == pardir:
506 if i > 0 and comps[i-1] != pardir:
Tim Peters54a14a32001-08-30 22:05:26 +0000507 del comps[i-1:i+1]
508 i -= 1
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300509 elif i == 0 and prefix.endswith(sep):
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000510 del comps[i]
511 else:
512 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000513 else:
Tim Peters54a14a32001-08-30 22:05:26 +0000514 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000515 # If the path is now empty, substitute '.'
516 if not prefix and not comps:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300517 comps.append(curdir)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000518 return prefix + sep.join(comps)
Guido van Rossume294cf61999-01-29 18:05:18 +0000519
520
521# Return an absolute path.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000522try:
523 from nt import _getfullpathname
Mark Hammondf717f052002-01-17 00:44:26 +0000524
Brett Cannoncd171c82013-07-04 17:43:24 -0400525except ImportError: # not running on Windows - mock up something sensible
Thomas Wouters477c8d52006-05-27 19:21:47 +0000526 def abspath(path):
527 """Return the absolute version of a path."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700528 path = os.fspath(path)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000529 if not isabs(path):
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000530 if isinstance(path, bytes):
531 cwd = os.getcwdb()
532 else:
533 cwd = os.getcwd()
534 path = join(cwd, path)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535 return normpath(path)
536
537else: # use native Windows method on Windows
538 def abspath(path):
539 """Return the absolute version of a path."""
540
541 if path: # Empty path must return current working directory.
Brett Cannon3f9183b2016-08-26 14:44:48 -0700542 path = os.fspath(path)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543 try:
544 path = _getfullpathname(path)
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200545 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000546 pass # Bad path - return unchanged.
Florent Xiclunaad8c5ca2010-03-08 14:44:41 +0000547 elif isinstance(path, bytes):
548 path = os.getcwdb()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549 else:
550 path = os.getcwd()
551 return normpath(path)
Guido van Rossum83eeef42001-09-17 15:16:09 +0000552
553# realpath is a no-op on systems without islink support
554realpath = abspath
Mark Hammond8696ebc2002-10-08 02:44:31 +0000555# Win9x family and earlier have no Unicode filename support.
Tim Peters26bc25a2002-10-09 07:56:04 +0000556supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
557 sys.getwindowsversion()[3] >= 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000558
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300559def relpath(path, start=None):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000560 """Return a relative version of a path"""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700561 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300562 if isinstance(path, bytes):
563 sep = b'\\'
564 curdir = b'.'
565 pardir = b'..'
566 else:
567 sep = '\\'
568 curdir = '.'
569 pardir = '..'
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000570
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300571 if start is None:
572 start = curdir
Guido van Rossumd8faa362007-04-27 19:54:29 +0000573
574 if not path:
575 raise ValueError("no path specified")
Mark Hammond5a607a32009-05-06 08:04:54 +0000576
Brett Cannon3f9183b2016-08-26 14:44:48 -0700577 start = os.fspath(start)
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300578 try:
579 start_abs = abspath(normpath(start))
580 path_abs = abspath(normpath(path))
581 start_drive, start_rest = splitdrive(start_abs)
582 path_drive, path_rest = splitdrive(path_abs)
583 if normcase(start_drive) != normcase(path_drive):
584 raise ValueError("path is on mount %r, start on mount %r" % (
585 path_drive, start_drive))
Mark Hammond5a607a32009-05-06 08:04:54 +0000586
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300587 start_list = [x for x in start_rest.split(sep) if x]
588 path_list = [x for x in path_rest.split(sep) if x]
589 # Work out how much of the filepath is shared by start and path.
590 i = 0
591 for e1, e2 in zip(start_list, path_list):
592 if normcase(e1) != normcase(e2):
593 break
594 i += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000595
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300596 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
597 if not rel_list:
598 return curdir
599 return join(*rel_list)
Serhiy Storchakae4f47082014-10-04 16:09:02 +0300600 except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300601 genericpath._check_arg_types('relpath', path, start)
602 raise
Brian Curtind40e6f72010-07-08 21:39:08 +0000603
604
Serhiy Storchaka38220932015-03-31 15:31:53 +0300605# Return the longest common sub-path of the sequence of paths given as input.
606# The function is case-insensitive and 'separator-insensitive', i.e. if the
607# only difference between two paths is the use of '\' versus '/' as separator,
608# they are deemed to be equal.
609#
610# However, the returned path will have the standard '\' separator (even if the
611# given paths had the alternative '/' separator) and will have the case of the
612# first path given in the sequence. Additionally, any trailing separator is
613# stripped from the returned path.
614
615def commonpath(paths):
616 """Given a sequence of path names, returns the longest common sub-path."""
617
618 if not paths:
619 raise ValueError('commonpath() arg is an empty sequence')
620
Brett Cannon3f9183b2016-08-26 14:44:48 -0700621 paths = tuple(map(os.fspath, paths))
Serhiy Storchaka38220932015-03-31 15:31:53 +0300622 if isinstance(paths[0], bytes):
623 sep = b'\\'
624 altsep = b'/'
625 curdir = b'.'
626 else:
627 sep = '\\'
628 altsep = '/'
629 curdir = '.'
630
631 try:
632 drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
633 split_paths = [p.split(sep) for d, p in drivesplits]
634
635 try:
636 isabs, = set(p[:1] == sep for d, p in drivesplits)
637 except ValueError:
638 raise ValueError("Can't mix absolute and relative paths") from None
639
640 # Check that all drive letters or UNC paths match. The check is made only
641 # now otherwise type errors for mixing strings and bytes would not be
642 # caught.
643 if len(set(d for d, p in drivesplits)) != 1:
644 raise ValueError("Paths don't have the same drive")
645
646 drive, path = splitdrive(paths[0].replace(altsep, sep))
647 common = path.split(sep)
648 common = [c for c in common if c and c != curdir]
649
650 split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
651 s1 = min(split_paths)
652 s2 = max(split_paths)
653 for i, c in enumerate(s1):
654 if c != s2[i]:
655 common = common[:i]
656 break
657 else:
658 common = common[:len(s1)]
659
660 prefix = drive + sep if isabs else drive
661 return prefix + sep.join(common)
662 except (TypeError, AttributeError):
663 genericpath._check_arg_types('commonpath', *paths)
664 raise
665
666
Brian Curtind40e6f72010-07-08 21:39:08 +0000667# determine if two files are in fact the same file
Brian Curtin0dac8082010-09-23 20:38:14 +0000668try:
Brian Curtine8e80422010-09-24 13:56:34 +0000669 # GetFinalPathNameByHandle is available starting with Windows 6.0.
670 # Windows XP and non-Windows OS'es will mock _getfinalpathname.
671 if sys.getwindowsversion()[:2] >= (6, 0):
672 from nt import _getfinalpathname
673 else:
674 raise ImportError
675except (AttributeError, ImportError):
Brian Curtin0dac8082010-09-23 20:38:14 +0000676 # On Windows XP and earlier, two files are the same if their absolute
677 # pathnames are the same.
Brian Curtine8e80422010-09-24 13:56:34 +0000678 # Non-Windows operating systems fake this method with an XP
679 # approximation.
Brian Curtin0dac8082010-09-23 20:38:14 +0000680 def _getfinalpathname(f):
Ronald Oussoren6355c162011-05-06 17:11:07 +0200681 return normcase(abspath(f))
Brian Curtin0dac8082010-09-23 20:38:14 +0000682
Brian Curtin9c669cc2011-06-08 18:17:18 -0500683
684try:
685 # The genericpath.isdir implementation uses os.stat and checks the mode
686 # attribute to tell whether or not the path is a directory.
687 # This is overkill on Windows - just pass the path to GetFileAttributes
688 # and check the attribute from there.
Brian Curtin95d028f2011-06-09 09:10:38 -0500689 from nt import _isdir as isdir
Brett Cannoncd171c82013-07-04 17:43:24 -0400690except ImportError:
Brian Curtin95d028f2011-06-09 09:10:38 -0500691 # Use genericpath.isdir as imported above.
692 pass