Coverage for /pythoncovmergedfiles/medio/medio/usr/lib/python3.9/posixpath.py: 8%
319 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1"""Common operations on Posix pathnames.
3Instead of importing this module directly, import os and refer to
4this module as os.path. The "os.path" name is an alias for this
5module on Posix systems; on other systems (e.g. Windows),
6os.path provides the same operations in a manner specific to that
7platform, and is an alias to another module (e.g. ntpath).
9Some of this can actually be useful on non-Posix systems too, e.g.
10for manipulation of the pathname component of URLs.
11"""
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.
16curdir = '.'
17pardir = '..'
18extsep = '.'
19sep = '/'
20pathsep = ':'
21defpath = '/bin:/usr/bin'
22altsep = None
23devnull = '/dev/null'
25import os
26import sys
27import stat
28import genericpath
29from genericpath import *
31__all__ = ["normcase","isabs","join","splitdrive","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"]
41def _get_sep(path):
42 if isinstance(path, bytes):
43 return b'/'
44 else:
45 return '/'
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).
52def normcase(s):
53 """Normalize case of pathname. Has no effect under Posix"""
54 return os.fspath(s)
57# Return whether a path is absolute.
58# Trivial in Posix, harder on the Mac or MS-DOS.
60def isabs(s):
61 """Test whether a path is absolute"""
62 s = os.fspath(s)
63 sep = _get_sep(s)
64 return s.startswith(sep)
67# Join pathnames.
68# Ignore the previous parts if a part is absolute.
69# Insert a '/' unless the first part is empty or already ends in '/'.
71def join(a, *p):
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."""
76 a = os.fspath(a)
77 sep = _get_sep(a)
78 path = a
79 try:
80 if not p:
81 path[:0] + sep #23780: Ensure compatible data type even if p is null.
82 for b in map(os.fspath, p):
83 if b.startswith(sep):
84 path = b
85 elif not path or path.endswith(sep):
86 path += b
87 else:
88 path += sep + b
89 except (TypeError, AttributeError, BytesWarning):
90 genericpath._check_arg_types('join', a, *p)
91 raise
92 return path
95# Split a path in head (everything up to the last '/') and tail (the
96# rest). If the path ends in '/', tail will be empty. If there is no
97# '/' in the path, head will be empty.
98# Trailing '/'es are stripped from head unless it is the root.
100def split(p):
101 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
102 everything after the final slash. Either part may be empty."""
103 p = os.fspath(p)
104 sep = _get_sep(p)
105 i = p.rfind(sep) + 1
106 head, tail = p[:i], p[i:]
107 if head and head != sep*len(head):
108 head = head.rstrip(sep)
109 return head, tail
112# Split a path in root and extension.
113# The extension is everything starting at the last dot in the last
114# pathname component; the root is everything before that.
115# It is always true that root + ext == p.
117def splitext(p):
118 p = os.fspath(p)
119 if isinstance(p, bytes):
120 sep = b'/'
121 extsep = b'.'
122 else:
123 sep = '/'
124 extsep = '.'
125 return genericpath._splitext(p, sep, None, extsep)
126splitext.__doc__ = genericpath._splitext.__doc__
128# Split a pathname into a drive specification and the rest of the
129# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
131def splitdrive(p):
132 """Split a pathname into drive and path. On Posix, drive is always
133 empty."""
134 p = os.fspath(p)
135 return p[:0], p
138# Return the tail (basename) part of a path, same as split(path)[1].
140def basename(p):
141 """Returns the final component of a pathname"""
142 p = os.fspath(p)
143 sep = _get_sep(p)
144 i = p.rfind(sep) + 1
145 return p[i:]
148# Return the head (dirname) part of a path, same as split(path)[0].
150def dirname(p):
151 """Returns the directory component of a pathname"""
152 p = os.fspath(p)
153 sep = _get_sep(p)
154 i = p.rfind(sep) + 1
155 head = p[:i]
156 if head and head != sep*len(head):
157 head = head.rstrip(sep)
158 return head
161# Is a path a symbolic link?
162# This will always return false on systems where os.lstat doesn't exist.
164def islink(path):
165 """Test whether a path is a symbolic link"""
166 try:
167 st = os.lstat(path)
168 except (OSError, ValueError, AttributeError):
169 return False
170 return stat.S_ISLNK(st.st_mode)
172# Being true for dangling symbolic links is also useful.
174def lexists(path):
175 """Test whether a path exists. Returns True for broken symbolic links"""
176 try:
177 os.lstat(path)
178 except (OSError, ValueError):
179 return False
180 return True
183# Is a path a mount point?
184# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
186def ismount(path):
187 """Test whether a path is a mount point"""
188 try:
189 s1 = os.lstat(path)
190 except (OSError, ValueError):
191 # It doesn't exist -- so not a mount point. :-)
192 return False
193 else:
194 # A symlink can never be a mount point
195 if stat.S_ISLNK(s1.st_mode):
196 return False
198 if isinstance(path, bytes):
199 parent = join(path, b'..')
200 else:
201 parent = join(path, '..')
202 parent = realpath(parent)
203 try:
204 s2 = os.lstat(parent)
205 except (OSError, ValueError):
206 return False
208 dev1 = s1.st_dev
209 dev2 = s2.st_dev
210 if dev1 != dev2:
211 return True # path/.. on a different device as path
212 ino1 = s1.st_ino
213 ino2 = s2.st_ino
214 if ino1 == ino2:
215 return True # path/.. is the same i-node as path
216 return False
219# Expand paths beginning with '~' or '~user'.
220# '~' means $HOME; '~user' means that user's home directory.
221# If the path doesn't begin with '~', or if the user or $HOME is unknown,
222# the path is returned unchanged (leaving error reporting to whatever
223# function is called with the expanded path as argument).
224# See also module 'glob' for expansion of *, ? and [...] in pathnames.
225# (A function should also be defined to do full *sh-style environment
226# variable expansion.)
228def expanduser(path):
229 """Expand ~ and ~user constructions. If user or $HOME is unknown,
230 do nothing."""
231 path = os.fspath(path)
232 if isinstance(path, bytes):
233 tilde = b'~'
234 else:
235 tilde = '~'
236 if not path.startswith(tilde):
237 return path
238 sep = _get_sep(path)
239 i = path.find(sep, 1)
240 if i < 0:
241 i = len(path)
242 if i == 1:
243 if 'HOME' not in os.environ:
244 import pwd
245 try:
246 userhome = pwd.getpwuid(os.getuid()).pw_dir
247 except KeyError:
248 # bpo-10496: if the current user identifier doesn't exist in the
249 # password database, return the path unchanged
250 return path
251 else:
252 userhome = os.environ['HOME']
253 else:
254 import pwd
255 name = path[1:i]
256 if isinstance(name, bytes):
257 name = str(name, 'ASCII')
258 try:
259 pwent = pwd.getpwnam(name)
260 except KeyError:
261 # bpo-10496: if the user name from the path doesn't exist in the
262 # password database, return the path unchanged
263 return path
264 userhome = pwent.pw_dir
265 if isinstance(path, bytes):
266 userhome = os.fsencode(userhome)
267 root = b'/'
268 else:
269 root = '/'
270 userhome = userhome.rstrip(root)
271 return (userhome + path[i:]) or root
274# Expand paths containing shell variable substitutions.
275# This expands the forms $variable and ${variable} only.
276# Non-existent variables are left unchanged.
278_varprog = None
279_varprogb = None
281def expandvars(path):
282 """Expand shell variables of form $var and ${var}. Unknown variables
283 are left unchanged."""
284 path = os.fspath(path)
285 global _varprog, _varprogb
286 if isinstance(path, bytes):
287 if b'$' not in path:
288 return path
289 if not _varprogb:
290 import re
291 _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
292 search = _varprogb.search
293 start = b'{'
294 end = b'}'
295 environ = getattr(os, 'environb', None)
296 else:
297 if '$' not in path:
298 return path
299 if not _varprog:
300 import re
301 _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
302 search = _varprog.search
303 start = '{'
304 end = '}'
305 environ = os.environ
306 i = 0
307 while True:
308 m = search(path, i)
309 if not m:
310 break
311 i, j = m.span(0)
312 name = m.group(1)
313 if name.startswith(start) and name.endswith(end):
314 name = name[1:-1]
315 try:
316 if environ is None:
317 value = os.fsencode(os.environ[os.fsdecode(name)])
318 else:
319 value = environ[name]
320 except KeyError:
321 i = j
322 else:
323 tail = path[j:]
324 path = path[:i] + value
325 i = len(path)
326 path += tail
327 return path
330# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
331# It should be understood that this may change the meaning of the path
332# if it contains symbolic links!
334def normpath(path):
335 """Normalize path, eliminating double slashes, etc."""
336 path = os.fspath(path)
337 if isinstance(path, bytes):
338 sep = b'/'
339 empty = b''
340 dot = b'.'
341 dotdot = b'..'
342 else:
343 sep = '/'
344 empty = ''
345 dot = '.'
346 dotdot = '..'
347 if path == empty:
348 return dot
349 initial_slashes = path.startswith(sep)
350 # POSIX allows one or two initial slashes, but treats three or more
351 # as single slash.
352 if (initial_slashes and
353 path.startswith(sep*2) and not path.startswith(sep*3)):
354 initial_slashes = 2
355 comps = path.split(sep)
356 new_comps = []
357 for comp in comps:
358 if comp in (empty, dot):
359 continue
360 if (comp != dotdot or (not initial_slashes and not new_comps) or
361 (new_comps and new_comps[-1] == dotdot)):
362 new_comps.append(comp)
363 elif new_comps:
364 new_comps.pop()
365 comps = new_comps
366 path = sep.join(comps)
367 if initial_slashes:
368 path = sep*initial_slashes + path
369 return path or dot
372def abspath(path):
373 """Return an absolute path."""
374 path = os.fspath(path)
375 if not isabs(path):
376 if isinstance(path, bytes):
377 cwd = os.getcwdb()
378 else:
379 cwd = os.getcwd()
380 path = join(cwd, path)
381 return normpath(path)
384# Return a canonical path (i.e. the absolute location of a file on the
385# filesystem).
387def realpath(filename):
388 """Return the canonical path of the specified filename, eliminating any
389symbolic links encountered in the path."""
390 filename = os.fspath(filename)
391 path, ok = _joinrealpath(filename[:0], filename, {})
392 return abspath(path)
394# Join two paths, normalizing and eliminating any symbolic links
395# encountered in the second path.
396def _joinrealpath(path, rest, seen):
397 if isinstance(path, bytes):
398 sep = b'/'
399 curdir = b'.'
400 pardir = b'..'
401 else:
402 sep = '/'
403 curdir = '.'
404 pardir = '..'
406 if isabs(rest):
407 rest = rest[1:]
408 path = sep
410 while rest:
411 name, _, rest = rest.partition(sep)
412 if not name or name == curdir:
413 # current dir
414 continue
415 if name == pardir:
416 # parent dir
417 if path:
418 path, name = split(path)
419 if name == pardir:
420 path = join(path, pardir, pardir)
421 else:
422 path = pardir
423 continue
424 newpath = join(path, name)
425 if not islink(newpath):
426 path = newpath
427 continue
428 # Resolve the symbolic link
429 if newpath in seen:
430 # Already seen this path
431 path = seen[newpath]
432 if path is not None:
433 # use cached value
434 continue
435 # The symlink is not resolved, so we must have a symlink loop.
436 # Return already resolved part + rest of the path unchanged.
437 return join(newpath, rest), False
438 seen[newpath] = None # not resolved symlink
439 path, ok = _joinrealpath(path, os.readlink(newpath), seen)
440 if not ok:
441 return join(path, rest), False
442 seen[newpath] = path # resolved symlink
444 return path, True
447supports_unicode_filenames = (sys.platform == 'darwin')
449def relpath(path, start=None):
450 """Return a relative version of a path"""
452 if not path:
453 raise ValueError("no path specified")
455 path = os.fspath(path)
456 if isinstance(path, bytes):
457 curdir = b'.'
458 sep = b'/'
459 pardir = b'..'
460 else:
461 curdir = '.'
462 sep = '/'
463 pardir = '..'
465 if start is None:
466 start = curdir
467 else:
468 start = os.fspath(start)
470 try:
471 start_list = [x for x in abspath(start).split(sep) if x]
472 path_list = [x for x in abspath(path).split(sep) if x]
473 # Work out how much of the filepath is shared by start and path.
474 i = len(commonprefix([start_list, path_list]))
476 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
477 if not rel_list:
478 return curdir
479 return join(*rel_list)
480 except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
481 genericpath._check_arg_types('relpath', path, start)
482 raise
485# Return the longest common sub-path of the sequence of paths given as input.
486# The paths are not normalized before comparing them (this is the
487# responsibility of the caller). Any trailing separator is stripped from the
488# returned path.
490def commonpath(paths):
491 """Given a sequence of path names, returns the longest common sub-path."""
493 if not paths:
494 raise ValueError('commonpath() arg is an empty sequence')
496 paths = tuple(map(os.fspath, paths))
497 if isinstance(paths[0], bytes):
498 sep = b'/'
499 curdir = b'.'
500 else:
501 sep = '/'
502 curdir = '.'
504 try:
505 split_paths = [path.split(sep) for path in paths]
507 try:
508 isabs, = set(p[:1] == sep for p in paths)
509 except ValueError:
510 raise ValueError("Can't mix absolute and relative paths") from None
512 split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
513 s1 = min(split_paths)
514 s2 = max(split_paths)
515 common = s1
516 for i, c in enumerate(s1):
517 if c != s2[i]:
518 common = s1[:i]
519 break
521 prefix = sep if isabs else sep[:0]
522 return prefix + sep.join(common)
523 except (TypeError, AttributeError):
524 genericpath._check_arg_types('commonpath', *paths)
525 raise