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
14 from genericpath
import *
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"]
23 # strings representing various path-related bits and pieces
31 if 'ce' in sys
.builtin_module_names
:
33 elif 'os2' in sys
.builtin_module_names
:
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).
43 """Normalize case of pathname.
45 Makes all characters lowercase and all slashes into backslashes."""
46 return s
.replace("/", "\\").lower()
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.
56 """Test whether a path is absolute"""
58 return s
!= '' and s
[:1] in '/\\'
61 # Join two (or more) paths.
64 """Join two or more pathname components, inserting "\\" as needed.
65 If any component is an absolute path, all previous path components
69 b_wins
= 0 # set to 1 iff b makes path irrelevant
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'
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.
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 "/\\"):
95 # Join, and ensure there's a separator.
98 if b
and b
[0] in "/\\":
102 elif path
[-1] == ":":
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
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
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
)
128 return pparts
[0] + ':', pparts
[1]
137 """Split a pathname into UNC mount point and relative path specifiers.
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.
144 if len(p
.split(':', 2)) > 1:
145 return '', p
# Drive letter present
147 if firstTwo
== '//' or firstTwo
== '\\\\':
149 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
150 # \\machine\mountpoint\directories...
151 # directory ^^^^^^^^^^^^^^^
153 index
= normp
.find('\\', 2)
155 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
157 index
= normp
.find('\\', index
+ 1)
160 return p
[:index
], p
[index
:]
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.
172 Return tuple (head, tail) where tail is everything after the final slash.
173 Either part may be empty."""
176 # set i to index beyond p's last slash
178 while i
and p
[i
-1] not in '/\\':
180 head
, tail
= p
[:i
], p
[i
:] # now tail has no slashes
181 # remove trailing slashes from head, unless it's all slashes
183 while head2
and head2
[-1] in '/\\':
186 return d
+ head
, tail
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.
195 return genericpath
._splitext
(p
, sep
, altsep
, extsep
)
196 splitext
.__doc
__ = genericpath
._splitext
.__doc
__
199 # Return the tail (basename) part of a path.
202 """Returns the final component of a pathname"""
206 # Return the head (dirname) part of a path.
209 """Returns the directory component of a pathname"""
212 # Is a path a symbolic link?
213 # This will always return false on systems where posix.lstat doesn't exist.
216 """Test for symbolic link.
217 On WindowsNT/95 and OS/2 always returns false
221 # alias exists to lexists
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.
228 """Test whether a path is a mount point (defined as root of drive)"""
229 unc
, rest
= splitunc(path
)
231 return rest
in ("", "/", "\\")
232 p
= splitdrive(path
)[1]
233 return len(p
) == 1 and p
[0] in '/\\'
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.
244 def walk(top
, func
, arg
):
245 """Directory tree walk with callback function.
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.",
261 names
= os
.listdir(top
)
264 func(arg
, top
, names
)
266 name
= join(top
, name
)
268 walk(name
, func
, arg
)
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.)
280 def expanduser(path
):
281 """Expand ~ and ~user constructs.
283 If user or $HOME is unknown, do nothing."""
287 while i
< n
and path
[i
] not in '/\\':
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
:
298 drive
= os
.environ
['HOMEDRIVE']
301 userhome
= join(drive
, os
.environ
['HOMEPATH'])
304 userhome
= join(dirname(userhome
), path
[1:i
])
306 return userhome
+ path
[i
:]
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 '^|<>='.
322 def expandvars(path
):
323 """Expand shell variables of the forms $var, ${var} and %var%.
325 Unknown variables are left unchanged."""
326 if '$' not in path
and '%' not in path
:
329 varchars
= string
.ascii_letters
+ string
.digits
+ '_-'
333 while index
< pathlen
:
335 if c
== '\'': # no expansion within single quotes
336 path
= path
[index
+ 1:]
339 index
= path
.index('\'')
340 res
= res
+ '\'' + path
[:index
+ 1]
344 elif c
== '%': # variable or '%'
345 if path
[index
+ 1:index
+ 2] == '%':
349 path
= path
[index
+1:]
352 index
= path
.index('%')
354 res
= res
+ '%' + path
358 if var
in os
.environ
:
359 res
= res
+ os
.environ
[var
]
361 res
= res
+ '%' + var
+ '%'
362 elif c
== '$': # variable or '$$'
363 if path
[index
+ 1:index
+ 2] == '$':
366 elif path
[index
+ 1:index
+ 2] == '{':
367 path
= path
[index
+2:]
370 index
= path
.index('}')
372 if var
in os
.environ
:
373 res
= res
+ os
.environ
[var
]
375 res
= res
+ '${' + var
+ '}'
377 res
= res
+ '${' + path
382 c
= path
[index
:index
+ 1]
383 while c
!= '' and c
in varchars
:
386 c
= path
[index
:index
+ 1]
387 if var
in os
.environ
:
388 res
= res
+ os
.environ
[var
]
390 res
= res
+ '$' + var
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!
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
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
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.
425 # No drive letter - preserve initial backslashes
426 while path
[:1] == "\\":
427 prefix
= prefix
+ backslash
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("\\")
436 while i
< len(comps
):
437 if comps
[i
] in ('.', ''):
439 elif comps
[i
] == '..':
440 if i
> 0 and comps
[i
-1] != '..':
443 elif i
== 0 and prefix
.endswith("\\"):
449 # If the path is now empty, substitute '.'
450 if not prefix
and not comps
:
452 return prefix
+ backslash
.join(comps
)
455 # Return an absolute path.
457 from nt
import _getfullpathname
459 except ImportError: # not running on Windows - mock up something sensible
461 """Return the absolute version of a path."""
463 if isinstance(path
, unicode):
467 path
= join(cwd
, path
)
468 return normpath(path
)
470 else: # use native Windows method on Windows
472 """Return the absolute version of a path."""
474 if path
: # Empty path must return current working directory.
476 path
= _getfullpathname(path
)
478 pass # Bad path - return unchanged.
479 elif isinstance(path
, unicode):
483 return normpath(path
)
485 # realpath is a no-op on systems without islink support
487 # Win9x family and earlier have no Unicode filename support.
488 supports_unicode_filenames
= (hasattr(sys
, "getwindowsversion") and
489 sys
.getwindowsversion()[3] >= 2)
491 def _abspath_split(path
):
492 abs = abspath(normpath(path
))
493 prefix
, rest
= splitunc(abs)
494 is_unc
= bool(prefix
)
496 prefix
, rest
= splitdrive(abs)
497 return is_unc
, prefix
, [x
for x
in rest
.split(sep
) if x
]
499 def relpath(path
, start
=curdir
):
500 """Return a relative version of a path"""
503 raise ValueError("no path specified")
505 start_is_unc
, start_prefix
, start_list
= _abspath_split(start
)
506 path_is_unc
, path_prefix
, path_list
= _abspath_split(path
)
508 if path_is_unc ^ start_is_unc
:
509 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
511 if path_prefix
.lower() != start_prefix
.lower():
513 raise ValueError("path is on UNC root %s, start on UNC root %s"
514 % (path_prefix
, start_prefix
))
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.
520 for e1
, e2
in zip(start_list
, path_list
):
521 if e1
.lower() != e2
.lower():
525 rel_list
= [pardir
] * (len(start_list
)-i
) + path_list
[i
:]
528 return join(*rel_list
)