1# Module 'ntpath' -- common operations on WinNT/Win95 pathnames 2"""Common pathname manipulations, WindowsNT/95 version. 3 4Instead of importing this module directly, import os and refer to this 5module as os.path. 6""" 7 8import os 9import sys 10import stat 11import genericpath 12import warnings 13 14from genericpath import * 15 16__all__ = ["normcase","isabs","join","splitdrive","split","splitext", 17 "basename","dirname","commonprefix","getsize","getmtime", 18 "getatime","getctime", "islink","exists","lexists","isdir","isfile", 19 "ismount","walk","expanduser","expandvars","normpath","abspath", 20 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep", 21 "extsep","devnull","realpath","supports_unicode_filenames","relpath"] 22 23# strings representing various path-related bits and pieces 24curdir = '.' 25pardir = '..' 26extsep = '.' 27sep = '\\' 28pathsep = ';' 29altsep = '/' 30defpath = '.;C:\\bin' 31if 'ce' in sys.builtin_module_names: 32 defpath = '\\Windows' 33elif 'os2' in sys.builtin_module_names: 34 # OS/2 w/ VACPP 35 altsep = '/' 36devnull = 'nul' 37 38# Normalize the case of a pathname and map slashes to backslashes. 39# Other normalizations (such as optimizing '../' away) are not done 40# (this is done by normpath). 41 42def normcase(s): 43 """Normalize case of pathname. 44 45 Makes all characters lowercase and all slashes into backslashes.""" 46 return s.replace("/", "\\").lower() 47 48 49# Return whether a path is absolute. 50# Trivial in Posix, harder on the Mac or MS-DOS. 51# For DOS it is absolute if it starts with a slash or backslash (current 52# volume), or if a pathname after the volume letter and colon / UNC resource 53# starts with a slash or backslash. 54 55def isabs(s): 56 """Test whether a path is absolute""" 57 s = splitdrive(s)[1] 58 return s != '' and s[:1] in '/\\' 59 60 61# Join two (or more) paths. 62 63def join(a, *p): 64 """Join two or more pathname components, inserting "\\" as needed. 65 If any component is an absolute path, all previous path components 66 will be discarded.""" 67 path = a 68 for b in p: 69 b_wins = 0 # set to 1 iff b makes path irrelevant 70 if path == "": 71 b_wins = 1 72 73 elif isabs(b): 74 # This probably wipes out path so far. However, it's more 75 # complicated if path begins with a drive letter: 76 # 1. join('c:', '/a') == 'c:/a' 77 # 2. join('c:/', '/a') == 'c:/a' 78 # But 79 # 3. join('c:/a', '/b') == '/b' 80 # 4. join('c:', 'd:/') = 'd:/' 81 # 5. join('c:/', 'd:/') = 'd:/' 82 if path[1:2] != ":" or b[1:2] == ":": 83 # Path doesn't start with a drive letter, or cases 4 and 5. 84 b_wins = 1 85 86 # Else path has a drive letter, and b doesn't but is absolute. 87 elif len(path) > 3 or (len(path) == 3 and 88 path[-1] not in "/\\"): 89 # case 3 90 b_wins = 1 91 92 if b_wins: 93 path = b 94 else: 95 # Join, and ensure there's a separator. 96 assert len(path) > 0 97 if path[-1] in "/\\": 98 if b and b[0] in "/\\": 99 path += b[1:] 100 else: 101 path += b 102 elif path[-1] == ":": 103 path += b 104 elif b: 105 if b[0] in "/\\": 106 path += b 107 else: 108 path += "\\" + b 109 else: 110 # path is not empty and does not end with a backslash, 111 # but b is empty; since, e.g., split('a/') produces 112 # ('a', ''), it's best if join() adds a backslash in 113 # this case. 114 path += '\\' 115 116 return path 117 118 119# Split a path in a drive specification (a drive letter followed by a 120# colon) and the path specification. 121# It is always true that drivespec + pathspec == p 122def splitdrive(p): 123 """Split a pathname into drive and path specifiers. Returns a 2-tuple 124"(drive,path)"; either part may be empty""" 125 pparts = p.split(':', 2) 126 numparts = len(pparts) 127 if numparts == 2: 128 return pparts[0] + ':', pparts[1] 129 else: 130 if numparts == 1: 131 return '', pparts[0] 132 return '', p 133 134 135# Parse UNC paths 136def splitunc(p): 137 """Split a pathname into UNC mount point and relative path specifiers. 138 139 Return a 2-tuple (unc, rest); either part may be empty. 140 If unc is not empty, it has the form '//host/mount' (or similar 141 using backslashes). unc+rest is always the input path. 142 Paths containing drive letters never have an UNC part. 143 """ 144 if len(p.split(':', 2)) > 1: 145 return '', p # Drive letter present 146 firstTwo = p[0:2] 147 if firstTwo == '//' or firstTwo == '\\\\': 148 # is a UNC path: 149 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter 150 # \\machine\mountpoint\directories... 151 # directory ^^^^^^^^^^^^^^^ 152 normp = normcase(p) 153 index = normp.find('\\', 2) 154 if index == -1: 155 ##raise RuntimeError, 'illegal UNC path: "' + p + '"' 156 return ("", p) 157 index = normp.find('\\', index + 1) 158 if index == -1: 159 index = len(p) 160 return p[:index], p[index:] 161 return '', p 162 163 164# Split a path in head (everything up to the last '/') and tail (the 165# rest). After the trailing '/' is stripped, the invariant 166# join(head, tail) == p holds. 167# The resulting head won't end in '/' unless it is the root. 168 169def split(p): 170 """Split a pathname. 171 172 Return tuple (head, tail) where tail is everything after the final slash. 173 Either part may be empty.""" 174 175 d, p = splitdrive(p) 176 # set i to index beyond p's last slash 177 i = len(p) 178 while i and p[i-1] not in '/\\': 179 i = i - 1 180 head, tail = p[:i], p[i:] # now tail has no slashes 181 # remove trailing slashes from head, unless it's all slashes 182 head2 = head 183 while head2 and head2[-1] in '/\\': 184 head2 = head2[:-1] 185 head = head2 or head 186 return d + head, tail 187 188 189# Split a path in root and extension. 190# The extension is everything starting at the last dot in the last 191# pathname component; the root is everything before that. 192# It is always true that root + ext == p. 193 194def splitext(p): 195 return genericpath._splitext(p, sep, altsep, extsep) 196splitext.__doc__ = genericpath._splitext.__doc__ 197 198 199# Return the tail (basename) part of a path. 200 201def basename(p): 202 """Returns the final component of a pathname""" 203 return split(p)[1] 204 205 206# Return the head (dirname) part of a path. 207 208def dirname(p): 209 """Returns the directory component of a pathname""" 210 return split(p)[0] 211 212# Is a path a symbolic link? 213# This will always return false on systems where posix.lstat doesn't exist. 214 215def islink(path): 216 """Test for symbolic link. 217 On WindowsNT/95 and OS/2 always returns false 218 """ 219 return False 220 221# alias exists to lexists 222lexists = exists 223 224# Is a path a mount point? Either a root (with or without drive letter) 225# or an UNC path with at most a / or \ after the mount point. 226 227def ismount(path): 228 """Test whether a path is a mount point (defined as root of drive)""" 229 unc, rest = splitunc(path) 230 if unc: 231 return rest in ("", "/", "\\") 232 p = splitdrive(path)[1] 233 return len(p) == 1 and p[0] in '/\\' 234 235 236# Directory tree walk. 237# For each directory under top (including top itself, but excluding 238# '.' and '..'), func(arg, dirname, filenames) is called, where 239# dirname is the name of the directory and filenames is the list 240# of files (and subdirectories etc.) in the directory. 241# The func may modify the filenames list, to implement a filter, 242# or to impose a different order of visiting. 243 244def walk(top, func, arg): 245 """Directory tree walk with callback function. 246 247 For each directory in the directory tree rooted at top (including top 248 itself, but excluding '.' and '..'), call func(arg, dirname, fnames). 249 dirname is the name of the directory, and fnames a list of the names of 250 the files and subdirectories in dirname (excluding '.' and '..'). func 251 may modify the fnames list in-place (e.g. via del or slice assignment), 252 and walk will only recurse into the subdirectories whose names remain in 253 fnames; this can be used to implement a filter, or to impose a specific 254 order of visiting. No semantics are defined for, or required of, arg, 255 beyond that arg is always passed to func. It can be used, e.g., to pass 256 a filename pattern, or a mutable object designed to accumulate 257 statistics. Passing None for arg is common.""" 258 warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.", 259 stacklevel=2) 260 try: 261 names = os.listdir(top) 262 except os.error: 263 return 264 func(arg, top, names) 265 for name in names: 266 name = join(top, name) 267 if isdir(name): 268 walk(name, func, arg) 269 270 271# Expand paths beginning with '~' or '~user'. 272# '~' means $HOME; '~user' means that user's home directory. 273# If the path doesn't begin with '~', or if the user or $HOME is unknown, 274# the path is returned unchanged (leaving error reporting to whatever 275# function is called with the expanded path as argument). 276# See also module 'glob' for expansion of *, ? and [...] in pathnames. 277# (A function should also be defined to do full *sh-style environment 278# variable expansion.) 279 280def expanduser(path): 281 """Expand ~ and ~user constructs. 282 283 If user or $HOME is unknown, do nothing.""" 284 if path[:1] != '~': 285 return path 286 i, n = 1, len(path) 287 while i < n and path[i] not in '/\\': 288 i = i + 1 289 290 if 'HOME' in os.environ: 291 userhome = os.environ['HOME'] 292 elif 'USERPROFILE' in os.environ: 293 userhome = os.environ['USERPROFILE'] 294 elif not 'HOMEPATH' in os.environ: 295 return path 296 else: 297 try: 298 drive = os.environ['HOMEDRIVE'] 299 except KeyError: 300 drive = '' 301 userhome = join(drive, os.environ['HOMEPATH']) 302 303 if i != 1: #~user 304 userhome = join(dirname(userhome), path[1:i]) 305 306 return userhome + path[i:] 307 308 309# Expand paths containing shell variable substitutions. 310# The following rules apply: 311# - no expansion within single quotes 312# - '$$' is translated into '$' 313# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2% 314# - ${varname} is accepted. 315# - $varname is accepted. 316# - %varname% is accepted. 317# - varnames can be made out of letters, digits and the characters '_-' 318# (though is not verified in the ${varname} and %varname% cases) 319# XXX With COMMAND.COM you can use any characters in a variable name, 320# XXX except '^|<>='. 321 322def expandvars(path): 323 """Expand shell variables of the forms $var, ${var} and %var%. 324 325 Unknown variables are left unchanged.""" 326 if '$' not in path and '%' not in path: 327 return path 328 import string 329 varchars = string.ascii_letters + string.digits + '_-' 330 res = '' 331 index = 0 332 pathlen = len(path) 333 while index < pathlen: 334 c = path[index] 335 if c == '\'': # no expansion within single quotes 336 path = path[index + 1:] 337 pathlen = len(path) 338 try: 339 index = path.index('\'') 340 res = res + '\'' + path[:index + 1] 341 except ValueError: 342 res = res + path 343 index = pathlen - 1 344 elif c == '%': # variable or '%' 345 if path[index + 1:index + 2] == '%': 346 res = res + c 347 index = index + 1 348 else: 349 path = path[index+1:] 350 pathlen = len(path) 351 try: 352 index = path.index('%') 353 except ValueError: 354 res = res + '%' + path 355 index = pathlen - 1 356 else: 357 var = path[:index] 358 if var in os.environ: 359 res = res + os.environ[var] 360 else: 361 res = res + '%' + var + '%' 362 elif c == '$': # variable or '$$' 363 if path[index + 1:index + 2] == '$': 364 res = res + c 365 index = index + 1 366 elif path[index + 1:index + 2] == '{': 367 path = path[index+2:] 368 pathlen = len(path) 369 try: 370 index = path.index('}') 371 var = path[:index] 372 if var in os.environ: 373 res = res + os.environ[var] 374 else: 375 res = res + '${' + var + '}' 376 except ValueError: 377 res = res + '${' + path 378 index = pathlen - 1 379 else: 380 var = '' 381 index = index + 1 382 c = path[index:index + 1] 383 while c != '' and c in varchars: 384 var = var + c 385 index = index + 1 386 c = path[index:index + 1] 387 if var in os.environ: 388 res = res + os.environ[var] 389 else: 390 res = res + '$' + var 391 if c != '': 392 index = index - 1 393 else: 394 res = res + c 395 index = index + 1 396 return res 397 398 399# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. 400# Previously, this function also truncated pathnames to 8+3 format, 401# but as this module is called "ntpath", that's obviously wrong! 402 403def normpath(path): 404 """Normalize path, eliminating double slashes, etc.""" 405 # Preserve unicode (if path is unicode) 406 backslash, dot = (u'\\', u'.') if isinstance(path, unicode) else ('\\', '.') 407 if path.startswith(('\\\\.\\', '\\\\?\\')): 408 # in the case of paths with these prefixes: 409 # \\.\ -> device names 410 # \\?\ -> literal paths 411 # do not do any normalization, but return the path unchanged 412 return path 413 path = path.replace("/", "\\") 414 prefix, path = splitdrive(path) 415 # We need to be careful here. If the prefix is empty, and the path starts 416 # with a backslash, it could either be an absolute path on the current 417 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It 418 # is therefore imperative NOT to collapse multiple backslashes blindly in 419 # that case. 420 # The code below preserves multiple backslashes when there is no drive 421 # letter. This means that the invalid filename \\\a\b is preserved 422 # unchanged, where a\\\b is normalised to a\b. It's not clear that there 423 # is any better behaviour for such edge cases. 424 if prefix == '': 425 # No drive letter - preserve initial backslashes 426 while path[:1] == "\\": 427 prefix = prefix + backslash 428 path = path[1:] 429 else: 430 # We have a drive letter - collapse initial backslashes 431 if path.startswith("\\"): 432 prefix = prefix + backslash 433 path = path.lstrip("\\") 434 comps = path.split("\\") 435 i = 0 436 while i < len(comps): 437 if comps[i] in ('.', ''): 438 del comps[i] 439 elif comps[i] == '..': 440 if i > 0 and comps[i-1] != '..': 441 del comps[i-1:i+1] 442 i -= 1 443 elif i == 0 and prefix.endswith("\\"): 444 del comps[i] 445 else: 446 i += 1 447 else: 448 i += 1 449 # If the path is now empty, substitute '.' 450 if not prefix and not comps: 451 comps.append(dot) 452 return prefix + backslash.join(comps) 453 454 455# Return an absolute path. 456try: 457 from nt import _getfullpathname 458 459except ImportError: # not running on Windows - mock up something sensible 460 def abspath(path): 461 """Return the absolute version of a path.""" 462 if not isabs(path): 463 if isinstance(path, unicode): 464 cwd = os.getcwdu() 465 else: 466 cwd = os.getcwd() 467 path = join(cwd, path) 468 return normpath(path) 469 470else: # use native Windows method on Windows 471 def abspath(path): 472 """Return the absolute version of a path.""" 473 474 if path: # Empty path must return current working directory. 475 try: 476 path = _getfullpathname(path) 477 except WindowsError: 478 pass # Bad path - return unchanged. 479 elif isinstance(path, unicode): 480 path = os.getcwdu() 481 else: 482 path = os.getcwd() 483 return normpath(path) 484 485# realpath is a no-op on systems without islink support 486realpath = abspath 487# Win9x family and earlier have no Unicode filename support. 488supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and 489 sys.getwindowsversion()[3] >= 2) 490 491def _abspath_split(path): 492 abs = abspath(normpath(path)) 493 prefix, rest = splitunc(abs) 494 is_unc = bool(prefix) 495 if not is_unc: 496 prefix, rest = splitdrive(abs) 497 return is_unc, prefix, [x for x in rest.split(sep) if x] 498 499def relpath(path, start=curdir): 500 """Return a relative version of a path""" 501 502 if not path: 503 raise ValueError("no path specified") 504 505 start_is_unc, start_prefix, start_list = _abspath_split(start) 506 path_is_unc, path_prefix, path_list = _abspath_split(path) 507 508 if path_is_unc ^ start_is_unc: 509 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" 510 % (path, start)) 511 if path_prefix.lower() != start_prefix.lower(): 512 if path_is_unc: 513 raise ValueError("path is on UNC root %s, start on UNC root %s" 514 % (path_prefix, start_prefix)) 515 else: 516 raise ValueError("path is on drive %s, start on drive %s" 517 % (path_prefix, start_prefix)) 518 # Work out how much of the filepath is shared by start and path. 519 i = 0 520 for e1, e2 in zip(start_list, path_list): 521 if e1.lower() != e2.lower(): 522 break 523 i += 1 524 525 rel_list = [pardir] * (len(start_list)-i) + path_list[i:] 526 if not rel_list: 527 return curdir 528 return join(*rel_list) 529