blob: ef4999e1473acb00b80e9c8acc61948232ea2189 [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
Serhiy Storchaka34601982018-01-07 17:54:31 +02008# strings representing various path-related bits and pieces
9# These are primarily for export; internally, they are hardcoded.
10# Should be set before imports for resolving cyclic dependency.
11curdir = '.'
12pardir = '..'
13extsep = '.'
14sep = '\\'
15pathsep = ';'
16altsep = '/'
17defpath = '.;C:\\bin'
18devnull = 'nul'
19
Guido van Rossum555915a1994-02-24 11:32:59 +000020import os
Mark Hammond8696ebc2002-10-08 02:44:31 +000021import sys
Christian Heimes05e8be12008-02-23 18:30:17 +000022import stat
Guido van Rossumd8faa362007-04-27 19:54:29 +000023import genericpath
Thomas Wouters89f507f2006-12-13 04:49:30 +000024from genericpath import *
Skip Montanaro4d5d5bf2000-07-13 01:01:03 +000025
Skip Montanaro269b83b2001-02-06 01:07:02 +000026__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
27 "basename","dirname","commonprefix","getsize","getmtime",
Georg Brandlf0de6a12005-08-22 18:02:59 +000028 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
Benjamin Petersond71ca412008-05-08 23:44:58 +000029 "ismount", "expanduser","expandvars","normpath","abspath",
Serhiy Storchaka9ed707e2017-01-13 20:55:05 +020030 "curdir","pardir","sep","pathsep","defpath","altsep",
Brian Curtind40e6f72010-07-08 21:39:08 +000031 "extsep","devnull","realpath","supports_unicode_filenames","relpath",
Serhiy Storchaka38220932015-03-31 15:31:53 +030032 "samefile", "sameopenfile", "samestat", "commonpath"]
Guido van Rossum555915a1994-02-24 11:32:59 +000033
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +000034def _get_bothseps(path):
35 if isinstance(path, bytes):
36 return b'\\/'
37 else:
38 return '\\/'
39
Guido van Rossume2ad88c1997-08-12 14:46:58 +000040# Normalize the case of a pathname and map slashes to backslashes.
41# Other normalizations (such as optimizing '../' away) are not done
Guido van Rossum555915a1994-02-24 11:32:59 +000042# (this is done by normpath).
Guido van Rossume2ad88c1997-08-12 14:46:58 +000043
Guido van Rossum555915a1994-02-24 11:32:59 +000044def normcase(s):
Guido van Rossum16a0bc21998-02-18 13:48:31 +000045 """Normalize case of pathname.
46
Guido van Rossum534972b1999-02-03 17:20:50 +000047 Makes all characters lowercase and all slashes into backslashes."""
Brett Cannon3f9183b2016-08-26 14:44:48 -070048 s = os.fspath(s)
Wolfgang Maier74510e22019-03-28 22:47:18 +010049 if isinstance(s, bytes):
50 return s.replace(b'/', b'\\').lower()
51 else:
52 return s.replace('/', '\\').lower()
Guido van Rossum555915a1994-02-24 11:32:59 +000053
Guido van Rossum77e1db31997-06-02 23:11:57 +000054
Fred Drakeef0b5dd2000-02-17 17:30:40 +000055# Return whether a path is absolute.
Mark Hammond5a607a32009-05-06 08:04:54 +000056# Trivial in Posix, harder on Windows.
57# For Windows it is absolute if it starts with a slash or backslash (current
58# volume), or if a pathname after the volume-letter-and-colon or UNC-resource
Guido van Rossum534972b1999-02-03 17:20:50 +000059# starts with a slash or backslash.
Guido van Rossum555915a1994-02-24 11:32:59 +000060
61def isabs(s):
Guido van Rossum15e22e11997-12-05 19:03:01 +000062 """Test whether a path is absolute"""
Brett Cannon3f9183b2016-08-26 14:44:48 -070063 s = os.fspath(s)
Guido van Rossum15e22e11997-12-05 19:03:01 +000064 s = splitdrive(s)[1]
Serhiy Storchaka8518b792014-07-23 20:43:13 +030065 return len(s) > 0 and s[0] in _get_bothseps(s)
Guido van Rossum555915a1994-02-24 11:32:59 +000066
67
Guido van Rossum77e1db31997-06-02 23:11:57 +000068# Join two (or more) paths.
Serhiy Storchakac369c2c2014-01-27 23:15:14 +020069def join(path, *paths):
Brett Cannon3f9183b2016-08-26 14:44:48 -070070 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +030071 if isinstance(path, bytes):
72 sep = b'\\'
73 seps = b'\\/'
74 colon = b':'
75 else:
76 sep = '\\'
77 seps = '\\/'
78 colon = ':'
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030079 try:
Serhiy Storchaka5bfc03f2015-05-19 11:00:07 +030080 if not paths:
81 path[:0] + sep #23780: Ensure compatible data type even if p is null.
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030082 result_drive, result_path = splitdrive(path)
Brett Cannon3f9183b2016-08-26 14:44:48 -070083 for p in map(os.fspath, paths):
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030084 p_drive, p_path = splitdrive(p)
85 if p_path and p_path[0] in seps:
86 # Second path is absolute
87 if p_drive or not result_drive:
88 result_drive = p_drive
Serhiy Storchakac369c2c2014-01-27 23:15:14 +020089 result_path = p_path
90 continue
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +030091 elif p_drive and p_drive != result_drive:
92 if p_drive.lower() != result_drive.lower():
93 # Different drives => ignore the first path entirely
94 result_drive = p_drive
95 result_path = p_path
96 continue
97 # Same drive in different case
98 result_drive = p_drive
99 # Second path is relative to the first
100 if result_path and result_path[-1] not in seps:
101 result_path = result_path + sep
102 result_path = result_path + p_path
103 ## add separator between UNC and non-absolute path
104 if (result_path and result_path[0] not in seps and
105 result_drive and result_drive[-1:] != colon):
106 return result_drive + sep + result_path
107 return result_drive + result_path
108 except (TypeError, AttributeError, BytesWarning):
109 genericpath._check_arg_types('join', path, *paths)
110 raise
Guido van Rossum555915a1994-02-24 11:32:59 +0000111
112
113# Split a path in a drive specification (a drive letter followed by a
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000114# colon) and the path specification.
Guido van Rossum555915a1994-02-24 11:32:59 +0000115# It is always true that drivespec + pathspec == p
116def splitdrive(p):
Mark Hammond5a607a32009-05-06 08:04:54 +0000117 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
118 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
119
120 If you assign
121 result = splitdrive(p)
122 It is always true that:
123 result[0] + result[1] == p
124
125 If the path contained a drive letter, drive_or_unc will contain everything
126 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
127
128 If the path contained a UNC path, the drive_or_unc will contain the host name
129 and share up to but not including the fourth directory separator character.
130 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
131
132 Paths cannot contain both a drive letter and a UNC path.
133
134 """
Brett Cannon3f9183b2016-08-26 14:44:48 -0700135 p = os.fspath(p)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300136 if len(p) >= 2:
137 if isinstance(p, bytes):
138 sep = b'\\'
139 altsep = b'/'
140 colon = b':'
141 else:
142 sep = '\\'
143 altsep = '/'
144 colon = ':'
145 normp = p.replace(altsep, sep)
Mark Hammond5a607a32009-05-06 08:04:54 +0000146 if (normp[0:2] == sep*2) and (normp[2:3] != sep):
147 # is a UNC path:
148 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
149 # \\machine\mountpoint\directory\etc\...
150 # directory ^^^^^^^^^^^^^^^
151 index = normp.find(sep, 2)
152 if index == -1:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300153 return p[:0], p
Mark Hammond5a607a32009-05-06 08:04:54 +0000154 index2 = normp.find(sep, index + 1)
155 # a UNC path can't have two slashes in a row
156 # (after the initial two)
157 if index2 == index + 1:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300158 return p[:0], p
Mark Hammond5a607a32009-05-06 08:04:54 +0000159 if index2 == -1:
160 index2 = len(p)
161 return p[:index2], p[index2:]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300162 if normp[1:2] == colon:
Mark Hammond5a607a32009-05-06 08:04:54 +0000163 return p[:2], p[2:]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300164 return p[:0], p
Guido van Rossumf3c695c1999-04-06 19:32:19 +0000165
166
Guido van Rossum555915a1994-02-24 11:32:59 +0000167# Split a path in head (everything up to the last '/') and tail (the
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000168# rest). After the trailing '/' is stripped, the invariant
Guido van Rossum555915a1994-02-24 11:32:59 +0000169# join(head, tail) == p holds.
170# The resulting head won't end in '/' unless it is the root.
171
172def split(p):
Guido van Rossum534972b1999-02-03 17:20:50 +0000173 """Split a pathname.
174
175 Return tuple (head, tail) where tail is everything after the final slash.
176 Either part may be empty."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700177 p = os.fspath(p)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000178 seps = _get_bothseps(p)
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)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000182 while i and p[i-1] not in seps:
Georg Brandl599b65d2010-07-23 08:46:35 +0000183 i -= 1
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +0000184 head, tail = p[:i], p[i:] # now tail has no slashes
185 # remove trailing slashes from head, unless it's all slashes
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300186 head = head.rstrip(seps) or head
Guido van Rossum15e22e11997-12-05 19:03:01 +0000187 return d + head, tail
Guido van Rossum555915a1994-02-24 11:32:59 +0000188
189
190# Split a path in root and extension.
Guido van Rossum73e122f1997-01-22 00:17:26 +0000191# The extension is everything starting at the last dot in the last
Guido van Rossum555915a1994-02-24 11:32:59 +0000192# pathname component; the root is everything before that.
193# It is always true that root + ext == p.
194
195def splitext(p):
Brett Cannon3f9183b2016-08-26 14:44:48 -0700196 p = os.fspath(p)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300197 if isinstance(p, bytes):
198 return genericpath._splitext(p, b'\\', b'/', b'.')
199 else:
200 return genericpath._splitext(p, '\\', '/', '.')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000201splitext.__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?
Brian Curtind40e6f72010-07-08 21:39:08 +0000218# This will always return false on systems where os.lstat doesn't exist.
Guido van Rossum555915a1994-02-24 11:32:59 +0000219
220def islink(path):
Brian Curtind40e6f72010-07-08 21:39:08 +0000221 """Test whether a path is a symbolic link.
Jesus Ceaf1af7052012-10-05 02:48:46 +0200222 This will always return false for Windows prior to 6.0.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000223 """
Brian Curtind40e6f72010-07-08 21:39:08 +0000224 try:
225 st = os.lstat(path)
Serhiy Storchaka0185f342018-09-18 11:28:51 +0300226 except (OSError, ValueError, AttributeError):
Brian Curtind40e6f72010-07-08 21:39:08 +0000227 return False
228 return stat.S_ISLNK(st.st_mode)
Guido van Rossum555915a1994-02-24 11:32:59 +0000229
Brian Curtind40e6f72010-07-08 21:39:08 +0000230# Being true for dangling symbolic links is also useful.
231
232def lexists(path):
233 """Test whether a path exists. Returns True for broken symbolic links"""
234 try:
235 st = os.lstat(path)
Serhiy Storchaka0185f342018-09-18 11:28:51 +0300236 except (OSError, ValueError):
Brian Curtind40e6f72010-07-08 21:39:08 +0000237 return False
238 return True
Johannes Gijsbersae882f72004-08-30 10:19:56 +0000239
Tim Golden6b528062013-08-01 12:44:00 +0100240# Is a path a mount point?
241# Any drive letter root (eg c:\)
242# Any share UNC (eg \\server\share)
243# Any volume mounted on a filesystem folder
244#
245# No one method detects all three situations. Historically we've lexically
246# detected drive letter roots and share UNCs. The canonical approach to
247# detecting mounted volumes (querying the reparse tag) fails for the most
248# common case: drive letter roots. The alternative which uses GetVolumePathName
249# fails if the drive letter is the result of a SUBST.
250try:
251 from nt import _getvolumepathname
252except ImportError:
253 _getvolumepathname = None
Guido van Rossum555915a1994-02-24 11:32:59 +0000254def ismount(path):
Tim Golden6b528062013-08-01 12:44:00 +0100255 """Test whether a path is a mount point (a drive root, the root of a
256 share, or a mounted volume)"""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700257 path = os.fspath(path)
Benjamin Peterson48e24782009-03-29 13:02:52 +0000258 seps = _get_bothseps(path)
Tim Golden6b528062013-08-01 12:44:00 +0100259 path = abspath(path)
Mark Hammond5a607a32009-05-06 08:04:54 +0000260 root, rest = splitdrive(path)
261 if root and root[0] in seps:
262 return (not rest) or (rest in seps)
Tim Golden6b528062013-08-01 12:44:00 +0100263 if rest in seps:
264 return True
265
266 if _getvolumepathname:
267 return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
268 else:
269 return False
Guido van Rossum555915a1994-02-24 11:32:59 +0000270
271
Guido van Rossum555915a1994-02-24 11:32:59 +0000272# Expand paths beginning with '~' or '~user'.
273# '~' means $HOME; '~user' means that user's home directory.
274# If the path doesn't begin with '~', or if the user or $HOME is unknown,
275# the path is returned unchanged (leaving error reporting to whatever
276# function is called with the expanded path as argument).
277# See also module 'glob' for expansion of *, ? and [...] in pathnames.
278# (A function should also be defined to do full *sh-style environment
279# variable expansion.)
280
281def expanduser(path):
Guido van Rossum534972b1999-02-03 17:20:50 +0000282 """Expand ~ and ~user constructs.
283
284 If user or $HOME is unknown, do nothing."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700285 path = os.fspath(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000286 if isinstance(path, bytes):
287 tilde = b'~'
288 else:
289 tilde = '~'
290 if not path.startswith(tilde):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000291 return path
292 i, n = 1, len(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000293 while i < n and path[i] not in _get_bothseps(path):
Georg Brandl599b65d2010-07-23 08:46:35 +0000294 i += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000295
Anthony Sottile25ec4a42019-03-12 08:39:57 -0700296 if 'USERPROFILE' in os.environ:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000297 userhome = os.environ['USERPROFILE']
298 elif not 'HOMEPATH' in os.environ:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000299 return path
Guido van Rossumd8faa362007-04-27 19:54:29 +0000300 else:
301 try:
302 drive = os.environ['HOMEDRIVE']
303 except KeyError:
304 drive = ''
305 userhome = join(drive, os.environ['HOMEPATH'])
306
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000307 if isinstance(path, bytes):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300308 userhome = os.fsencode(userhome)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000309
Guido van Rossumd8faa362007-04-27 19:54:29 +0000310 if i != 1: #~user
311 userhome = join(dirname(userhome), path[1:i])
312
Guido van Rossum15e22e11997-12-05 19:03:01 +0000313 return userhome + path[i:]
Guido van Rossum555915a1994-02-24 11:32:59 +0000314
315
316# Expand paths containing shell variable substitutions.
317# The following rules apply:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000318# - no expansion within single quotes
Guido van Rossumd8faa362007-04-27 19:54:29 +0000319# - '$$' is translated into '$'
320# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
Guido van Rossum15e22e11997-12-05 19:03:01 +0000321# - ${varname} is accepted.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000322# - $varname is accepted.
323# - %varname% is accepted.
324# - varnames can be made out of letters, digits and the characters '_-'
Ezio Melotti13925002011-03-16 11:05:33 +0200325# (though is not verified in the ${varname} and %varname% cases)
Guido van Rossum555915a1994-02-24 11:32:59 +0000326# XXX With COMMAND.COM you can use any characters in a variable name,
327# XXX except '^|<>='.
328
Tim Peters2344fae2001-01-15 00:50:52 +0000329def expandvars(path):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000330 """Expand shell variables of the forms $var, ${var} and %var%.
Guido van Rossum534972b1999-02-03 17:20:50 +0000331
332 Unknown variables are left unchanged."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700333 path = os.fspath(path)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000334 if isinstance(path, bytes):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300335 if b'$' not in path and b'%' not in path:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000336 return path
337 import string
338 varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000339 quote = b'\''
340 percent = b'%'
341 brace = b'{'
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300342 rbrace = b'}'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000343 dollar = b'$'
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200344 environ = getattr(os, 'environb', None)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000345 else:
346 if '$' not in path and '%' not in path:
347 return path
348 import string
349 varchars = string.ascii_letters + string.digits + '_-'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000350 quote = '\''
351 percent = '%'
352 brace = '{'
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300353 rbrace = '}'
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000354 dollar = '$'
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200355 environ = os.environ
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000356 res = path[:0]
Guido van Rossum15e22e11997-12-05 19:03:01 +0000357 index = 0
358 pathlen = len(path)
359 while index < pathlen:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000360 c = path[index:index+1]
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000361 if c == quote: # no expansion within single quotes
Guido van Rossum15e22e11997-12-05 19:03:01 +0000362 path = path[index + 1:]
363 pathlen = len(path)
364 try:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000365 index = path.index(c)
Georg Brandl599b65d2010-07-23 08:46:35 +0000366 res += c + path[:index + 1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000367 except ValueError:
Serhiy Storchaka1b87ae02015-03-25 16:40:15 +0200368 res += c + path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000369 index = pathlen - 1
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000370 elif c == percent: # variable or '%'
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000371 if path[index + 1:index + 2] == percent:
Georg Brandl599b65d2010-07-23 08:46:35 +0000372 res += c
373 index += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000374 else:
375 path = path[index+1:]
376 pathlen = len(path)
377 try:
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000378 index = path.index(percent)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000379 except ValueError:
Georg Brandl599b65d2010-07-23 08:46:35 +0000380 res += percent + path
Guido van Rossumd8faa362007-04-27 19:54:29 +0000381 index = pathlen - 1
382 else:
383 var = path[:index]
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200384 try:
385 if environ is None:
386 value = os.fsencode(os.environ[os.fsdecode(var)])
387 else:
388 value = environ[var]
389 except KeyError:
390 value = percent + var + percent
Georg Brandl599b65d2010-07-23 08:46:35 +0000391 res += value
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000392 elif c == dollar: # variable or '$$'
393 if path[index + 1:index + 2] == dollar:
Georg Brandl599b65d2010-07-23 08:46:35 +0000394 res += c
395 index += 1
Amaury Forgeot d'Arc3b44e612008-10-03 20:32:33 +0000396 elif path[index + 1:index + 2] == brace:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000397 path = path[index+2:]
398 pathlen = len(path)
399 try:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300400 index = path.index(rbrace)
Fred Drakeb4e460a2000-09-28 16:25:20 +0000401 except ValueError:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300402 res += dollar + brace + path
Guido van Rossum15e22e11997-12-05 19:03:01 +0000403 index = pathlen - 1
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200404 else:
405 var = path[:index]
406 try:
407 if environ is None:
408 value = os.fsencode(os.environ[os.fsdecode(var)])
409 else:
410 value = environ[var]
411 except KeyError:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300412 value = dollar + brace + var + rbrace
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200413 res += value
Guido van Rossum15e22e11997-12-05 19:03:01 +0000414 else:
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200415 var = path[:0]
Georg Brandl599b65d2010-07-23 08:46:35 +0000416 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000417 c = path[index:index + 1]
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000418 while c and c in varchars:
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200419 var += c
Georg Brandl599b65d2010-07-23 08:46:35 +0000420 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000421 c = path[index:index + 1]
Serhiy Storchakadbb10192014-02-13 10:13:53 +0200422 try:
423 if environ is None:
424 value = os.fsencode(os.environ[os.fsdecode(var)])
425 else:
426 value = environ[var]
427 except KeyError:
428 value = dollar + var
Georg Brandl599b65d2010-07-23 08:46:35 +0000429 res += value
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000430 if c:
Georg Brandl599b65d2010-07-23 08:46:35 +0000431 index -= 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000432 else:
Georg Brandl599b65d2010-07-23 08:46:35 +0000433 res += c
434 index += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000435 return res
Guido van Rossum555915a1994-02-24 11:32:59 +0000436
437
Tim Peters54a14a32001-08-30 22:05:26 +0000438# 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 +0000439# Previously, this function also truncated pathnames to 8+3 format,
440# but as this module is called "ntpath", that's obviously wrong!
Guido van Rossum555915a1994-02-24 11:32:59 +0000441
442def normpath(path):
Guido van Rossum15e22e11997-12-05 19:03:01 +0000443 """Normalize path, eliminating double slashes, etc."""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700444 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300445 if isinstance(path, bytes):
446 sep = b'\\'
447 altsep = b'/'
448 curdir = b'.'
449 pardir = b'..'
450 special_prefixes = (b'\\\\.\\', b'\\\\?\\')
451 else:
452 sep = '\\'
453 altsep = '/'
454 curdir = '.'
455 pardir = '..'
456 special_prefixes = ('\\\\.\\', '\\\\?\\')
Georg Brandlcfb68212010-07-31 21:40:15 +0000457 if path.startswith(special_prefixes):
458 # in the case of paths with these prefixes:
459 # \\.\ -> device names
460 # \\?\ -> literal paths
461 # do not do any normalization, but return the path unchanged
462 return path
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300463 path = path.replace(altsep, sep)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000464 prefix, path = splitdrive(path)
Mark Hammond5a607a32009-05-06 08:04:54 +0000465
466 # collapse initial backslashes
467 if path.startswith(sep):
Georg Brandl599b65d2010-07-23 08:46:35 +0000468 prefix += sep
Mark Hammond5a607a32009-05-06 08:04:54 +0000469 path = path.lstrip(sep)
470
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000471 comps = path.split(sep)
Guido van Rossum15e22e11997-12-05 19:03:01 +0000472 i = 0
473 while i < len(comps):
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300474 if not comps[i] or comps[i] == curdir:
Guido van Rossum15e22e11997-12-05 19:03:01 +0000475 del comps[i]
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300476 elif comps[i] == pardir:
477 if i > 0 and comps[i-1] != pardir:
Tim Peters54a14a32001-08-30 22:05:26 +0000478 del comps[i-1:i+1]
479 i -= 1
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300480 elif i == 0 and prefix.endswith(sep):
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000481 del comps[i]
482 else:
483 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000484 else:
Tim Peters54a14a32001-08-30 22:05:26 +0000485 i += 1
Guido van Rossum15e22e11997-12-05 19:03:01 +0000486 # If the path is now empty, substitute '.'
487 if not prefix and not comps:
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300488 comps.append(curdir)
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000489 return prefix + sep.join(comps)
Guido van Rossume294cf61999-01-29 18:05:18 +0000490
Franz Wöllertd2e902e2018-07-29 14:47:09 +0200491def _abspath_fallback(path):
492 """Return the absolute version of a path as a fallback function in case
493 `nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
494 more.
495
496 """
497
498 path = os.fspath(path)
499 if not isabs(path):
500 if isinstance(path, bytes):
501 cwd = os.getcwdb()
502 else:
503 cwd = os.getcwd()
504 path = join(cwd, path)
505 return normpath(path)
Guido van Rossume294cf61999-01-29 18:05:18 +0000506
507# Return an absolute path.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000508try:
509 from nt import _getfullpathname
Mark Hammondf717f052002-01-17 00:44:26 +0000510
Brett Cannoncd171c82013-07-04 17:43:24 -0400511except ImportError: # not running on Windows - mock up something sensible
Franz Wöllertd2e902e2018-07-29 14:47:09 +0200512 abspath = _abspath_fallback
Thomas Wouters477c8d52006-05-27 19:21:47 +0000513
514else: # use native Windows method on Windows
515 def abspath(path):
516 """Return the absolute version of a path."""
Franz Wöllertd2e902e2018-07-29 14:47:09 +0200517 try:
Tim Grahamd03b7752018-10-25 11:26:38 -0400518 return normpath(_getfullpathname(path))
Serhiy Storchaka0185f342018-09-18 11:28:51 +0300519 except (OSError, ValueError):
Franz Wöllertd2e902e2018-07-29 14:47:09 +0200520 return _abspath_fallback(path)
Guido van Rossum83eeef42001-09-17 15:16:09 +0000521
Miss Islington (bot)c30c8692019-08-21 14:09:33 -0700522try:
523 from nt import _getfinalpathname, readlink as _nt_readlink
524except ImportError:
525 # realpath is a no-op on systems without _getfinalpathname support.
526 realpath = abspath
527else:
528 def _readlink_deep(path, seen=None):
529 if seen is None:
530 seen = set()
531
532 while normcase(path) not in seen:
533 seen.add(normcase(path))
534 try:
535 path = _nt_readlink(path)
536 except OSError as ex:
537 # Stop on file (2) or directory (3) not found, or
538 # paths that are not reparse points (4390)
539 if ex.winerror in (2, 3, 4390):
540 break
541 raise
542 except ValueError:
543 # Stop on reparse points that are not symlinks
544 break
545 return path
546
547 def _getfinalpathname_nonstrict(path):
548 # Fast path to get the final path name. If this succeeds, there
549 # is no need to go any further.
550 try:
551 return _getfinalpathname(path)
552 except OSError:
553 pass
554
555 # Allow file (2) or directory (3) not found, invalid syntax (123),
556 # and symlinks that cannot be followed (1921)
557 allowed_winerror = 2, 3, 123, 1921
558
559 # Non-strict algorithm is to find as much of the target directory
560 # as we can and join the rest.
561 tail = ''
562 seen = set()
563 while path:
564 try:
565 path = _readlink_deep(path, seen)
566 path = _getfinalpathname(path)
567 return join(path, tail) if tail else path
568 except OSError as ex:
569 if ex.winerror not in allowed_winerror:
570 raise
571 path, name = split(path)
572 if path and not name:
573 return abspath(path + tail)
574 tail = join(name, tail) if tail else name
575 return abspath(tail)
576
577 def realpath(path):
578 path = os.fspath(path)
579 if isinstance(path, bytes):
580 prefix = b'\\\\?\\'
581 unc_prefix = b'\\\\?\\UNC\\'
582 new_unc_prefix = b'\\\\'
583 cwd = os.getcwdb()
584 else:
585 prefix = '\\\\?\\'
586 unc_prefix = '\\\\?\\UNC\\'
587 new_unc_prefix = '\\\\'
588 cwd = os.getcwd()
589 had_prefix = path.startswith(prefix)
590 path = _getfinalpathname_nonstrict(path)
591 # The path returned by _getfinalpathname will always start with \\?\ -
592 # strip off that prefix unless it was already provided on the original
593 # path.
594 if not had_prefix and path.startswith(prefix):
595 # For UNC paths, the prefix will actually be \\?\UNC\
596 # Handle that case as well.
597 if path.startswith(unc_prefix):
598 spath = new_unc_prefix + path[len(unc_prefix):]
599 else:
600 spath = path[len(prefix):]
601 # Ensure that the non-prefixed path resolves to the same path
602 try:
603 if _getfinalpathname(spath) == path:
604 path = spath
605 except OSError as ex:
606 pass
607 return path
608
609
Mark Hammond8696ebc2002-10-08 02:44:31 +0000610# Win9x family and earlier have no Unicode filename support.
Tim Peters26bc25a2002-10-09 07:56:04 +0000611supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
612 sys.getwindowsversion()[3] >= 2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000613
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300614def relpath(path, start=None):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000615 """Return a relative version of a path"""
Brett Cannon3f9183b2016-08-26 14:44:48 -0700616 path = os.fspath(path)
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300617 if isinstance(path, bytes):
618 sep = b'\\'
619 curdir = b'.'
620 pardir = b'..'
621 else:
622 sep = '\\'
623 curdir = '.'
624 pardir = '..'
Amaury Forgeot d'Arcc72ef8b2008-10-03 18:38:26 +0000625
Serhiy Storchaka8518b792014-07-23 20:43:13 +0300626 if start is None:
627 start = curdir
Guido van Rossumd8faa362007-04-27 19:54:29 +0000628
629 if not path:
630 raise ValueError("no path specified")
Mark Hammond5a607a32009-05-06 08:04:54 +0000631
Brett Cannon3f9183b2016-08-26 14:44:48 -0700632 start = os.fspath(start)
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300633 try:
634 start_abs = abspath(normpath(start))
635 path_abs = abspath(normpath(path))
636 start_drive, start_rest = splitdrive(start_abs)
637 path_drive, path_rest = splitdrive(path_abs)
638 if normcase(start_drive) != normcase(path_drive):
639 raise ValueError("path is on mount %r, start on mount %r" % (
640 path_drive, start_drive))
Mark Hammond5a607a32009-05-06 08:04:54 +0000641
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300642 start_list = [x for x in start_rest.split(sep) if x]
643 path_list = [x for x in path_rest.split(sep) if x]
644 # Work out how much of the filepath is shared by start and path.
645 i = 0
646 for e1, e2 in zip(start_list, path_list):
647 if normcase(e1) != normcase(e2):
648 break
649 i += 1
Guido van Rossumd8faa362007-04-27 19:54:29 +0000650
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300651 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
652 if not rel_list:
653 return curdir
654 return join(*rel_list)
Serhiy Storchakae4f47082014-10-04 16:09:02 +0300655 except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
Serhiy Storchaka3deeeb02014-10-04 14:58:43 +0300656 genericpath._check_arg_types('relpath', path, start)
657 raise
Brian Curtind40e6f72010-07-08 21:39:08 +0000658
659
Serhiy Storchaka38220932015-03-31 15:31:53 +0300660# Return the longest common sub-path of the sequence of paths given as input.
661# The function is case-insensitive and 'separator-insensitive', i.e. if the
662# only difference between two paths is the use of '\' versus '/' as separator,
663# they are deemed to be equal.
664#
665# However, the returned path will have the standard '\' separator (even if the
666# given paths had the alternative '/' separator) and will have the case of the
667# first path given in the sequence. Additionally, any trailing separator is
668# stripped from the returned path.
669
670def commonpath(paths):
671 """Given a sequence of path names, returns the longest common sub-path."""
672
673 if not paths:
674 raise ValueError('commonpath() arg is an empty sequence')
675
Brett Cannon3f9183b2016-08-26 14:44:48 -0700676 paths = tuple(map(os.fspath, paths))
Serhiy Storchaka38220932015-03-31 15:31:53 +0300677 if isinstance(paths[0], bytes):
678 sep = b'\\'
679 altsep = b'/'
680 curdir = b'.'
681 else:
682 sep = '\\'
683 altsep = '/'
684 curdir = '.'
685
686 try:
687 drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
688 split_paths = [p.split(sep) for d, p in drivesplits]
689
690 try:
691 isabs, = set(p[:1] == sep for d, p in drivesplits)
692 except ValueError:
693 raise ValueError("Can't mix absolute and relative paths") from None
694
695 # Check that all drive letters or UNC paths match. The check is made only
696 # now otherwise type errors for mixing strings and bytes would not be
697 # caught.
698 if len(set(d for d, p in drivesplits)) != 1:
699 raise ValueError("Paths don't have the same drive")
700
701 drive, path = splitdrive(paths[0].replace(altsep, sep))
702 common = path.split(sep)
703 common = [c for c in common if c and c != curdir]
704
705 split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
706 s1 = min(split_paths)
707 s2 = max(split_paths)
708 for i, c in enumerate(s1):
709 if c != s2[i]:
710 common = common[:i]
711 break
712 else:
713 common = common[:len(s1)]
714
715 prefix = drive + sep if isabs else drive
716 return prefix + sep.join(common)
717 except (TypeError, AttributeError):
718 genericpath._check_arg_types('commonpath', *paths)
719 raise
720
721
Brian Curtin9c669cc2011-06-08 18:17:18 -0500722try:
723 # The genericpath.isdir implementation uses os.stat and checks the mode
724 # attribute to tell whether or not the path is a directory.
725 # This is overkill on Windows - just pass the path to GetFileAttributes
726 # and check the attribute from there.
Brian Curtin95d028f2011-06-09 09:10:38 -0500727 from nt import _isdir as isdir
Brett Cannoncd171c82013-07-04 17:43:24 -0400728except ImportError:
Brian Curtin95d028f2011-06-09 09:10:38 -0500729 # Use genericpath.isdir as imported above.
730 pass