| 1 | n/a | # Module 'ntpath' -- common operations on WinNT/Win95 pathnames |
|---|
| 2 | n/a | """Common pathname manipulations, WindowsNT/95 version. |
|---|
| 3 | n/a | |
|---|
| 4 | n/a | Instead of importing this module directly, import os and refer to this |
|---|
| 5 | n/a | module as os.path. |
|---|
| 6 | n/a | """ |
|---|
| 7 | n/a | |
|---|
| 8 | n/a | import os |
|---|
| 9 | n/a | import sys |
|---|
| 10 | n/a | import stat |
|---|
| 11 | n/a | import genericpath |
|---|
| 12 | n/a | from genericpath import * |
|---|
| 13 | n/a | |
|---|
| 14 | n/a | __all__ = ["normcase","isabs","join","splitdrive","split","splitext", |
|---|
| 15 | n/a | "basename","dirname","commonprefix","getsize","getmtime", |
|---|
| 16 | n/a | "getatime","getctime", "islink","exists","lexists","isdir","isfile", |
|---|
| 17 | n/a | "ismount", "expanduser","expandvars","normpath","abspath", |
|---|
| 18 | n/a | "curdir","pardir","sep","pathsep","defpath","altsep", |
|---|
| 19 | n/a | "extsep","devnull","realpath","supports_unicode_filenames","relpath", |
|---|
| 20 | n/a | "samefile", "sameopenfile", "samestat", "commonpath"] |
|---|
| 21 | n/a | |
|---|
| 22 | n/a | # strings representing various path-related bits and pieces |
|---|
| 23 | n/a | # These are primarily for export; internally, they are hardcoded. |
|---|
| 24 | n/a | curdir = '.' |
|---|
| 25 | n/a | pardir = '..' |
|---|
| 26 | n/a | extsep = '.' |
|---|
| 27 | n/a | sep = '\\' |
|---|
| 28 | n/a | pathsep = ';' |
|---|
| 29 | n/a | altsep = '/' |
|---|
| 30 | n/a | defpath = '.;C:\\bin' |
|---|
| 31 | n/a | devnull = 'nul' |
|---|
| 32 | n/a | |
|---|
| 33 | n/a | def _get_bothseps(path): |
|---|
| 34 | n/a | if isinstance(path, bytes): |
|---|
| 35 | n/a | return b'\\/' |
|---|
| 36 | n/a | else: |
|---|
| 37 | n/a | return '\\/' |
|---|
| 38 | n/a | |
|---|
| 39 | n/a | # Normalize the case of a pathname and map slashes to backslashes. |
|---|
| 40 | n/a | # Other normalizations (such as optimizing '../' away) are not done |
|---|
| 41 | n/a | # (this is done by normpath). |
|---|
| 42 | n/a | |
|---|
| 43 | n/a | def normcase(s): |
|---|
| 44 | n/a | """Normalize case of pathname. |
|---|
| 45 | n/a | |
|---|
| 46 | n/a | Makes all characters lowercase and all slashes into backslashes.""" |
|---|
| 47 | n/a | s = os.fspath(s) |
|---|
| 48 | n/a | try: |
|---|
| 49 | n/a | if isinstance(s, bytes): |
|---|
| 50 | n/a | return s.replace(b'/', b'\\').lower() |
|---|
| 51 | n/a | else: |
|---|
| 52 | n/a | return s.replace('/', '\\').lower() |
|---|
| 53 | n/a | except (TypeError, AttributeError): |
|---|
| 54 | n/a | if not isinstance(s, (bytes, str)): |
|---|
| 55 | n/a | raise TypeError("normcase() argument must be str or bytes, " |
|---|
| 56 | n/a | "not %r" % s.__class__.__name__) from None |
|---|
| 57 | n/a | raise |
|---|
| 58 | n/a | |
|---|
| 59 | n/a | |
|---|
| 60 | n/a | # Return whether a path is absolute. |
|---|
| 61 | n/a | # Trivial in Posix, harder on Windows. |
|---|
| 62 | n/a | # For Windows it is absolute if it starts with a slash or backslash (current |
|---|
| 63 | n/a | # volume), or if a pathname after the volume-letter-and-colon or UNC-resource |
|---|
| 64 | n/a | # starts with a slash or backslash. |
|---|
| 65 | n/a | |
|---|
| 66 | n/a | def isabs(s): |
|---|
| 67 | n/a | """Test whether a path is absolute""" |
|---|
| 68 | n/a | s = os.fspath(s) |
|---|
| 69 | n/a | s = splitdrive(s)[1] |
|---|
| 70 | n/a | return len(s) > 0 and s[0] in _get_bothseps(s) |
|---|
| 71 | n/a | |
|---|
| 72 | n/a | |
|---|
| 73 | n/a | # Join two (or more) paths. |
|---|
| 74 | n/a | def join(path, *paths): |
|---|
| 75 | n/a | path = os.fspath(path) |
|---|
| 76 | n/a | if isinstance(path, bytes): |
|---|
| 77 | n/a | sep = b'\\' |
|---|
| 78 | n/a | seps = b'\\/' |
|---|
| 79 | n/a | colon = b':' |
|---|
| 80 | n/a | else: |
|---|
| 81 | n/a | sep = '\\' |
|---|
| 82 | n/a | seps = '\\/' |
|---|
| 83 | n/a | colon = ':' |
|---|
| 84 | n/a | try: |
|---|
| 85 | n/a | if not paths: |
|---|
| 86 | n/a | path[:0] + sep #23780: Ensure compatible data type even if p is null. |
|---|
| 87 | n/a | result_drive, result_path = splitdrive(path) |
|---|
| 88 | n/a | for p in map(os.fspath, paths): |
|---|
| 89 | n/a | p_drive, p_path = splitdrive(p) |
|---|
| 90 | n/a | if p_path and p_path[0] in seps: |
|---|
| 91 | n/a | # Second path is absolute |
|---|
| 92 | n/a | if p_drive or not result_drive: |
|---|
| 93 | n/a | result_drive = p_drive |
|---|
| 94 | n/a | result_path = p_path |
|---|
| 95 | n/a | continue |
|---|
| 96 | n/a | elif p_drive and p_drive != result_drive: |
|---|
| 97 | n/a | if p_drive.lower() != result_drive.lower(): |
|---|
| 98 | n/a | # Different drives => ignore the first path entirely |
|---|
| 99 | n/a | result_drive = p_drive |
|---|
| 100 | n/a | result_path = p_path |
|---|
| 101 | n/a | continue |
|---|
| 102 | n/a | # Same drive in different case |
|---|
| 103 | n/a | result_drive = p_drive |
|---|
| 104 | n/a | # Second path is relative to the first |
|---|
| 105 | n/a | if result_path and result_path[-1] not in seps: |
|---|
| 106 | n/a | result_path = result_path + sep |
|---|
| 107 | n/a | result_path = result_path + p_path |
|---|
| 108 | n/a | ## add separator between UNC and non-absolute path |
|---|
| 109 | n/a | if (result_path and result_path[0] not in seps and |
|---|
| 110 | n/a | result_drive and result_drive[-1:] != colon): |
|---|
| 111 | n/a | return result_drive + sep + result_path |
|---|
| 112 | n/a | return result_drive + result_path |
|---|
| 113 | n/a | except (TypeError, AttributeError, BytesWarning): |
|---|
| 114 | n/a | genericpath._check_arg_types('join', path, *paths) |
|---|
| 115 | n/a | raise |
|---|
| 116 | n/a | |
|---|
| 117 | n/a | |
|---|
| 118 | n/a | # Split a path in a drive specification (a drive letter followed by a |
|---|
| 119 | n/a | # colon) and the path specification. |
|---|
| 120 | n/a | # It is always true that drivespec + pathspec == p |
|---|
| 121 | n/a | def splitdrive(p): |
|---|
| 122 | n/a | """Split a pathname into drive/UNC sharepoint and relative path specifiers. |
|---|
| 123 | n/a | Returns a 2-tuple (drive_or_unc, path); either part may be empty. |
|---|
| 124 | n/a | |
|---|
| 125 | n/a | If you assign |
|---|
| 126 | n/a | result = splitdrive(p) |
|---|
| 127 | n/a | It is always true that: |
|---|
| 128 | n/a | result[0] + result[1] == p |
|---|
| 129 | n/a | |
|---|
| 130 | n/a | If the path contained a drive letter, drive_or_unc will contain everything |
|---|
| 131 | n/a | up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir") |
|---|
| 132 | n/a | |
|---|
| 133 | n/a | If the path contained a UNC path, the drive_or_unc will contain the host name |
|---|
| 134 | n/a | and share up to but not including the fourth directory separator character. |
|---|
| 135 | n/a | e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir") |
|---|
| 136 | n/a | |
|---|
| 137 | n/a | Paths cannot contain both a drive letter and a UNC path. |
|---|
| 138 | n/a | |
|---|
| 139 | n/a | """ |
|---|
| 140 | n/a | p = os.fspath(p) |
|---|
| 141 | n/a | if len(p) >= 2: |
|---|
| 142 | n/a | if isinstance(p, bytes): |
|---|
| 143 | n/a | sep = b'\\' |
|---|
| 144 | n/a | altsep = b'/' |
|---|
| 145 | n/a | colon = b':' |
|---|
| 146 | n/a | else: |
|---|
| 147 | n/a | sep = '\\' |
|---|
| 148 | n/a | altsep = '/' |
|---|
| 149 | n/a | colon = ':' |
|---|
| 150 | n/a | normp = p.replace(altsep, sep) |
|---|
| 151 | n/a | if (normp[0:2] == sep*2) and (normp[2:3] != sep): |
|---|
| 152 | n/a | # is a UNC path: |
|---|
| 153 | n/a | # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path |
|---|
| 154 | n/a | # \\machine\mountpoint\directory\etc\... |
|---|
| 155 | n/a | # directory ^^^^^^^^^^^^^^^ |
|---|
| 156 | n/a | index = normp.find(sep, 2) |
|---|
| 157 | n/a | if index == -1: |
|---|
| 158 | n/a | return p[:0], p |
|---|
| 159 | n/a | index2 = normp.find(sep, index + 1) |
|---|
| 160 | n/a | # a UNC path can't have two slashes in a row |
|---|
| 161 | n/a | # (after the initial two) |
|---|
| 162 | n/a | if index2 == index + 1: |
|---|
| 163 | n/a | return p[:0], p |
|---|
| 164 | n/a | if index2 == -1: |
|---|
| 165 | n/a | index2 = len(p) |
|---|
| 166 | n/a | return p[:index2], p[index2:] |
|---|
| 167 | n/a | if normp[1:2] == colon: |
|---|
| 168 | n/a | return p[:2], p[2:] |
|---|
| 169 | n/a | return p[:0], p |
|---|
| 170 | n/a | |
|---|
| 171 | n/a | |
|---|
| 172 | n/a | # Split a path in head (everything up to the last '/') and tail (the |
|---|
| 173 | n/a | # rest). After the trailing '/' is stripped, the invariant |
|---|
| 174 | n/a | # join(head, tail) == p holds. |
|---|
| 175 | n/a | # The resulting head won't end in '/' unless it is the root. |
|---|
| 176 | n/a | |
|---|
| 177 | n/a | def split(p): |
|---|
| 178 | n/a | """Split a pathname. |
|---|
| 179 | n/a | |
|---|
| 180 | n/a | Return tuple (head, tail) where tail is everything after the final slash. |
|---|
| 181 | n/a | Either part may be empty.""" |
|---|
| 182 | n/a | p = os.fspath(p) |
|---|
| 183 | n/a | seps = _get_bothseps(p) |
|---|
| 184 | n/a | d, p = splitdrive(p) |
|---|
| 185 | n/a | # set i to index beyond p's last slash |
|---|
| 186 | n/a | i = len(p) |
|---|
| 187 | n/a | while i and p[i-1] not in seps: |
|---|
| 188 | n/a | i -= 1 |
|---|
| 189 | n/a | head, tail = p[:i], p[i:] # now tail has no slashes |
|---|
| 190 | n/a | # remove trailing slashes from head, unless it's all slashes |
|---|
| 191 | n/a | head = head.rstrip(seps) or head |
|---|
| 192 | n/a | return d + head, tail |
|---|
| 193 | n/a | |
|---|
| 194 | n/a | |
|---|
| 195 | n/a | # Split a path in root and extension. |
|---|
| 196 | n/a | # The extension is everything starting at the last dot in the last |
|---|
| 197 | n/a | # pathname component; the root is everything before that. |
|---|
| 198 | n/a | # It is always true that root + ext == p. |
|---|
| 199 | n/a | |
|---|
| 200 | n/a | def splitext(p): |
|---|
| 201 | n/a | p = os.fspath(p) |
|---|
| 202 | n/a | if isinstance(p, bytes): |
|---|
| 203 | n/a | return genericpath._splitext(p, b'\\', b'/', b'.') |
|---|
| 204 | n/a | else: |
|---|
| 205 | n/a | return genericpath._splitext(p, '\\', '/', '.') |
|---|
| 206 | n/a | splitext.__doc__ = genericpath._splitext.__doc__ |
|---|
| 207 | n/a | |
|---|
| 208 | n/a | |
|---|
| 209 | n/a | # Return the tail (basename) part of a path. |
|---|
| 210 | n/a | |
|---|
| 211 | n/a | def basename(p): |
|---|
| 212 | n/a | """Returns the final component of a pathname""" |
|---|
| 213 | n/a | return split(p)[1] |
|---|
| 214 | n/a | |
|---|
| 215 | n/a | |
|---|
| 216 | n/a | # Return the head (dirname) part of a path. |
|---|
| 217 | n/a | |
|---|
| 218 | n/a | def dirname(p): |
|---|
| 219 | n/a | """Returns the directory component of a pathname""" |
|---|
| 220 | n/a | return split(p)[0] |
|---|
| 221 | n/a | |
|---|
| 222 | n/a | # Is a path a symbolic link? |
|---|
| 223 | n/a | # This will always return false on systems where os.lstat doesn't exist. |
|---|
| 224 | n/a | |
|---|
| 225 | n/a | def islink(path): |
|---|
| 226 | n/a | """Test whether a path is a symbolic link. |
|---|
| 227 | n/a | This will always return false for Windows prior to 6.0. |
|---|
| 228 | n/a | """ |
|---|
| 229 | n/a | try: |
|---|
| 230 | n/a | st = os.lstat(path) |
|---|
| 231 | n/a | except (OSError, AttributeError): |
|---|
| 232 | n/a | return False |
|---|
| 233 | n/a | return stat.S_ISLNK(st.st_mode) |
|---|
| 234 | n/a | |
|---|
| 235 | n/a | # Being true for dangling symbolic links is also useful. |
|---|
| 236 | n/a | |
|---|
| 237 | n/a | def lexists(path): |
|---|
| 238 | n/a | """Test whether a path exists. Returns True for broken symbolic links""" |
|---|
| 239 | n/a | try: |
|---|
| 240 | n/a | st = os.lstat(path) |
|---|
| 241 | n/a | except OSError: |
|---|
| 242 | n/a | return False |
|---|
| 243 | n/a | return True |
|---|
| 244 | n/a | |
|---|
| 245 | n/a | # Is a path a mount point? |
|---|
| 246 | n/a | # Any drive letter root (eg c:\) |
|---|
| 247 | n/a | # Any share UNC (eg \\server\share) |
|---|
| 248 | n/a | # Any volume mounted on a filesystem folder |
|---|
| 249 | n/a | # |
|---|
| 250 | n/a | # No one method detects all three situations. Historically we've lexically |
|---|
| 251 | n/a | # detected drive letter roots and share UNCs. The canonical approach to |
|---|
| 252 | n/a | # detecting mounted volumes (querying the reparse tag) fails for the most |
|---|
| 253 | n/a | # common case: drive letter roots. The alternative which uses GetVolumePathName |
|---|
| 254 | n/a | # fails if the drive letter is the result of a SUBST. |
|---|
| 255 | n/a | try: |
|---|
| 256 | n/a | from nt import _getvolumepathname |
|---|
| 257 | n/a | except ImportError: |
|---|
| 258 | n/a | _getvolumepathname = None |
|---|
| 259 | n/a | def ismount(path): |
|---|
| 260 | n/a | """Test whether a path is a mount point (a drive root, the root of a |
|---|
| 261 | n/a | share, or a mounted volume)""" |
|---|
| 262 | n/a | path = os.fspath(path) |
|---|
| 263 | n/a | seps = _get_bothseps(path) |
|---|
| 264 | n/a | path = abspath(path) |
|---|
| 265 | n/a | root, rest = splitdrive(path) |
|---|
| 266 | n/a | if root and root[0] in seps: |
|---|
| 267 | n/a | return (not rest) or (rest in seps) |
|---|
| 268 | n/a | if rest in seps: |
|---|
| 269 | n/a | return True |
|---|
| 270 | n/a | |
|---|
| 271 | n/a | if _getvolumepathname: |
|---|
| 272 | n/a | return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps) |
|---|
| 273 | n/a | else: |
|---|
| 274 | n/a | return False |
|---|
| 275 | n/a | |
|---|
| 276 | n/a | |
|---|
| 277 | n/a | # Expand paths beginning with '~' or '~user'. |
|---|
| 278 | n/a | # '~' means $HOME; '~user' means that user's home directory. |
|---|
| 279 | n/a | # If the path doesn't begin with '~', or if the user or $HOME is unknown, |
|---|
| 280 | n/a | # the path is returned unchanged (leaving error reporting to whatever |
|---|
| 281 | n/a | # function is called with the expanded path as argument). |
|---|
| 282 | n/a | # See also module 'glob' for expansion of *, ? and [...] in pathnames. |
|---|
| 283 | n/a | # (A function should also be defined to do full *sh-style environment |
|---|
| 284 | n/a | # variable expansion.) |
|---|
| 285 | n/a | |
|---|
| 286 | n/a | def expanduser(path): |
|---|
| 287 | n/a | """Expand ~ and ~user constructs. |
|---|
| 288 | n/a | |
|---|
| 289 | n/a | If user or $HOME is unknown, do nothing.""" |
|---|
| 290 | n/a | path = os.fspath(path) |
|---|
| 291 | n/a | if isinstance(path, bytes): |
|---|
| 292 | n/a | tilde = b'~' |
|---|
| 293 | n/a | else: |
|---|
| 294 | n/a | tilde = '~' |
|---|
| 295 | n/a | if not path.startswith(tilde): |
|---|
| 296 | n/a | return path |
|---|
| 297 | n/a | i, n = 1, len(path) |
|---|
| 298 | n/a | while i < n and path[i] not in _get_bothseps(path): |
|---|
| 299 | n/a | i += 1 |
|---|
| 300 | n/a | |
|---|
| 301 | n/a | if 'HOME' in os.environ: |
|---|
| 302 | n/a | userhome = os.environ['HOME'] |
|---|
| 303 | n/a | elif 'USERPROFILE' in os.environ: |
|---|
| 304 | n/a | userhome = os.environ['USERPROFILE'] |
|---|
| 305 | n/a | elif not 'HOMEPATH' in os.environ: |
|---|
| 306 | n/a | return path |
|---|
| 307 | n/a | else: |
|---|
| 308 | n/a | try: |
|---|
| 309 | n/a | drive = os.environ['HOMEDRIVE'] |
|---|
| 310 | n/a | except KeyError: |
|---|
| 311 | n/a | drive = '' |
|---|
| 312 | n/a | userhome = join(drive, os.environ['HOMEPATH']) |
|---|
| 313 | n/a | |
|---|
| 314 | n/a | if isinstance(path, bytes): |
|---|
| 315 | n/a | userhome = os.fsencode(userhome) |
|---|
| 316 | n/a | |
|---|
| 317 | n/a | if i != 1: #~user |
|---|
| 318 | n/a | userhome = join(dirname(userhome), path[1:i]) |
|---|
| 319 | n/a | |
|---|
| 320 | n/a | return userhome + path[i:] |
|---|
| 321 | n/a | |
|---|
| 322 | n/a | |
|---|
| 323 | n/a | # Expand paths containing shell variable substitutions. |
|---|
| 324 | n/a | # The following rules apply: |
|---|
| 325 | n/a | # - no expansion within single quotes |
|---|
| 326 | n/a | # - '$$' is translated into '$' |
|---|
| 327 | n/a | # - '%%' is translated into '%' if '%%' are not seen in %var1%%var2% |
|---|
| 328 | n/a | # - ${varname} is accepted. |
|---|
| 329 | n/a | # - $varname is accepted. |
|---|
| 330 | n/a | # - %varname% is accepted. |
|---|
| 331 | n/a | # - varnames can be made out of letters, digits and the characters '_-' |
|---|
| 332 | n/a | # (though is not verified in the ${varname} and %varname% cases) |
|---|
| 333 | n/a | # XXX With COMMAND.COM you can use any characters in a variable name, |
|---|
| 334 | n/a | # XXX except '^|<>='. |
|---|
| 335 | n/a | |
|---|
| 336 | n/a | def expandvars(path): |
|---|
| 337 | n/a | """Expand shell variables of the forms $var, ${var} and %var%. |
|---|
| 338 | n/a | |
|---|
| 339 | n/a | Unknown variables are left unchanged.""" |
|---|
| 340 | n/a | path = os.fspath(path) |
|---|
| 341 | n/a | if isinstance(path, bytes): |
|---|
| 342 | n/a | if b'$' not in path and b'%' not in path: |
|---|
| 343 | n/a | return path |
|---|
| 344 | n/a | import string |
|---|
| 345 | n/a | varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii') |
|---|
| 346 | n/a | quote = b'\'' |
|---|
| 347 | n/a | percent = b'%' |
|---|
| 348 | n/a | brace = b'{' |
|---|
| 349 | n/a | rbrace = b'}' |
|---|
| 350 | n/a | dollar = b'$' |
|---|
| 351 | n/a | environ = getattr(os, 'environb', None) |
|---|
| 352 | n/a | else: |
|---|
| 353 | n/a | if '$' not in path and '%' not in path: |
|---|
| 354 | n/a | return path |
|---|
| 355 | n/a | import string |
|---|
| 356 | n/a | varchars = string.ascii_letters + string.digits + '_-' |
|---|
| 357 | n/a | quote = '\'' |
|---|
| 358 | n/a | percent = '%' |
|---|
| 359 | n/a | brace = '{' |
|---|
| 360 | n/a | rbrace = '}' |
|---|
| 361 | n/a | dollar = '$' |
|---|
| 362 | n/a | environ = os.environ |
|---|
| 363 | n/a | res = path[:0] |
|---|
| 364 | n/a | index = 0 |
|---|
| 365 | n/a | pathlen = len(path) |
|---|
| 366 | n/a | while index < pathlen: |
|---|
| 367 | n/a | c = path[index:index+1] |
|---|
| 368 | n/a | if c == quote: # no expansion within single quotes |
|---|
| 369 | n/a | path = path[index + 1:] |
|---|
| 370 | n/a | pathlen = len(path) |
|---|
| 371 | n/a | try: |
|---|
| 372 | n/a | index = path.index(c) |
|---|
| 373 | n/a | res += c + path[:index + 1] |
|---|
| 374 | n/a | except ValueError: |
|---|
| 375 | n/a | res += c + path |
|---|
| 376 | n/a | index = pathlen - 1 |
|---|
| 377 | n/a | elif c == percent: # variable or '%' |
|---|
| 378 | n/a | if path[index + 1:index + 2] == percent: |
|---|
| 379 | n/a | res += c |
|---|
| 380 | n/a | index += 1 |
|---|
| 381 | n/a | else: |
|---|
| 382 | n/a | path = path[index+1:] |
|---|
| 383 | n/a | pathlen = len(path) |
|---|
| 384 | n/a | try: |
|---|
| 385 | n/a | index = path.index(percent) |
|---|
| 386 | n/a | except ValueError: |
|---|
| 387 | n/a | res += percent + path |
|---|
| 388 | n/a | index = pathlen - 1 |
|---|
| 389 | n/a | else: |
|---|
| 390 | n/a | var = path[:index] |
|---|
| 391 | n/a | try: |
|---|
| 392 | n/a | if environ is None: |
|---|
| 393 | n/a | value = os.fsencode(os.environ[os.fsdecode(var)]) |
|---|
| 394 | n/a | else: |
|---|
| 395 | n/a | value = environ[var] |
|---|
| 396 | n/a | except KeyError: |
|---|
| 397 | n/a | value = percent + var + percent |
|---|
| 398 | n/a | res += value |
|---|
| 399 | n/a | elif c == dollar: # variable or '$$' |
|---|
| 400 | n/a | if path[index + 1:index + 2] == dollar: |
|---|
| 401 | n/a | res += c |
|---|
| 402 | n/a | index += 1 |
|---|
| 403 | n/a | elif path[index + 1:index + 2] == brace: |
|---|
| 404 | n/a | path = path[index+2:] |
|---|
| 405 | n/a | pathlen = len(path) |
|---|
| 406 | n/a | try: |
|---|
| 407 | n/a | index = path.index(rbrace) |
|---|
| 408 | n/a | except ValueError: |
|---|
| 409 | n/a | res += dollar + brace + path |
|---|
| 410 | n/a | index = pathlen - 1 |
|---|
| 411 | n/a | else: |
|---|
| 412 | n/a | var = path[:index] |
|---|
| 413 | n/a | try: |
|---|
| 414 | n/a | if environ is None: |
|---|
| 415 | n/a | value = os.fsencode(os.environ[os.fsdecode(var)]) |
|---|
| 416 | n/a | else: |
|---|
| 417 | n/a | value = environ[var] |
|---|
| 418 | n/a | except KeyError: |
|---|
| 419 | n/a | value = dollar + brace + var + rbrace |
|---|
| 420 | n/a | res += value |
|---|
| 421 | n/a | else: |
|---|
| 422 | n/a | var = path[:0] |
|---|
| 423 | n/a | index += 1 |
|---|
| 424 | n/a | c = path[index:index + 1] |
|---|
| 425 | n/a | while c and c in varchars: |
|---|
| 426 | n/a | var += c |
|---|
| 427 | n/a | index += 1 |
|---|
| 428 | n/a | c = path[index:index + 1] |
|---|
| 429 | n/a | try: |
|---|
| 430 | n/a | if environ is None: |
|---|
| 431 | n/a | value = os.fsencode(os.environ[os.fsdecode(var)]) |
|---|
| 432 | n/a | else: |
|---|
| 433 | n/a | value = environ[var] |
|---|
| 434 | n/a | except KeyError: |
|---|
| 435 | n/a | value = dollar + var |
|---|
| 436 | n/a | res += value |
|---|
| 437 | n/a | if c: |
|---|
| 438 | n/a | index -= 1 |
|---|
| 439 | n/a | else: |
|---|
| 440 | n/a | res += c |
|---|
| 441 | n/a | index += 1 |
|---|
| 442 | n/a | return res |
|---|
| 443 | n/a | |
|---|
| 444 | n/a | |
|---|
| 445 | n/a | # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. |
|---|
| 446 | n/a | # Previously, this function also truncated pathnames to 8+3 format, |
|---|
| 447 | n/a | # but as this module is called "ntpath", that's obviously wrong! |
|---|
| 448 | n/a | |
|---|
| 449 | n/a | def normpath(path): |
|---|
| 450 | n/a | """Normalize path, eliminating double slashes, etc.""" |
|---|
| 451 | n/a | path = os.fspath(path) |
|---|
| 452 | n/a | if isinstance(path, bytes): |
|---|
| 453 | n/a | sep = b'\\' |
|---|
| 454 | n/a | altsep = b'/' |
|---|
| 455 | n/a | curdir = b'.' |
|---|
| 456 | n/a | pardir = b'..' |
|---|
| 457 | n/a | special_prefixes = (b'\\\\.\\', b'\\\\?\\') |
|---|
| 458 | n/a | else: |
|---|
| 459 | n/a | sep = '\\' |
|---|
| 460 | n/a | altsep = '/' |
|---|
| 461 | n/a | curdir = '.' |
|---|
| 462 | n/a | pardir = '..' |
|---|
| 463 | n/a | special_prefixes = ('\\\\.\\', '\\\\?\\') |
|---|
| 464 | n/a | if path.startswith(special_prefixes): |
|---|
| 465 | n/a | # in the case of paths with these prefixes: |
|---|
| 466 | n/a | # \\.\ -> device names |
|---|
| 467 | n/a | # \\?\ -> literal paths |
|---|
| 468 | n/a | # do not do any normalization, but return the path unchanged |
|---|
| 469 | n/a | return path |
|---|
| 470 | n/a | path = path.replace(altsep, sep) |
|---|
| 471 | n/a | prefix, path = splitdrive(path) |
|---|
| 472 | n/a | |
|---|
| 473 | n/a | # collapse initial backslashes |
|---|
| 474 | n/a | if path.startswith(sep): |
|---|
| 475 | n/a | prefix += sep |
|---|
| 476 | n/a | path = path.lstrip(sep) |
|---|
| 477 | n/a | |
|---|
| 478 | n/a | comps = path.split(sep) |
|---|
| 479 | n/a | i = 0 |
|---|
| 480 | n/a | while i < len(comps): |
|---|
| 481 | n/a | if not comps[i] or comps[i] == curdir: |
|---|
| 482 | n/a | del comps[i] |
|---|
| 483 | n/a | elif comps[i] == pardir: |
|---|
| 484 | n/a | if i > 0 and comps[i-1] != pardir: |
|---|
| 485 | n/a | del comps[i-1:i+1] |
|---|
| 486 | n/a | i -= 1 |
|---|
| 487 | n/a | elif i == 0 and prefix.endswith(sep): |
|---|
| 488 | n/a | del comps[i] |
|---|
| 489 | n/a | else: |
|---|
| 490 | n/a | i += 1 |
|---|
| 491 | n/a | else: |
|---|
| 492 | n/a | i += 1 |
|---|
| 493 | n/a | # If the path is now empty, substitute '.' |
|---|
| 494 | n/a | if not prefix and not comps: |
|---|
| 495 | n/a | comps.append(curdir) |
|---|
| 496 | n/a | return prefix + sep.join(comps) |
|---|
| 497 | n/a | |
|---|
| 498 | n/a | |
|---|
| 499 | n/a | # Return an absolute path. |
|---|
| 500 | n/a | try: |
|---|
| 501 | n/a | from nt import _getfullpathname |
|---|
| 502 | n/a | |
|---|
| 503 | n/a | except ImportError: # not running on Windows - mock up something sensible |
|---|
| 504 | n/a | def abspath(path): |
|---|
| 505 | n/a | """Return the absolute version of a path.""" |
|---|
| 506 | n/a | path = os.fspath(path) |
|---|
| 507 | n/a | if not isabs(path): |
|---|
| 508 | n/a | if isinstance(path, bytes): |
|---|
| 509 | n/a | cwd = os.getcwdb() |
|---|
| 510 | n/a | else: |
|---|
| 511 | n/a | cwd = os.getcwd() |
|---|
| 512 | n/a | path = join(cwd, path) |
|---|
| 513 | n/a | return normpath(path) |
|---|
| 514 | n/a | |
|---|
| 515 | n/a | else: # use native Windows method on Windows |
|---|
| 516 | n/a | def abspath(path): |
|---|
| 517 | n/a | """Return the absolute version of a path.""" |
|---|
| 518 | n/a | |
|---|
| 519 | n/a | if path: # Empty path must return current working directory. |
|---|
| 520 | n/a | path = os.fspath(path) |
|---|
| 521 | n/a | try: |
|---|
| 522 | n/a | path = _getfullpathname(path) |
|---|
| 523 | n/a | except OSError: |
|---|
| 524 | n/a | pass # Bad path - return unchanged. |
|---|
| 525 | n/a | elif isinstance(path, bytes): |
|---|
| 526 | n/a | path = os.getcwdb() |
|---|
| 527 | n/a | else: |
|---|
| 528 | n/a | path = os.getcwd() |
|---|
| 529 | n/a | return normpath(path) |
|---|
| 530 | n/a | |
|---|
| 531 | n/a | # realpath is a no-op on systems without islink support |
|---|
| 532 | n/a | realpath = abspath |
|---|
| 533 | n/a | # Win9x family and earlier have no Unicode filename support. |
|---|
| 534 | n/a | supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and |
|---|
| 535 | n/a | sys.getwindowsversion()[3] >= 2) |
|---|
| 536 | n/a | |
|---|
| 537 | n/a | def relpath(path, start=None): |
|---|
| 538 | n/a | """Return a relative version of a path""" |
|---|
| 539 | n/a | path = os.fspath(path) |
|---|
| 540 | n/a | if isinstance(path, bytes): |
|---|
| 541 | n/a | sep = b'\\' |
|---|
| 542 | n/a | curdir = b'.' |
|---|
| 543 | n/a | pardir = b'..' |
|---|
| 544 | n/a | else: |
|---|
| 545 | n/a | sep = '\\' |
|---|
| 546 | n/a | curdir = '.' |
|---|
| 547 | n/a | pardir = '..' |
|---|
| 548 | n/a | |
|---|
| 549 | n/a | if start is None: |
|---|
| 550 | n/a | start = curdir |
|---|
| 551 | n/a | |
|---|
| 552 | n/a | if not path: |
|---|
| 553 | n/a | raise ValueError("no path specified") |
|---|
| 554 | n/a | |
|---|
| 555 | n/a | start = os.fspath(start) |
|---|
| 556 | n/a | try: |
|---|
| 557 | n/a | start_abs = abspath(normpath(start)) |
|---|
| 558 | n/a | path_abs = abspath(normpath(path)) |
|---|
| 559 | n/a | start_drive, start_rest = splitdrive(start_abs) |
|---|
| 560 | n/a | path_drive, path_rest = splitdrive(path_abs) |
|---|
| 561 | n/a | if normcase(start_drive) != normcase(path_drive): |
|---|
| 562 | n/a | raise ValueError("path is on mount %r, start on mount %r" % ( |
|---|
| 563 | n/a | path_drive, start_drive)) |
|---|
| 564 | n/a | |
|---|
| 565 | n/a | start_list = [x for x in start_rest.split(sep) if x] |
|---|
| 566 | n/a | path_list = [x for x in path_rest.split(sep) if x] |
|---|
| 567 | n/a | # Work out how much of the filepath is shared by start and path. |
|---|
| 568 | n/a | i = 0 |
|---|
| 569 | n/a | for e1, e2 in zip(start_list, path_list): |
|---|
| 570 | n/a | if normcase(e1) != normcase(e2): |
|---|
| 571 | n/a | break |
|---|
| 572 | n/a | i += 1 |
|---|
| 573 | n/a | |
|---|
| 574 | n/a | rel_list = [pardir] * (len(start_list)-i) + path_list[i:] |
|---|
| 575 | n/a | if not rel_list: |
|---|
| 576 | n/a | return curdir |
|---|
| 577 | n/a | return join(*rel_list) |
|---|
| 578 | n/a | except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning): |
|---|
| 579 | n/a | genericpath._check_arg_types('relpath', path, start) |
|---|
| 580 | n/a | raise |
|---|
| 581 | n/a | |
|---|
| 582 | n/a | |
|---|
| 583 | n/a | # Return the longest common sub-path of the sequence of paths given as input. |
|---|
| 584 | n/a | # The function is case-insensitive and 'separator-insensitive', i.e. if the |
|---|
| 585 | n/a | # only difference between two paths is the use of '\' versus '/' as separator, |
|---|
| 586 | n/a | # they are deemed to be equal. |
|---|
| 587 | n/a | # |
|---|
| 588 | n/a | # However, the returned path will have the standard '\' separator (even if the |
|---|
| 589 | n/a | # given paths had the alternative '/' separator) and will have the case of the |
|---|
| 590 | n/a | # first path given in the sequence. Additionally, any trailing separator is |
|---|
| 591 | n/a | # stripped from the returned path. |
|---|
| 592 | n/a | |
|---|
| 593 | n/a | def commonpath(paths): |
|---|
| 594 | n/a | """Given a sequence of path names, returns the longest common sub-path.""" |
|---|
| 595 | n/a | |
|---|
| 596 | n/a | if not paths: |
|---|
| 597 | n/a | raise ValueError('commonpath() arg is an empty sequence') |
|---|
| 598 | n/a | |
|---|
| 599 | n/a | paths = tuple(map(os.fspath, paths)) |
|---|
| 600 | n/a | if isinstance(paths[0], bytes): |
|---|
| 601 | n/a | sep = b'\\' |
|---|
| 602 | n/a | altsep = b'/' |
|---|
| 603 | n/a | curdir = b'.' |
|---|
| 604 | n/a | else: |
|---|
| 605 | n/a | sep = '\\' |
|---|
| 606 | n/a | altsep = '/' |
|---|
| 607 | n/a | curdir = '.' |
|---|
| 608 | n/a | |
|---|
| 609 | n/a | try: |
|---|
| 610 | n/a | drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths] |
|---|
| 611 | n/a | split_paths = [p.split(sep) for d, p in drivesplits] |
|---|
| 612 | n/a | |
|---|
| 613 | n/a | try: |
|---|
| 614 | n/a | isabs, = set(p[:1] == sep for d, p in drivesplits) |
|---|
| 615 | n/a | except ValueError: |
|---|
| 616 | n/a | raise ValueError("Can't mix absolute and relative paths") from None |
|---|
| 617 | n/a | |
|---|
| 618 | n/a | # Check that all drive letters or UNC paths match. The check is made only |
|---|
| 619 | n/a | # now otherwise type errors for mixing strings and bytes would not be |
|---|
| 620 | n/a | # caught. |
|---|
| 621 | n/a | if len(set(d for d, p in drivesplits)) != 1: |
|---|
| 622 | n/a | raise ValueError("Paths don't have the same drive") |
|---|
| 623 | n/a | |
|---|
| 624 | n/a | drive, path = splitdrive(paths[0].replace(altsep, sep)) |
|---|
| 625 | n/a | common = path.split(sep) |
|---|
| 626 | n/a | common = [c for c in common if c and c != curdir] |
|---|
| 627 | n/a | |
|---|
| 628 | n/a | split_paths = [[c for c in s if c and c != curdir] for s in split_paths] |
|---|
| 629 | n/a | s1 = min(split_paths) |
|---|
| 630 | n/a | s2 = max(split_paths) |
|---|
| 631 | n/a | for i, c in enumerate(s1): |
|---|
| 632 | n/a | if c != s2[i]: |
|---|
| 633 | n/a | common = common[:i] |
|---|
| 634 | n/a | break |
|---|
| 635 | n/a | else: |
|---|
| 636 | n/a | common = common[:len(s1)] |
|---|
| 637 | n/a | |
|---|
| 638 | n/a | prefix = drive + sep if isabs else drive |
|---|
| 639 | n/a | return prefix + sep.join(common) |
|---|
| 640 | n/a | except (TypeError, AttributeError): |
|---|
| 641 | n/a | genericpath._check_arg_types('commonpath', *paths) |
|---|
| 642 | n/a | raise |
|---|
| 643 | n/a | |
|---|
| 644 | n/a | |
|---|
| 645 | n/a | # determine if two files are in fact the same file |
|---|
| 646 | n/a | try: |
|---|
| 647 | n/a | # GetFinalPathNameByHandle is available starting with Windows 6.0. |
|---|
| 648 | n/a | # Windows XP and non-Windows OS'es will mock _getfinalpathname. |
|---|
| 649 | n/a | if sys.getwindowsversion()[:2] >= (6, 0): |
|---|
| 650 | n/a | from nt import _getfinalpathname |
|---|
| 651 | n/a | else: |
|---|
| 652 | n/a | raise ImportError |
|---|
| 653 | n/a | except (AttributeError, ImportError): |
|---|
| 654 | n/a | # On Windows XP and earlier, two files are the same if their absolute |
|---|
| 655 | n/a | # pathnames are the same. |
|---|
| 656 | n/a | # Non-Windows operating systems fake this method with an XP |
|---|
| 657 | n/a | # approximation. |
|---|
| 658 | n/a | def _getfinalpathname(f): |
|---|
| 659 | n/a | return normcase(abspath(f)) |
|---|
| 660 | n/a | |
|---|
| 661 | n/a | |
|---|
| 662 | n/a | try: |
|---|
| 663 | n/a | # The genericpath.isdir implementation uses os.stat and checks the mode |
|---|
| 664 | n/a | # attribute to tell whether or not the path is a directory. |
|---|
| 665 | n/a | # This is overkill on Windows - just pass the path to GetFileAttributes |
|---|
| 666 | n/a | # and check the attribute from there. |
|---|
| 667 | n/a | from nt import _isdir as isdir |
|---|
| 668 | n/a | except ImportError: |
|---|
| 669 | n/a | # Use genericpath.isdir as imported above. |
|---|
| 670 | n/a | pass |
|---|