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

1"""Common operations on Posix pathnames. 

2 

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). 

8 

9Some of this can actually be useful on non-Posix systems too, e.g. 

10for manipulation of the pathname component of URLs. 

11""" 

12 

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' 

24 

25import os 

26import sys 

27import stat 

28import genericpath 

29from genericpath import * 

30 

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"] 

39 

40 

41def _get_sep(path): 

42 if isinstance(path, bytes): 

43 return b'/' 

44 else: 

45 return '/' 

46 

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). 

51 

52def normcase(s): 

53 """Normalize case of pathname. Has no effect under Posix""" 

54 return os.fspath(s) 

55 

56 

57# Return whether a path is absolute. 

58# Trivial in Posix, harder on the Mac or MS-DOS. 

59 

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) 

65 

66 

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 '/'. 

70 

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 

93 

94 

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. 

99 

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 

110 

111 

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. 

116 

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__ 

127 

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. 

130 

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 

136 

137 

138# Return the tail (basename) part of a path, same as split(path)[1]. 

139 

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:] 

146 

147 

148# Return the head (dirname) part of a path, same as split(path)[0]. 

149 

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 

159 

160 

161# Is a path a symbolic link? 

162# This will always return false on systems where os.lstat doesn't exist. 

163 

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) 

171 

172# Being true for dangling symbolic links is also useful. 

173 

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 

181 

182 

183# Is a path a mount point? 

184# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) 

185 

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 

197 

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 

207 

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 

217 

218 

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.) 

227 

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 

272 

273 

274# Expand paths containing shell variable substitutions. 

275# This expands the forms $variable and ${variable} only. 

276# Non-existent variables are left unchanged. 

277 

278_varprog = None 

279_varprogb = None 

280 

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 

328 

329 

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! 

333 

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 

370 

371 

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) 

382 

383 

384# Return a canonical path (i.e. the absolute location of a file on the 

385# filesystem). 

386 

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) 

393 

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 = '..' 

405 

406 if isabs(rest): 

407 rest = rest[1:] 

408 path = sep 

409 

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 

443 

444 return path, True 

445 

446 

447supports_unicode_filenames = (sys.platform == 'darwin') 

448 

449def relpath(path, start=None): 

450 """Return a relative version of a path""" 

451 

452 if not path: 

453 raise ValueError("no path specified") 

454 

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 = '..' 

464 

465 if start is None: 

466 start = curdir 

467 else: 

468 start = os.fspath(start) 

469 

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])) 

475 

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 

483 

484 

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. 

489 

490def commonpath(paths): 

491 """Given a sequence of path names, returns the longest common sub-path.""" 

492 

493 if not paths: 

494 raise ValueError('commonpath() arg is an empty sequence') 

495 

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 = '.' 

503 

504 try: 

505 split_paths = [path.split(sep) for path in paths] 

506 

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 

511 

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 

520 

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