posixpath.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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","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"]
  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. # Return the tail (basename) part of a path, same as split(path)[1].
  114. def basename(p):
  115. """Returns the final component of a pathname"""
  116. p = os.fspath(p)
  117. sep = _get_sep(p)
  118. i = p.rfind(sep) + 1
  119. return p[i:]
  120. # Return the head (dirname) part of a path, same as split(path)[0].
  121. def dirname(p):
  122. """Returns the directory component of a pathname"""
  123. p = os.fspath(p)
  124. sep = _get_sep(p)
  125. i = p.rfind(sep) + 1
  126. head = p[:i]
  127. if head and head != sep*len(head):
  128. head = head.rstrip(sep)
  129. return head
  130. # Is a path a symbolic link?
  131. # This will always return false on systems where os.lstat doesn't exist.
  132. def islink(path):
  133. """Test whether a path is a symbolic link"""
  134. try:
  135. st = os.lstat(path)
  136. except (OSError, ValueError, AttributeError):
  137. return False
  138. return stat.S_ISLNK(st.st_mode)
  139. # Being true for dangling symbolic links is also useful.
  140. def lexists(path):
  141. """Test whether a path exists. Returns True for broken symbolic links"""
  142. try:
  143. os.lstat(path)
  144. except (OSError, ValueError):
  145. return False
  146. return True
  147. # Is a path a mount point?
  148. # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
  149. def ismount(path):
  150. """Test whether a path is a mount point"""
  151. try:
  152. s1 = os.lstat(path)
  153. except (OSError, ValueError):
  154. # It doesn't exist -- so not a mount point. :-)
  155. return False
  156. else:
  157. # A symlink can never be a mount point
  158. if stat.S_ISLNK(s1.st_mode):
  159. return False
  160. path = os.fspath(path)
  161. if isinstance(path, bytes):
  162. parent = join(path, b'..')
  163. else:
  164. parent = join(path, '..')
  165. parent = realpath(parent)
  166. try:
  167. s2 = os.lstat(parent)
  168. except (OSError, ValueError):
  169. return False
  170. dev1 = s1.st_dev
  171. dev2 = s2.st_dev
  172. if dev1 != dev2:
  173. return True # path/.. on a different device as path
  174. ino1 = s1.st_ino
  175. ino2 = s2.st_ino
  176. if ino1 == ino2:
  177. return True # path/.. is the same i-node as path
  178. return False
  179. # Expand paths beginning with '~' or '~user'.
  180. # '~' means $HOME; '~user' means that user's home directory.
  181. # If the path doesn't begin with '~', or if the user or $HOME is unknown,
  182. # the path is returned unchanged (leaving error reporting to whatever
  183. # function is called with the expanded path as argument).
  184. # See also module 'glob' for expansion of *, ? and [...] in pathnames.
  185. # (A function should also be defined to do full *sh-style environment
  186. # variable expansion.)
  187. def expanduser(path):
  188. """Expand ~ and ~user constructions. If user or $HOME is unknown,
  189. do nothing."""
  190. path = os.fspath(path)
  191. if isinstance(path, bytes):
  192. tilde = b'~'
  193. else:
  194. tilde = '~'
  195. if not path.startswith(tilde):
  196. return path
  197. sep = _get_sep(path)
  198. i = path.find(sep, 1)
  199. if i < 0:
  200. i = len(path)
  201. if i == 1:
  202. if 'HOME' not in os.environ:
  203. try:
  204. import pwd
  205. except ImportError:
  206. # pwd module unavailable, return path unchanged
  207. return path
  208. try:
  209. userhome = pwd.getpwuid(os.getuid()).pw_dir
  210. except KeyError:
  211. # bpo-10496: if the current user identifier doesn't exist in the
  212. # password database, return the path unchanged
  213. return path
  214. else:
  215. userhome = os.environ['HOME']
  216. else:
  217. try:
  218. import pwd
  219. except ImportError:
  220. # pwd module unavailable, return path unchanged
  221. return path
  222. name = path[1:i]
  223. if isinstance(name, bytes):
  224. name = str(name, 'ASCII')
  225. try:
  226. pwent = pwd.getpwnam(name)
  227. except KeyError:
  228. # bpo-10496: if the user name from the path doesn't exist in the
  229. # password database, return the path unchanged
  230. return path
  231. userhome = pwent.pw_dir
  232. # if no user home, return the path unchanged on VxWorks
  233. if userhome is None and sys.platform == "vxworks":
  234. return path
  235. if isinstance(path, bytes):
  236. userhome = os.fsencode(userhome)
  237. root = b'/'
  238. else:
  239. root = '/'
  240. userhome = userhome.rstrip(root)
  241. return (userhome + path[i:]) or root
  242. # Expand paths containing shell variable substitutions.
  243. # This expands the forms $variable and ${variable} only.
  244. # Non-existent variables are left unchanged.
  245. _varprog = None
  246. _varprogb = None
  247. def expandvars(path):
  248. """Expand shell variables of form $var and ${var}. Unknown variables
  249. are left unchanged."""
  250. path = os.fspath(path)
  251. global _varprog, _varprogb
  252. if isinstance(path, bytes):
  253. if b'$' not in path:
  254. return path
  255. if not _varprogb:
  256. import re
  257. _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
  258. search = _varprogb.search
  259. start = b'{'
  260. end = b'}'
  261. environ = getattr(os, 'environb', None)
  262. else:
  263. if '$' not in path:
  264. return path
  265. if not _varprog:
  266. import re
  267. _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
  268. search = _varprog.search
  269. start = '{'
  270. end = '}'
  271. environ = os.environ
  272. i = 0
  273. while True:
  274. m = search(path, i)
  275. if not m:
  276. break
  277. i, j = m.span(0)
  278. name = m.group(1)
  279. if name.startswith(start) and name.endswith(end):
  280. name = name[1:-1]
  281. try:
  282. if environ is None:
  283. value = os.fsencode(os.environ[os.fsdecode(name)])
  284. else:
  285. value = environ[name]
  286. except KeyError:
  287. i = j
  288. else:
  289. tail = path[j:]
  290. path = path[:i] + value
  291. i = len(path)
  292. path += tail
  293. return path
  294. # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
  295. # It should be understood that this may change the meaning of the path
  296. # if it contains symbolic links!
  297. try:
  298. from posix import _path_normpath
  299. except ImportError:
  300. def normpath(path):
  301. """Normalize path, eliminating double slashes, etc."""
  302. path = os.fspath(path)
  303. if isinstance(path, bytes):
  304. sep = b'/'
  305. empty = b''
  306. dot = b'.'
  307. dotdot = b'..'
  308. else:
  309. sep = '/'
  310. empty = ''
  311. dot = '.'
  312. dotdot = '..'
  313. if path == empty:
  314. return dot
  315. initial_slashes = path.startswith(sep)
  316. # POSIX allows one or two initial slashes, but treats three or more
  317. # as single slash.
  318. # (see https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13)
  319. if (initial_slashes and
  320. path.startswith(sep*2) and not path.startswith(sep*3)):
  321. initial_slashes = 2
  322. comps = path.split(sep)
  323. new_comps = []
  324. for comp in comps:
  325. if comp in (empty, dot):
  326. continue
  327. if (comp != dotdot or (not initial_slashes and not new_comps) or
  328. (new_comps and new_comps[-1] == dotdot)):
  329. new_comps.append(comp)
  330. elif new_comps:
  331. new_comps.pop()
  332. comps = new_comps
  333. path = sep.join(comps)
  334. if initial_slashes:
  335. path = sep*initial_slashes + path
  336. return path or dot
  337. else:
  338. def normpath(path):
  339. """Normalize path, eliminating double slashes, etc."""
  340. path = os.fspath(path)
  341. if isinstance(path, bytes):
  342. return os.fsencode(_path_normpath(os.fsdecode(path))) or b"."
  343. return _path_normpath(path) or "."
  344. def abspath(path):
  345. """Return an absolute path."""
  346. path = os.fspath(path)
  347. if not isabs(path):
  348. if isinstance(path, bytes):
  349. cwd = os.getcwdb()
  350. else:
  351. cwd = os.getcwd()
  352. path = join(cwd, path)
  353. return normpath(path)
  354. # Return a canonical path (i.e. the absolute location of a file on the
  355. # filesystem).
  356. def realpath(filename, *, strict=False):
  357. """Return the canonical path of the specified filename, eliminating any
  358. symbolic links encountered in the path."""
  359. filename = os.fspath(filename)
  360. path, ok = _joinrealpath(filename[:0], filename, strict, {})
  361. return abspath(path)
  362. # Join two paths, normalizing and eliminating any symbolic links
  363. # encountered in the second path.
  364. def _joinrealpath(path, rest, strict, seen):
  365. if isinstance(path, bytes):
  366. sep = b'/'
  367. curdir = b'.'
  368. pardir = b'..'
  369. else:
  370. sep = '/'
  371. curdir = '.'
  372. pardir = '..'
  373. if isabs(rest):
  374. rest = rest[1:]
  375. path = sep
  376. while rest:
  377. name, _, rest = rest.partition(sep)
  378. if not name or name == curdir:
  379. # current dir
  380. continue
  381. if name == pardir:
  382. # parent dir
  383. if path:
  384. path, name = split(path)
  385. if name == pardir:
  386. path = join(path, pardir, pardir)
  387. else:
  388. path = pardir
  389. continue
  390. newpath = join(path, name)
  391. try:
  392. st = os.lstat(newpath)
  393. except OSError:
  394. if strict:
  395. raise
  396. is_link = False
  397. else:
  398. is_link = stat.S_ISLNK(st.st_mode)
  399. if not is_link:
  400. path = newpath
  401. continue
  402. # Resolve the symbolic link
  403. if newpath in seen:
  404. # Already seen this path
  405. path = seen[newpath]
  406. if path is not None:
  407. # use cached value
  408. continue
  409. # The symlink is not resolved, so we must have a symlink loop.
  410. if strict:
  411. # Raise OSError(errno.ELOOP)
  412. os.stat(newpath)
  413. else:
  414. # Return already resolved part + rest of the path unchanged.
  415. return join(newpath, rest), False
  416. seen[newpath] = None # not resolved symlink
  417. path, ok = _joinrealpath(path, os.readlink(newpath), strict, seen)
  418. if not ok:
  419. return join(path, rest), False
  420. seen[newpath] = path # resolved symlink
  421. return path, True
  422. supports_unicode_filenames = (sys.platform == 'darwin')
  423. def relpath(path, start=None):
  424. """Return a relative version of a path"""
  425. if not path:
  426. raise ValueError("no path specified")
  427. path = os.fspath(path)
  428. if isinstance(path, bytes):
  429. curdir = b'.'
  430. sep = b'/'
  431. pardir = b'..'
  432. else:
  433. curdir = '.'
  434. sep = '/'
  435. pardir = '..'
  436. if start is None:
  437. start = curdir
  438. else:
  439. start = os.fspath(start)
  440. try:
  441. start_list = [x for x in abspath(start).split(sep) if x]
  442. path_list = [x for x in abspath(path).split(sep) if x]
  443. # Work out how much of the filepath is shared by start and path.
  444. i = len(commonprefix([start_list, path_list]))
  445. rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
  446. if not rel_list:
  447. return curdir
  448. return join(*rel_list)
  449. except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
  450. genericpath._check_arg_types('relpath', path, start)
  451. raise
  452. # Return the longest common sub-path of the sequence of paths given as input.
  453. # The paths are not normalized before comparing them (this is the
  454. # responsibility of the caller). Any trailing separator is stripped from the
  455. # returned path.
  456. def commonpath(paths):
  457. """Given a sequence of path names, returns the longest common sub-path."""
  458. if not paths:
  459. raise ValueError('commonpath() arg is an empty sequence')
  460. paths = tuple(map(os.fspath, paths))
  461. if isinstance(paths[0], bytes):
  462. sep = b'/'
  463. curdir = b'.'
  464. else:
  465. sep = '/'
  466. curdir = '.'
  467. try:
  468. split_paths = [path.split(sep) for path in paths]
  469. try:
  470. isabs, = set(p[:1] == sep for p in paths)
  471. except ValueError:
  472. raise ValueError("Can't mix absolute and relative paths") from None
  473. split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
  474. s1 = min(split_paths)
  475. s2 = max(split_paths)
  476. common = s1
  477. for i, c in enumerate(s1):
  478. if c != s2[i]:
  479. common = s1[:i]
  480. break
  481. prefix = sep if isabs else sep[:0]
  482. return prefix + sep.join(common)
  483. except (TypeError, AttributeError):
  484. genericpath._check_arg_types('commonpath', *paths)
  485. raise