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 *
15 from genericpath
import _unicode
17 __all__
= ["normcase","isabs","join","splitdrive","split","splitext",
18 "basename","dirname","commonprefix","getsize","getmtime",
19 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
20 "ismount","walk","expanduser","expandvars","normpath","abspath",
21 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
22 "extsep","devnull","realpath","supports_unicode_filenames","relpath"]
24 # strings representing various path-related bits and pieces
32 if 'ce' in sys
.builtin_module_names
:
34 elif 'os2' in sys
.builtin_module_names
:
39 # Normalize the case of a pathname and map slashes to backslashes.
40 # Other normalizations (such as optimizing '../' away) are not done
41 # (this is done by normpath).
44 """Normalize case of pathname.
46 Makes all characters lowercase and all slashes into backslashes."""
47 return s
.replace("/", "\\").lower()
50 # Return whether a path is absolute.
51 # Trivial in Posix, harder on the Mac or MS-DOS.
52 # For DOS it is absolute if it starts with a slash or backslash (current
53 # volume), or if a pathname after the volume letter and colon / UNC resource
54 # starts with a slash or backslash.
57 """Test whether a path is absolute"""
59 return s
!= '' and s
[:1] in '/\\'
62 # Join two (or more) paths.
63 def join(path
, *paths
):
64 """Join two or more pathname components, inserting "\\" as needed."""
65 result_drive
, result_path
= splitdrive(path
)
67 p_drive
, p_path
= splitdrive(p
)
68 if p_path
and p_path
[0] in '\\/':
69 # Second path is absolute
70 if p_drive
or not result_drive
:
71 result_drive
= p_drive
74 elif p_drive
and p_drive
!= result_drive
:
75 if p_drive
.lower() != result_drive
.lower():
76 # Different drives => ignore the first path entirely
77 result_drive
= p_drive
80 # Same drive in different case
81 result_drive
= p_drive
82 # Second path is relative to the first
83 if result_path
and result_path
[-1] not in '\\/':
84 result_path
= result_path
+ '\\'
85 result_path
= result_path
+ p_path
86 ## add separator between UNC and non-absolute path
87 if (result_path
and result_path
[0] not in '\\/' and
88 result_drive
and result_drive
[-1:] != ':'):
89 return result_drive
+ sep
+ result_path
90 return result_drive
+ result_path
93 # Split a path in a drive specification (a drive letter followed by a
94 # colon) and the path specification.
95 # It is always true that drivespec + pathspec == p
97 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
98 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
101 result = splitdrive(p)
102 It is always true that:
103 result[0] + result[1] == p
105 If the path contained a drive letter, drive_or_unc will contain everything
106 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
108 If the path contained a UNC path, the drive_or_unc will contain the host name
109 and share up to but not including the fourth directory separator character.
110 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
112 Paths cannot contain both a drive letter and a UNC path.
116 normp
= p
.replace(altsep
, sep
)
117 if (normp
[0:2] == sep
*2) and (normp
[2:3] != sep
):
119 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
120 # \\machine\mountpoint\directory\etc\...
121 # directory ^^^^^^^^^^^^^^^
122 index
= normp
.find(sep
, 2)
125 index2
= normp
.find(sep
, index
+ 1)
126 # a UNC path can't have two slashes in a row
127 # (after the initial two)
128 if index2
== index
+ 1:
132 return p
[:index2
], p
[index2
:]
139 """Split a pathname into UNC mount point and relative path specifiers.
141 Return a 2-tuple (unc, rest); either part may be empty.
142 If unc is not empty, it has the form '//host/mount' (or similar
143 using backslashes). unc+rest is always the input path.
144 Paths containing drive letters never have an UNC part.
147 return '', p
# Drive letter present
149 if firstTwo
== '//' or firstTwo
== '\\\\':
151 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
152 # \\machine\mountpoint\directories...
153 # directory ^^^^^^^^^^^^^^^
154 normp
= p
.replace('\\', '/')
155 index
= normp
.find('/', 2)
158 index2
= normp
.find('/', index
+ 1)
159 # a UNC path can't have two slashes in a row
160 # (after the initial two)
161 if index2
== index
+ 1:
165 return p
[:index2
], p
[index2
:]
169 # Split a path in head (everything up to the last '/') and tail (the
170 # rest). After the trailing '/' is stripped, the invariant
171 # join(head, tail) == p holds.
172 # The resulting head won't end in '/' unless it is the root.
177 Return tuple (head, tail) where tail is everything after the final slash.
178 Either part may be empty."""
181 # set i to index beyond p's last slash
183 while i
and p
[i
-1] not in '/\\':
185 head
, tail
= p
[:i
], p
[i
:] # now tail has no slashes
186 # remove trailing slashes from head, unless it's all slashes
188 while head2
and head2
[-1] in '/\\':
191 return d
+ head
, tail
194 # Split a path in root and extension.
195 # The extension is everything starting at the last dot in the last
196 # pathname component; the root is everything before that.
197 # It is always true that root + ext == p.
200 return genericpath
._splitext
(p
, sep
, altsep
, extsep
)
201 splitext
.__doc
__ = genericpath
._splitext
.__doc
__
204 # Return the tail (basename) part of a path.
207 """Returns the final component of a pathname"""
211 # Return the head (dirname) part of a path.
214 """Returns the directory component of a pathname"""
217 # Is a path a symbolic link?
218 # This will always return false on systems where posix.lstat doesn't exist.
221 """Test for symbolic link.
222 On WindowsNT/95 and OS/2 always returns false
226 # alias exists to lexists
229 # Is a path a mount point? Either a root (with or without drive letter)
230 # or an UNC path with at most a / or \ after the mount point.
233 """Test whether a path is a mount point (defined as root of drive)"""
234 unc
, rest
= splitunc(path
)
236 return rest
in ("", "/", "\\")
237 p
= splitdrive(path
)[1]
238 return len(p
) == 1 and p
[0] in '/\\'
241 # Directory tree walk.
242 # For each directory under top (including top itself, but excluding
243 # '.' and '..'), func(arg, dirname, filenames) is called, where
244 # dirname is the name of the directory and filenames is the list
245 # of files (and subdirectories etc.) in the directory.
246 # The func may modify the filenames list, to implement a filter,
247 # or to impose a different order of visiting.
249 def walk(top
, func
, arg
):
250 """Directory tree walk with callback function.
252 For each directory in the directory tree rooted at top (including top
253 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
254 dirname is the name of the directory, and fnames a list of the names of
255 the files and subdirectories in dirname (excluding '.' and '..'). func
256 may modify the fnames list in-place (e.g. via del or slice assignment),
257 and walk will only recurse into the subdirectories whose names remain in
258 fnames; this can be used to implement a filter, or to impose a specific
259 order of visiting. No semantics are defined for, or required of, arg,
260 beyond that arg is always passed to func. It can be used, e.g., to pass
261 a filename pattern, or a mutable object designed to accumulate
262 statistics. Passing None for arg is common."""
263 warnings
.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
266 names
= os
.listdir(top
)
269 func(arg
, top
, names
)
271 name
= join(top
, name
)
273 walk(name
, func
, arg
)
276 # Expand paths beginning with '~' or '~user'.
277 # '~' means $HOME; '~user' means that user's home directory.
278 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
279 # the path is returned unchanged (leaving error reporting to whatever
280 # function is called with the expanded path as argument).
281 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
282 # (A function should also be defined to do full *sh-style environment
283 # variable expansion.)
285 def expanduser(path
):
286 """Expand ~ and ~user constructs.
288 If user or $HOME is unknown, do nothing."""
292 while i
< n
and path
[i
] not in '/\\':
295 if 'HOME' in os
.environ
:
296 userhome
= os
.environ
['HOME']
297 elif 'USERPROFILE' in os
.environ
:
298 userhome
= os
.environ
['USERPROFILE']
299 elif not 'HOMEPATH' in os
.environ
:
303 drive
= os
.environ
['HOMEDRIVE']
306 userhome
= join(drive
, os
.environ
['HOMEPATH'])
309 userhome
= join(dirname(userhome
), path
[1:i
])
311 return userhome
+ path
[i
:]
314 # Expand paths containing shell variable substitutions.
315 # The following rules apply:
316 # - no expansion within single quotes
317 # - '$$' is translated into '$'
318 # - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
319 # - ${varname} is accepted.
320 # - $varname is accepted.
321 # - %varname% is accepted.
322 # - varnames can be made out of letters, digits and the characters '_-'
323 # (though is not verified in the ${varname} and %varname% cases)
324 # XXX With COMMAND.COM you can use any characters in a variable name,
325 # XXX except '^|<>='.
327 def expandvars(path
):
328 """Expand shell variables of the forms $var, ${var} and %var%.
330 Unknown variables are left unchanged."""
331 if '$' not in path
and '%' not in path
:
334 varchars
= string
.ascii_letters
+ string
.digits
+ '_-'
335 if isinstance(path
, _unicode
):
336 encoding
= sys
.getfilesystemencoding()
338 return os
.environ
[var
.encode(encoding
)].decode(encoding
)
341 return os
.environ
[var
]
345 while index
< pathlen
:
347 if c
== '\'': # no expansion within single quotes
348 path
= path
[index
+ 1:]
351 index
= path
.index('\'')
352 res
= res
+ '\'' + path
[:index
+ 1]
356 elif c
== '%': # variable or '%'
357 if path
[index
+ 1:index
+ 2] == '%':
361 path
= path
[index
+1:]
364 index
= path
.index('%')
366 res
= res
+ '%' + path
371 res
= res
+ getenv(var
)
373 res
= res
+ '%' + var
+ '%'
374 elif c
== '$': # variable or '$$'
375 if path
[index
+ 1:index
+ 2] == '$':
378 elif path
[index
+ 1:index
+ 2] == '{':
379 path
= path
[index
+2:]
382 index
= path
.index('}')
385 res
= res
+ getenv(var
)
387 res
= res
+ '${' + var
+ '}'
389 res
= res
+ '${' + path
394 c
= path
[index
:index
+ 1]
395 while c
!= '' and c
in varchars
:
398 c
= path
[index
:index
+ 1]
400 res
= res
+ getenv(var
)
402 res
= res
+ '$' + var
411 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
412 # Previously, this function also truncated pathnames to 8+3 format,
413 # but as this module is called "ntpath", that's obviously wrong!
416 """Normalize path, eliminating double slashes, etc."""
417 # Preserve unicode (if path is unicode)
418 backslash
, dot
= (u
'\\', u
'.') if isinstance(path
, _unicode
) else ('\\', '.')
419 if path
.startswith(('\\\\.\\', '\\\\?\\')):
420 # in the case of paths with these prefixes:
421 # \\.\ -> device names
422 # \\?\ -> literal paths
423 # do not do any normalization, but return the path unchanged
425 path
= path
.replace("/", "\\")
426 prefix
, path
= splitdrive(path
)
427 # We need to be careful here. If the prefix is empty, and the path starts
428 # with a backslash, it could either be an absolute path on the current
429 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
430 # is therefore imperative NOT to collapse multiple backslashes blindly in
432 # The code below preserves multiple backslashes when there is no drive
433 # letter. This means that the invalid filename \\\a\b is preserved
434 # unchanged, where a\\\b is normalised to a\b. It's not clear that there
435 # is any better behaviour for such edge cases.
437 # No drive letter - preserve initial backslashes
438 while path
[:1] == "\\":
439 prefix
= prefix
+ backslash
442 # We have a drive letter - collapse initial backslashes
443 if path
.startswith("\\"):
444 prefix
= prefix
+ backslash
445 path
= path
.lstrip("\\")
446 comps
= path
.split("\\")
448 while i
< len(comps
):
449 if comps
[i
] in ('.', ''):
451 elif comps
[i
] == '..':
452 if i
> 0 and comps
[i
-1] != '..':
455 elif i
== 0 and prefix
.endswith("\\"):
461 # If the path is now empty, substitute '.'
462 if not prefix
and not comps
:
464 return prefix
+ backslash
.join(comps
)
467 # Return an absolute path.
469 from nt
import _getfullpathname
471 except ImportError: # not running on Windows - mock up something sensible
473 """Return the absolute version of a path."""
475 if isinstance(path
, _unicode
):
479 path
= join(cwd
, path
)
480 return normpath(path
)
482 else: # use native Windows method on Windows
484 """Return the absolute version of a path."""
486 if path
: # Empty path must return current working directory.
488 path
= _getfullpathname(path
)
490 pass # Bad path - return unchanged.
491 elif isinstance(path
, _unicode
):
495 return normpath(path
)
497 # realpath is a no-op on systems without islink support
499 # Win9x family and earlier have no Unicode filename support.
500 supports_unicode_filenames
= (hasattr(sys
, "getwindowsversion") and
501 sys
.getwindowsversion()[3] >= 2)
503 def _abspath_split(path
):
504 abs = abspath(normpath(path
))
505 prefix
, rest
= splitunc(abs)
506 is_unc
= bool(prefix
)
508 prefix
, rest
= splitdrive(abs)
509 return is_unc
, prefix
, [x
for x
in rest
.split(sep
) if x
]
511 def relpath(path
, start
=curdir
):
512 """Return a relative version of a path"""
515 raise ValueError("no path specified")
517 start_is_unc
, start_prefix
, start_list
= _abspath_split(start
)
518 path_is_unc
, path_prefix
, path_list
= _abspath_split(path
)
520 if path_is_unc ^ start_is_unc
:
521 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
523 if path_prefix
.lower() != start_prefix
.lower():
525 raise ValueError("path is on UNC root %s, start on UNC root %s"
526 % (path_prefix
, start_prefix
))
528 raise ValueError("path is on drive %s, start on drive %s"
529 % (path_prefix
, start_prefix
))
530 # Work out how much of the filepath is shared by start and path.
532 for e1
, e2
in zip(start_list
, path_list
):
533 if e1
.lower() != e2
.lower():
537 rel_list
= [pardir
] * (len(start_list
)-i
) + path_list
[i
:]
540 return join(*rel_list
)
543 # The genericpath.isdir implementation uses os.stat and checks the mode
544 # attribute to tell whether or not the path is a directory.
545 # This is overkill on Windows - just pass the path to GetFileAttributes
546 # and check the attribute from there.
547 from nt
import _isdir
as isdir
549 # Use genericpath.isdir as imported above.