posixpath.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. """Common operations on Posix pathnames.
  2. Instead of importing this module directly, import os and refer to
  3. this module as os.path. The "os.path" name is an alias for this
  4. module on Posix systems; on other systems (e.g. Windows),
  5. os.path provides the same operations in a manner specific to that
  6. platform, and is an alias to another module (e.g. ntpath).
  7. Some of this can actually be useful on non-Posix systems too, e.g.
  8. for manipulation of the pathname component of URLs.
  9. """
  10. # Strings representing various path-related bits and pieces.
  11. # These are primarily for export; internally, they are hardcoded.
  12. # Should be set before imports for resolving cyclic dependency.
  13. curdir = '.'
  14. pardir = '..'
  15. extsep = '.'
  16. sep = '/'
  17. pathsep = ':'
  18. defpath = '/bin:/usr/bin'
  19. altsep = None
  20. devnull = '/dev/null'
  21. import os
  22. import sys
  23. import stat
  24. import genericpath
  25. from genericpath import *
  26. __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext",
  27. "basename","dirname","commonprefix","getsize","getmtime",
  28. "getatime","getctime","islink","exists","lexists","isdir","isfile",
  29. "ismount", "expanduser","expandvars","normpath","abspath",
  30. "samefile","sameopenfile","samestat",
  31. "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
  32. "devnull","realpath","supports_unicode_filenames","relpath",
  33. "commonpath", "isjunction"]
  34. def _get_sep(path):
  35. if isinstance(path, bytes):
  36. return b'/'
  37. else:
  38. return '/'
  39. # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
  40. # On MS-DOS this may also turn slashes into backslashes; however, other
  41. # normalizations (such as optimizing '../' away) are not allowed
  42. # (another function should be defined to do that).
  43. def normcase(s):
  44. """Normalize case of pathname. Has no effect under Posix"""
  45. return os.fspath(s)
  46. # Return whether a path is absolute.
  47. # Trivial in Posix, harder on the Mac or MS-DOS.
  48. def isabs(s):
  49. """Test whether a path is absolute"""
  50. s = os.fspath(s)
  51. sep = _get_sep(s)
  52. return s.startswith(sep)
  53. # Join pathnames.
  54. # Ignore the previous parts if a part is absolute.
  55. # Insert a '/' unless the first part is empty or already ends in '/'.
  56. def join(a, *p):
  57. """Join two or more pathname components, inserting '/' as needed.
  58. If any component is an absolute path, all previous path components
  59. will be discarded. An empty last part will result in a path that
  60. ends with a separator."""
  61. a = os.fspath(a)
  62. sep = _get_sep(a)
  63. path = a
  64. try:
  65. if not p:
  66. path[:0] + sep #23780: Ensure compatible data type even if p is null.
  67. for b in map(os.fspath, p):
  68. if b.startswith(sep):
  69. path = b
  70. elif not path or path.endswith(sep):
  71. path += b
  72. else:
  73. path += sep + b
  74. except (TypeError, AttributeError, BytesWarning):
  75. genericpath._check_arg_types('join', a, *p)
  76. raise
  77. return path
  78. # Split a path in head (everything up to the last '/') and tail (the
  79. # rest). If the path ends in '/', tail will be empty. If there is no
  80. # '/' in the path, head will be empty.
  81. # Trailing '/'es are stripped from head unless it is the root.
  82. def split(p):
  83. """Split a pathname. Returns tuple "(head, tail)" where "tail" is
  84. everything after the final slash. Either part may be empty."""
  85. p = os.fspath(p)
  86. sep = _get_sep(p)
  87. i = p.rfind(sep) + 1
  88. head, tail = p[:i], p[i:]
  89. if head and head != sep*len(head):
  90. head = head.rstrip(sep)
  91. return head, tail
  92. # Split a path in root and extension.
  93. # The extension is everything starting at the last dot in the last
  94. # pathname component; the root is everything before that.
  95. # It is always true that root + ext == p.
  96. def splitext(p):
  97. p = os.fspath(p)
  98. if isinstance(p, bytes):
  99. sep = b'/'
  100. extsep = b'.'
  101. else:
  102. sep = '/'
  103. extsep = '.'
  104. return genericpath._splitext(p, sep, None, extsep)
  105. splitext.__doc__ = genericpath._splitext.__doc__
  106. # Split a pathname into a drive specification and the rest of the
  107. # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
  108. def splitdrive(p):
  109. """Split a pathname into drive and path. On Posix, drive is always
  110. empty."""
  111. p = os.fspath(p)
  112. return p[:0], p
  113. def splitroot(p):
  114. """Split a pathname into drive, root and tail. On Posix, drive is always
  115. empty; the root may be empty, a single slash, or two slashes. The tail
  116. contains anything after the root. For example:
  117. splitroot('foo/bar') == ('', '', 'foo/bar')
  118. splitroot('/foo/bar') == ('', '/', 'foo/bar')
  119. splitroot('//foo/bar') == ('', '//', 'foo/bar')
  120. splitroot('///foo/bar') == ('', '/', '//foo/bar')
  121. """
  122. p = os.fspath(p)
  123. if isinstance(p, bytes):
  124. sep = b'/'
  125. empty = b''
  126. else:
  127. sep = '/'
  128. empty = ''
  129. if p[:1] != sep:
  130. # Relative path, e.g.: 'foo'
  131. return empty, empty, p
  132. elif p[1:2] != sep or p[2:3] == sep:
  133. # Absolute path, e.g.: '/foo', '///foo', '////foo', etc.
  134. return empty, sep, p[1:]
  135. else:
  136. # Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see
  137. # https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
  138. return empty, p[:2], p[2:]
  139. # Return the tail (basename) part of a path, same as split(path)[1].
  140. def 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. # Return the head (dirname) part of a path, same as split(path)[0].
  147. def dirname(p):
  148. """Returns the directory component of a pathname"""
  149. p = os.fspath(p)
  150. sep = _get_sep(p)
  151. i = p.rfind(sep) + 1
  152. head = p[:i]
  153. if head and head != sep*len(head):
  154. head = head.rstrip(sep)
  155. return head
  156. # Is a path a junction?
  157. def isjunction(path):
  158. """Test whether a path is a junction
  159. Junctions are not a part of posix semantics"""
  160. os.fspath(path)
  161. return False
  162. # Being true for dangling symbolic links is also useful.
  163. def lexists(path):
  164. """Test whether a path exists. Returns True for broken symbolic links"""
  165. try:
  166. os.lstat(path)
  167. except (OSError, ValueError):
  168. return False
  169. return True
  170. # Is a path a mount point?
  171. # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
  172. def ismount(path):
  173. """Test whether a path is a mount point"""
  174. try:
  175. s1 = os.lstat(path)
  176. except (OSError, ValueError):
  177. # It doesn't exist -- so not a mount point. :-)
  178. return False
  179. else:
  180. # A symlink can never be a mount point
  181. if stat.S_ISLNK(s1.st_mode):
  182. return False
  183. path = os.fspath(path)
  184. if isinstance(path, bytes):
  185. parent = join(path, b'..')
  186. else:
  187. parent = join(path, '..')
  188. parent = realpath(parent)
  189. try:
  190. s2 = os.lstat(parent)
  191. except (OSError, ValueError):
  192. return False
  193. dev1 = s1.st_dev
  194. dev2 = s2.st_dev
  195. if dev1 != dev2:
  196. return True # path/.. on a different device as path
  197. ino1 = s1.st_ino
  198. ino2 = s2.st_ino
  199. if ino1 == ino2:
  200. return True # path/.. is the same i-node as path
  201. return False
  202. # Expand paths beginning with '~' or '~user'.
  203. # '~' means $HOME; '~user' means that user's home directory.
  204. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  205. # the path is returned unchanged (leaving error reporting to whatever
  206. # function is called with the expanded path as argument).
  207. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  208. # (A function should also be defined to do full *sh-style environment
  209. # variable expansion.)
  210. def expanduser(path):
  211. """Expand ~ and ~user constructions. If user or $HOME is unknown,
  212. do nothing."""
  213. path = os.fspath(path)
  214. if isinstance(path, bytes):
  215. tilde = b'~'
  216. else:
  217. tilde = '~'
  218. if not path.startswith(tilde):
  219. return path
  220. sep = _get_sep(path)
  221. i = path.find(sep, 1)
  222. if i < 0:
  223. i = len(path)
  224. if i == 1:
  225. if 'HOME' not in os.environ:
  226. try:
  227. import pwd
  228. except ImportError:
  229. # pwd module unavailable, return path unchanged
  230. return path
  231. try:
  232. userhome = pwd.getpwuid(os.getuid()).pw_dir
  233. except KeyError:
  234. # bpo-10496: if the current user identifier doesn't exist in the
  235. # password database, return the path unchanged
  236. return path
  237. else:
  238. userhome = os.environ['HOME']
  239. else:
  240. try:
  241. import pwd
  242. except ImportError:
  243. # pwd module unavailable, return path unchanged
  244. return path
  245. name = path[1:i]
  246. if isinstance(name, bytes):
  247. name = os.fsdecode(name)
  248. try:
  249. pwent = pwd.getpwnam(name)
  250. except KeyError:
  251. # bpo-10496: if the user name from the path doesn't exist in the
  252. # password database, return the path unchanged
  253. return path
  254. userhome = pwent.pw_dir
  255. # if no user home, return the path unchanged on VxWorks
  256. if userhome is None and sys.platform == "vxworks":
  257. return path
  258. if isinstance(path, bytes):
  259. userhome = os.fsencode(userhome)
  260. root = b'/'
  261. else:
  262. root = '/'
  263. userhome = userhome.rstrip(root)
  264. return (userhome + path[i:]) or root
  265. # Expand paths containing shell variable substitutions.
  266. # This expands the forms $variable and ${variable} only.
  267. # Non-existent variables are left unchanged.
  268. _varprog = None
  269. _varprogb = None
  270. def expandvars(path):
  271. """Expand shell variables of form $var and ${var}. Unknown variables
  272. are left unchanged."""
  273. path = os.fspath(path)
  274. global _varprog, _varprogb
  275. if isinstance(path, bytes):
  276. if b'$' not in path:
  277. return path
  278. if not _varprogb:
  279. import re
  280. _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
  281. search = _varprogb.search
  282. start = b'{'
  283. end = b'}'
  284. environ = getattr(os, 'environb', None)
  285. else:
  286. if '$' not in path:
  287. return path
  288. if not _varprog:
  289. import re
  290. _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
  291. search = _varprog.search
  292. start = '{'
  293. end = '}'
  294. environ = os.environ
  295. i = 0
  296. while True:
  297. m = search(path, i)
  298. if not m:
  299. break
  300. i, j = m.span(0)
  301. name = m.group(1)
  302. if name.startswith(start) and name.endswith(end):
  303. name = name[1:-1]
  304. try:
  305. if environ is None:
  306. value = os.fsencode(os.environ[os.fsdecode(name)])
  307. else:
  308. value = environ[name]
  309. except KeyError:
  310. i = j
  311. else:
  312. tail = path[j:]
  313. path = path[:i] + value
  314. i = len(path)
  315. path += tail
  316. return path
  317. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
  318. # It should be understood that this may change the meaning of the path
  319. # if it contains symbolic links!
  320. try:
  321. from posix import _path_normpath as normpath
  322. except ImportError:
  323. def normpath(path):
  324. """Normalize path, eliminating double slashes, etc."""
  325. path = os.fspath(path)
  326. if isinstance(path, bytes):
  327. sep = b'/'
  328. empty = b''
  329. dot = b'.'
  330. dotdot = b'..'
  331. else:
  332. sep = '/'
  333. empty = ''
  334. dot = '.'
  335. dotdot = '..'
  336. if path == empty:
  337. return dot
  338. _, initial_slashes, path = splitroot(path)
  339. comps = path.split(sep)
  340. new_comps = []
  341. for comp in comps:
  342. if comp in (empty, dot):
  343. continue
  344. if (comp != dotdot or (not initial_slashes and not new_comps) or
  345. (new_comps and new_comps[-1] == dotdot)):
  346. new_comps.append(comp)
  347. elif new_comps:
  348. new_comps.pop()
  349. comps = new_comps
  350. path = initial_slashes + sep.join(comps)
  351. return path or dot
  352. def abspath(path):
  353. """Return an absolute path."""
  354. path = os.fspath(path)
  355. if not isabs(path):
  356. if isinstance(path, bytes):
  357. cwd = os.getcwdb()
  358. else:
  359. cwd = os.getcwd()
  360. path = join(cwd, path)
  361. return normpath(path)
  362. # Return a canonical path (i.e. the absolute location of a file on the
  363. # filesystem).
  364. def realpath(filename, *, strict=False):
  365. """Return the canonical path of the specified filename, eliminating any
  366. symbolic links encountered in the path."""
  367. filename = os.fspath(filename)
  368. path, ok = _joinrealpath(filename[:0], filename, strict, {})
  369. return abspath(path)
  370. # Join two paths, normalizing and eliminating any symbolic links
  371. # encountered in the second path.
  372. def _joinrealpath(path, rest, strict, seen):
  373. if isinstance(path, bytes):
  374. sep = b'/'
  375. curdir = b'.'
  376. pardir = b'..'
  377. else:
  378. sep = '/'
  379. curdir = '.'
  380. pardir = '..'
  381. if isabs(rest):
  382. rest = rest[1:]
  383. path = sep
  384. while rest:
  385. name, _, rest = rest.partition(sep)
  386. if not name or name == curdir:
  387. # current dir
  388. continue
  389. if name == pardir:
  390. # parent dir
  391. if path:
  392. path, name = split(path)
  393. if name == pardir:
  394. path = join(path, pardir, pardir)
  395. else:
  396. path = pardir
  397. continue
  398. newpath = join(path, name)
  399. try:
  400. st = os.lstat(newpath)
  401. except OSError:
  402. if strict:
  403. raise
  404. is_link = False
  405. else:
  406. is_link = stat.S_ISLNK(st.st_mode)
  407. if not is_link:
  408. path = newpath
  409. continue
  410. # Resolve the symbolic link
  411. if newpath in seen:
  412. # Already seen this path
  413. path = seen[newpath]
  414. if path is not None:
  415. # use cached value
  416. continue
  417. # The symlink is not resolved, so we must have a symlink loop.
  418. if strict:
  419. # Raise OSError(errno.ELOOP)
  420. os.stat(newpath)
  421. else:
  422. # Return already resolved part + rest of the path unchanged.
  423. return join(newpath, rest), False
  424. seen[newpath] = None # not resolved symlink
  425. path, ok = _joinrealpath(path, os.readlink(newpath), strict, seen)
  426. if not ok:
  427. return join(path, rest), False
  428. seen[newpath] = path # resolved symlink
  429. return path, True
  430. supports_unicode_filenames = (sys.platform == 'darwin')
  431. def relpath(path, start=None):
  432. """Return a relative version of a path"""
  433. if not path:
  434. raise ValueError("no path specified")
  435. path = os.fspath(path)
  436. if isinstance(path, bytes):
  437. curdir = b'.'
  438. sep = b'/'
  439. pardir = b'..'
  440. else:
  441. curdir = '.'
  442. sep = '/'
  443. pardir = '..'
  444. if start is None:
  445. start = curdir
  446. else:
  447. start = os.fspath(start)
  448. try:
  449. start_list = [x for x in abspath(start).split(sep) if x]
  450. path_list = [x for x in abspath(path).split(sep) if x]
  451. # Work out how much of the filepath is shared by start and path.
  452. i = len(commonprefix([start_list, path_list]))
  453. rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
  454. if not rel_list:
  455. return curdir
  456. return join(*rel_list)
  457. except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
  458. genericpath._check_arg_types('relpath', path, start)
  459. raise
  460. # Return the longest common sub-path of the sequence of paths given as input.
  461. # The paths are not normalized before comparing them (this is the
  462. # responsibility of the caller). Any trailing separator is stripped from the
  463. # returned path.
  464. def commonpath(paths):
  465. """Given a sequence of path names, returns the longest common sub-path."""
  466. if not paths:
  467. raise ValueError('commonpath() arg is an empty sequence')
  468. paths = tuple(map(os.fspath, paths))
  469. if isinstance(paths[0], bytes):
  470. sep = b'/'
  471. curdir = b'.'
  472. else:
  473. sep = '/'
  474. curdir = '.'
  475. try:
  476. split_paths = [path.split(sep) for path in paths]
  477. try:
  478. isabs, = set(p[:1] == sep for p in paths)
  479. except ValueError:
  480. raise ValueError("Can't mix absolute and relative paths") from None
  481. split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
  482. s1 = min(split_paths)
  483. s2 = max(split_paths)
  484. common = s1
  485. for i, c in enumerate(s1):
  486. if c != s2[i]:
  487. common = s1[:i]
  488. break
  489. prefix = sep if isabs else sep[:0]
  490. return prefix + sep.join(common)
  491. except (TypeError, AttributeError):
  492. genericpath._check_arg_types('commonpath', *paths)
  493. raise