1 """Common operations on Posix pathnames.
3 Instead of importing this module directly, import os and refer to
4 this module as os.path. The "os.path" name is an alias for this
5 module on Posix systems; on other systems (e.g. Mac, Windows),
6 os.path provides the same operations in a manner specific to that
7 platform, and is an alias to another module (e.g. macpath, ntpath).
9 Some of this can actually be useful on non-Posix systems too, e.g.
10 for manipulation of the pathname component of URLs.
17 from genericpath
import *
19 __all__
= ["normcase","isabs","join","splitdrive","split","splitext",
20 "basename","dirname","commonprefix","getsize","getmtime",
21 "getatime","getctime","islink","exists","lexists","isdir","isfile",
22 "ismount", "expanduser","expandvars","normpath","abspath",
23 "samefile","sameopenfile","samestat",
24 "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
25 "devnull","realpath","supports_unicode_filenames","relpath"]
27 # Strings representing various path-related bits and pieces.
28 # These are primarily for export; internally, they are hardcoded.
34 defpath
= ':/bin:/usr/bin'
39 if isinstance(path
, bytes
):
44 # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
45 # On MS-DOS this may also turn slashes into backslashes; however, other
46 # normalizations (such as optimizing '../' away) are not allowed
47 # (another function should be defined to do that).
50 """Normalize case of pathname. Has no effect under Posix"""
51 # TODO: on Mac OS X, this should really return s.lower().
55 # Return whether a path is absolute.
56 # Trivial in Posix, harder on the Mac or MS-DOS.
59 """Test whether a path is absolute"""
61 return s
.startswith(sep
)
65 # Ignore the previous parts if a part is absolute.
66 # Insert a '/' unless the first part is empty or already ends in '/'.
69 """Join two or more pathname components, inserting '/' as needed.
70 If any component is an absolute path, all previous path components
77 elif not path
or path
.endswith(sep
):
84 # Split a path in head (everything up to the last '/') and tail (the
85 # rest). If the path ends in '/', tail will be empty. If there is no
86 # '/' in the path, head will be empty.
87 # Trailing '/'es are stripped from head unless it is the root.
90 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
91 everything after the final slash. Either part may be empty."""
94 head
, tail
= p
[:i
], p
[i
:]
95 if head
and head
!= sep
*len(head
):
96 head
= head
.rstrip(sep
)
100 # Split a path in root and extension.
101 # The extension is everything starting at the last dot in the last
102 # pathname component; the root is everything before that.
103 # It is always true that root + ext == p.
106 if isinstance(p
, bytes
):
112 return genericpath
._splitext
(p
, sep
, None, extsep
)
113 splitext
.__doc
__ = genericpath
._splitext
.__doc
__
115 # Split a pathname into a drive specification and the rest of the
116 # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
119 """Split a pathname into drive and path. On Posix, drive is always
124 # Return the tail (basename) part of a path, same as split(path)[1].
127 """Returns the final component of a pathname"""
133 # Return the head (dirname) part of a path, same as split(path)[0].
136 """Returns the directory component of a pathname"""
140 if head
and head
!= sep
*len(head
):
141 head
= head
.rstrip(sep
)
145 # Is a path a symbolic link?
146 # This will always return false on systems where os.lstat doesn't exist.
149 """Test whether a path is a symbolic link"""
152 except (os
.error
, AttributeError):
154 return stat
.S_ISLNK(st
.st_mode
)
156 # Being true for dangling symbolic links is also useful.
159 """Test whether a path exists. Returns True for broken symbolic links"""
167 # Are two filenames really pointing to the same file?
169 def samefile(f1
, f2
):
170 """Test whether two pathnames reference the same actual file"""
173 return samestat(s1
, s2
)
176 # Are two open files really referencing the same file?
177 # (Not necessarily the same file descriptor!)
179 def sameopenfile(fp1
, fp2
):
180 """Test whether two open file objects reference the same file"""
183 return samestat(s1
, s2
)
186 # Are two stat buffers (obtained from stat, fstat or lstat)
187 # describing the same file?
189 def samestat(s1
, s2
):
190 """Test whether two stat buffers reference the same file"""
191 return s1
.st_ino
== s2
.st_ino
and \
192 s1
.st_dev
== s2
.st_dev
195 # Is a path a mount point?
196 # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
199 """Test whether a path is a mount point"""
202 if isinstance(path
, bytes
):
203 parent
= join(path
, b
'..')
205 parent
= join(path
, '..')
206 s2
= os
.lstat(parent
)
208 return False # It doesn't exist -- so not a mount point :-)
212 return True # path/.. on a different device as path
216 return True # path/.. is the same i-node as path
220 # Expand paths beginning with '~' or '~user'.
221 # '~' means $HOME; '~user' means that user's home directory.
222 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
223 # the path is returned unchanged (leaving error reporting to whatever
224 # function is called with the expanded path as argument).
225 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
226 # (A function should also be defined to do full *sh-style environment
227 # variable expansion.)
229 def expanduser(path
):
230 """Expand ~ and ~user constructions. If user or $HOME is unknown,
232 if isinstance(path
, bytes
):
236 if not path
.startswith(tilde
):
239 i
= path
.find(sep
, 1)
243 if 'HOME' not in os
.environ
:
245 userhome
= pwd
.getpwuid(os
.getuid()).pw_dir
247 userhome
= os
.environ
['HOME']
251 if isinstance(name
, bytes
):
252 name
= str(name
, 'ASCII')
254 pwent
= pwd
.getpwnam(name
)
257 userhome
= pwent
.pw_dir
258 if isinstance(path
, bytes
):
259 userhome
= userhome
.encode(sys
.getfilesystemencoding())
263 userhome
= userhome
.rstrip(root
) or userhome
264 return userhome
+ path
[i
:]
267 # Expand paths containing shell variable substitutions.
268 # This expands the forms $variable and ${variable} only.
269 # Non-existent variables are left unchanged.
274 def expandvars(path
):
275 """Expand shell variables of form $var and ${var}. Unknown variables
276 are left unchanged."""
277 global _varprog
, _varprogb
278 if isinstance(path
, bytes
):
283 _varprogb
= re
.compile(br
'\$(\w+|\{[^}]*\})', re
.ASCII
)
284 search
= _varprogb
.search
292 _varprog
= re
.compile(r
'\$(\w+|\{[^}]*\})', re
.ASCII
)
293 search
= _varprog
.search
303 if name
.startswith(start
) and name
.endswith(end
):
305 if isinstance(name
, bytes
):
306 name
= str(name
, 'ASCII')
307 if name
in os
.environ
:
309 value
= os
.environ
[name
]
310 if isinstance(path
, bytes
):
311 value
= value
.encode('ASCII')
312 path
= path
[:i
] + value
320 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
321 # It should be understood that this may change the meaning of the path
322 # if it contains symbolic links!
325 """Normalize path, eliminating double slashes, etc."""
326 if isinstance(path
, bytes
):
338 initial_slashes
= path
.startswith(sep
)
339 # POSIX allows one or two initial slashes, but treats three or more
341 if (initial_slashes
and
342 path
.startswith(sep
*2) and not path
.startswith(sep
*3)):
344 comps
= path
.split(sep
)
347 if comp
in (empty
, dot
):
349 if (comp
!= dotdot
or (not initial_slashes
and not new_comps
) or
350 (new_comps
and new_comps
[-1] == dotdot
)):
351 new_comps
.append(comp
)
355 path
= sep
.join(comps
)
357 path
= sep
*initial_slashes
+ path
362 """Return an absolute path."""
364 if isinstance(path
, bytes
):
368 path
= join(cwd
, path
)
369 return normpath(path
)
372 # Return a canonical path (i.e. the absolute location of a file on the
375 def realpath(filename
):
376 """Return the canonical path of the specified filename, eliminating any
377 symbolic links encountered in the path."""
378 if isinstance(filename
, bytes
):
385 bits
= [sep
] + filename
.split(sep
)[1:]
387 bits
= [empty
] + filename
.split(sep
)
389 for i
in range(2, len(bits
)+1):
390 component
= join(*bits
[0:i
])
391 # Resolve symbolic links.
392 if islink(component
):
393 resolved
= _resolve_link(component
)
395 # Infinite loop -- return original component + rest of the path
396 return abspath(join(*([component
] + bits
[i
:])))
398 newpath
= join(*([resolved
] + bits
[i
:]))
399 return realpath(newpath
)
401 return abspath(filename
)
404 def _resolve_link(path
):
405 """Internal helper function. Takes a path and follows symlinks
406 until we either arrive at something that isn't a symlink, or
407 encounter a path we've seen before (meaning that there's a loop).
411 if path
in paths_seen
:
412 # Already seen this path, so we must have a symlink loop
415 # Resolve where the link points to
416 resolved
= os
.readlink(path
)
417 if not isabs(resolved
):
419 path
= normpath(join(dir, resolved
))
421 path
= normpath(resolved
)
424 supports_unicode_filenames
= False
426 def relpath(path
, start
=None):
427 """Return a relative version of a path"""
430 raise ValueError("no path specified")
432 if isinstance(path
, bytes
):
444 start_list
= abspath(start
).split(sep
)
445 path_list
= abspath(path
).split(sep
)
447 # Work out how much of the filepath is shared by start and path.
448 i
= len(commonprefix([start_list
, path_list
]))
450 rel_list
= [pardir
] * (len(start_list
)-i
) + path_list
[i
:]
453 return join(*rel_list
)