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