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. 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. 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.
13 # Strings representing various path-related bits and pieces.
14 # These are primarily for export; internally, they are hardcoded.
15 # Should be set before imports for resolving cyclic dependency.
21 defpath
= '/bin:/usr/bin'
29 from genericpath
import *
31 __all__
= ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
32 "basename","dirname","commonprefix","getsize","getmtime",
33 "getatime","getctime","islink","exists","lexists","isdir","isfile",
34 "ismount", "expanduser","expandvars","normpath","abspath",
35 "samefile","sameopenfile","samestat",
36 "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
37 "devnull","realpath","supports_unicode_filenames","relpath",
38 "commonpath", "isjunction","isdevdrive"]
42 if isinstance(path
, bytes
):
47 # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
48 # On MS-DOS this may also turn slashes into backslashes; however, other
49 # normalizations (such as optimizing '../' away) are not allowed
50 # (another function should be defined to do that).
53 """Normalize case of pathname. Has no effect under Posix"""
57 # Return whether a path is absolute.
58 # Trivial in Posix, harder on the Mac or MS-DOS.
61 """Test whether a path is absolute"""
64 return s
.startswith(sep
)
68 # Ignore the previous parts if a part is absolute.
69 # Insert a '/' unless the first part is empty or already ends in '/'.
72 """Join two or more pathname components, inserting '/' as needed.
73 If any component is an absolute path, all previous path components
74 will be discarded. An empty last part will result in a path that
75 ends with a separator."""
82 if b
.startswith(sep
) or not path
:
84 elif path
.endswith(sep
):
88 except (TypeError, AttributeError, BytesWarning
):
89 genericpath
._check
_arg
_types
('join', a
, *p
)
94 # Split a path in head (everything up to the last '/') and tail (the
95 # rest). If the path ends in '/', tail will be empty. If there is no
96 # '/' in the path, head will be empty.
97 # Trailing '/'es are stripped from head unless it is the root.
100 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
101 everything after the final slash. Either part may be empty."""
105 head
, tail
= p
[:i
], p
[i
:]
106 if head
and head
!= sep
*len(head
):
107 head
= head
.rstrip(sep
)
111 # Split a path in root and extension.
112 # The extension is everything starting at the last dot in the last
113 # pathname component; the root is everything before that.
114 # It is always true that root + ext == p.
118 if isinstance(p
, bytes
):
124 return genericpath
._splitext
(p
, sep
, None, extsep
)
125 splitext
.__doc
__ = genericpath
._splitext
.__doc
__
127 # Split a pathname into a drive specification and the rest of the
128 # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
131 """Split a pathname into drive and path. On Posix, drive is always
138 """Split a pathname into drive, root and tail. On Posix, drive is always
139 empty; the root may be empty, a single slash, or two slashes. The tail
140 contains anything after the root. For example:
142 splitroot('foo/bar') == ('', '', 'foo/bar')
143 splitroot('/foo/bar') == ('', '/', 'foo/bar')
144 splitroot('//foo/bar') == ('', '//', 'foo/bar')
145 splitroot('///foo/bar') == ('', '/', '//foo/bar')
148 if isinstance(p
, bytes
):
155 # Relative path, e.g.: 'foo'
156 return empty
, empty
, p
157 elif p
[1:2] != sep
or p
[2:3] == sep
:
158 # Absolute path, e.g.: '/foo', '///foo', '////foo', etc.
159 return empty
, sep
, p
[1:]
161 # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see
162 # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
163 return empty
, p
[:2], p
[2:]
166 # Return the tail (basename) part of a path, same as split(path)[1].
169 """Returns the final component of a pathname"""
176 # Return the head (dirname) part of a path, same as split(path)[0].
179 """Returns the directory component of a pathname"""
184 if head
and head
!= sep
*len(head
):
185 head
= head
.rstrip(sep
)
189 # Is a path a mount point?
190 # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
193 """Test whether a path is a mount point"""
196 except (OSError, ValueError):
197 # It doesn't exist -- so not a mount point. :-)
200 # A symlink can never be a mount point
201 if stat
.S_ISLNK(s1
.st_mode
):
204 path
= os
.fspath(path
)
205 if isinstance(path
, bytes
):
206 parent
= join(path
, b
'..')
208 parent
= join(path
, '..')
210 s2
= os
.lstat(parent
)
212 parent
= realpath(parent
)
214 s2
= os
.lstat(parent
)
218 # path/.. on a different device as path or the same i-node as path
219 return s1
.st_dev
!= s2
.st_dev
or s1
.st_ino
== s2
.st_ino
222 # Expand paths beginning with '~' or '~user'.
223 # '~' means $HOME; '~user' means that user's home directory.
224 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
225 # the path is returned unchanged (leaving error reporting to whatever
226 # function is called with the expanded path as argument).
227 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
228 # (A function should also be defined to do full *sh-style environment
229 # variable expansion.)
231 def expanduser(path
):
232 """Expand ~ and ~user constructions. If user or $HOME is unknown,
234 path
= os
.fspath(path
)
235 if isinstance(path
, bytes
):
239 if not path
.startswith(tilde
):
242 i
= path
.find(sep
, 1)
246 if 'HOME' not in os
.environ
:
250 # pwd module unavailable, return path unchanged
253 userhome
= pwd
.getpwuid(os
.getuid()).pw_dir
255 # bpo-10496: if the current user identifier doesn't exist in the
256 # password database, return the path unchanged
259 userhome
= os
.environ
['HOME']
264 # pwd module unavailable, return path unchanged
267 if isinstance(name
, bytes
):
268 name
= os
.fsdecode(name
)
270 pwent
= pwd
.getpwnam(name
)
272 # bpo-10496: if the user name from the path doesn't exist in the
273 # password database, return the path unchanged
275 userhome
= pwent
.pw_dir
276 # if no user home, return the path unchanged on VxWorks
277 if userhome
is None and sys
.platform
== "vxworks":
279 if isinstance(path
, bytes
):
280 userhome
= os
.fsencode(userhome
)
284 userhome
= userhome
.rstrip(root
)
285 return (userhome
+ path
[i
:]) or root
288 # Expand paths containing shell variable substitutions.
289 # This expands the forms $variable and ${variable} only.
290 # Non-existent variables are left unchanged.
295 def expandvars(path
):
296 """Expand shell variables of form $var and ${var}. Unknown variables
297 are left unchanged."""
298 path
= os
.fspath(path
)
299 global _varprog
, _varprogb
300 if isinstance(path
, bytes
):
305 _varprogb
= re
.compile(br
'\$(\w+|\{[^}]*\})', re
.ASCII
)
306 search
= _varprogb
.search
309 environ
= getattr(os
, 'environb', None)
315 _varprog
= re
.compile(r
'\$(\w+|\{[^}]*\})', re
.ASCII
)
316 search
= _varprog
.search
327 if name
.startswith(start
) and name
.endswith(end
):
331 value
= os
.fsencode(os
.environ
[os
.fsdecode(name
)])
333 value
= environ
[name
]
338 path
= path
[:i
] + value
344 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
345 # It should be understood that this may change the meaning of the path
346 # if it contains symbolic links!
349 from posix
import _path_normpath
353 """Normalize path, eliminating double slashes, etc."""
354 path
= os
.fspath(path
)
355 if isinstance(path
, bytes
):
365 _
, initial_slashes
, path
= splitroot(path
)
366 comps
= path
.split(sep
)
369 if not comp
or comp
== dot
:
371 if (comp
!= dotdot
or (not initial_slashes
and not new_comps
) or
372 (new_comps
and new_comps
[-1] == dotdot
)):
373 new_comps
.append(comp
)
377 path
= initial_slashes
+ sep
.join(comps
)
382 """Normalize path, eliminating double slashes, etc."""
383 path
= os
.fspath(path
)
384 if isinstance(path
, bytes
):
385 return os
.fsencode(_path_normpath(os
.fsdecode(path
))) or b
"."
386 return _path_normpath(path
) or "."
390 """Return an absolute path."""
391 path
= os
.fspath(path
)
392 if isinstance(path
, bytes
):
393 if not path
.startswith(b
'/'):
394 path
= join(os
.getcwdb(), path
)
396 if not path
.startswith('/'):
397 path
= join(os
.getcwd(), path
)
398 return normpath(path
)
401 # Return a canonical path (i.e. the absolute location of a file on the
404 def realpath(filename
, *, strict
=False):
405 """Return the canonical path of the specified filename, eliminating any
406 symbolic links encountered in the path."""
407 filename
= os
.fspath(filename
)
408 if isinstance(filename
, bytes
):
419 # The stack of unresolved path parts. When popped, a special value of None
420 # indicates that a symlink target has been resolved, and that the original
421 # symlink path can be retrieved by popping again. The [::-1] slice is a
422 # very fast way of spelling list(reversed(...)).
423 rest
= filename
.split(sep
)[::-1]
425 # The resolved path, which is absolute throughout this function.
426 # Note: getcwd() returns a normalized and symlink-free path.
427 path
= sep
if filename
.startswith(sep
) else getcwd()
429 # Mapping from symlink paths to *fully resolved* symlink targets. If a
430 # symlink is encountered but not yet resolved, the value is None. This is
431 # used both to detect symlink loops and to speed up repeated traversals of
438 # resolved symlink target
439 seen
[rest
.pop()] = path
441 if not name
or name
== curdir
:
446 path
= path
[:path
.rindex(sep
)] or sep
449 newpath
= path
+ name
451 newpath
= path
+ sep
+ name
453 st
= os
.lstat(newpath
)
454 if not stat
.S_ISLNK(st
.st_mode
):
462 # Resolve the symbolic link
464 # Already seen this path
469 # The symlink is not resolved, so we must have a symlink loop.
471 # Raise OSError(errno.ELOOP)
475 seen
[newpath
] = None # not resolved symlink
476 target
= os
.readlink(newpath
)
477 if target
.startswith(sep
):
478 # Symlink target is absolute; reset resolved path.
480 # Push the symlink path onto the stack, and signal its specialness by
481 # also pushing None. When these entries are popped, we'll record the
482 # fully-resolved symlink target in the 'seen' mapping.
485 # Push the unresolved symlink target parts onto the stack.
486 rest
.extend(target
.split(sep
)[::-1])
491 supports_unicode_filenames
= (sys
.platform
== 'darwin')
493 def relpath(path
, start
=None):
494 """Return a relative version of a path"""
496 path
= os
.fspath(path
)
498 raise ValueError("no path specified")
500 if isinstance(path
, bytes
):
512 start
= os
.fspath(start
)
515 start_list
= [x
for x
in abspath(start
).split(sep
) if x
]
516 path_list
= [x
for x
in abspath(path
).split(sep
) if x
]
517 # Work out how much of the filepath is shared by start and path.
518 i
= len(commonprefix([start_list
, path_list
]))
520 rel_list
= [pardir
] * (len(start_list
)-i
) + path_list
[i
:]
523 return join(*rel_list
)
524 except (TypeError, AttributeError, BytesWarning
, DeprecationWarning):
525 genericpath
._check
_arg
_types
('relpath', path
, start
)
529 # Return the longest common sub-path of the sequence of paths given as input.
530 # The paths are not normalized before comparing them (this is the
531 # responsibility of the caller). Any trailing separator is stripped from the
534 def commonpath(paths
):
535 """Given a sequence of path names, returns the longest common sub-path."""
537 paths
= tuple(map(os
.fspath
, paths
))
540 raise ValueError('commonpath() arg is an empty sequence')
542 if isinstance(paths
[0], bytes
):
550 split_paths
= [path
.split(sep
) for path
in paths
]
553 isabs
, = set(p
[:1] == sep
for p
in paths
)
555 raise ValueError("Can't mix absolute and relative paths") from None
557 split_paths
= [[c
for c
in s
if c
and c
!= curdir
] for s
in split_paths
]
558 s1
= min(split_paths
)
559 s2
= max(split_paths
)
561 for i
, c
in enumerate(s1
):
566 prefix
= sep
if isabs
else sep
[:0]
567 return prefix
+ sep
.join(common
)
568 except (TypeError, AttributeError):
569 genericpath
._check
_arg
_types
('commonpath', *paths
)