| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | import fnmatch | 
					
						
							|  |  |  | import functools | 
					
						
							|  |  |  | import io | 
					
						
							|  |  |  | import ntpath | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import posixpath | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2017-09-26 00:55:55 +03:00
										 |  |  | from _collections_abc import Sequence | 
					
						
							| 
									
										
										
										
											2019-05-21 19:44:40 +02:00
										 |  |  | from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | from operator import attrgetter | 
					
						
							|  |  |  | from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO | 
					
						
							| 
									
										
										
										
											2013-12-03 09:41:35 +01:00
										 |  |  | from urllib.parse import quote_from_bytes as urlquote_from_bytes | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | supports_symlinks = True | 
					
						
							| 
									
										
										
										
											2014-11-19 00:32:08 +01:00
										 |  |  | if os.name == 'nt': | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     import nt | 
					
						
							|  |  |  |     if sys.getwindowsversion()[:2] >= (6, 0): | 
					
						
							|  |  |  |         from nt import _getfinalpathname | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         supports_symlinks = False | 
					
						
							|  |  |  |         _getfinalpathname = None | 
					
						
							| 
									
										
										
										
											2014-11-19 00:32:08 +01:00
										 |  |  | else: | 
					
						
							|  |  |  |     nt = None | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __all__ = [ | 
					
						
							|  |  |  |     "PurePath", "PurePosixPath", "PureWindowsPath", | 
					
						
							|  |  |  |     "Path", "PosixPath", "WindowsPath", | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Internals | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-06 14:57:17 -04:00
										 |  |  | # EBADF - guard against macOS `stat` throwing EBADF | 
					
						
							| 
									
										
										
										
											2019-05-21 19:44:40 +02:00
										 |  |  | _IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP) | 
					
						
							| 
									
										
										
										
											2018-08-27 23:33:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  | _IGNORED_WINERRORS = ( | 
					
						
							|  |  |  |     21,  # ERROR_NOT_READY - drive exists but is not accessible | 
					
						
							| 
									
										
										
										
											2019-05-21 19:44:40 +02:00
										 |  |  |     1921,  # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _ignore_error(exception): | 
					
						
							|  |  |  |     return (getattr(exception, 'errno', None) in _IGNORED_ERROS or | 
					
						
							|  |  |  |             getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | def _is_wildcard_pattern(pat): | 
					
						
							|  |  |  |     # Whether this pattern needs actual matching using fnmatch, or can | 
					
						
							|  |  |  |     # be looked up directly as a file. | 
					
						
							|  |  |  |     return "*" in pat or "?" in pat or "[" in pat | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Flavour(object): | 
					
						
							|  |  |  |     """A flavour implements a particular (platform-specific) set of path
 | 
					
						
							|  |  |  |     semantics."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.join = self.sep.join | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def parse_parts(self, parts): | 
					
						
							|  |  |  |         parsed = [] | 
					
						
							|  |  |  |         sep = self.sep | 
					
						
							|  |  |  |         altsep = self.altsep | 
					
						
							|  |  |  |         drv = root = '' | 
					
						
							|  |  |  |         it = reversed(parts) | 
					
						
							|  |  |  |         for part in it: | 
					
						
							|  |  |  |             if not part: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if altsep: | 
					
						
							|  |  |  |                 part = part.replace(altsep, sep) | 
					
						
							|  |  |  |             drv, root, rel = self.splitroot(part) | 
					
						
							|  |  |  |             if sep in rel: | 
					
						
							|  |  |  |                 for x in reversed(rel.split(sep)): | 
					
						
							|  |  |  |                     if x and x != '.': | 
					
						
							|  |  |  |                         parsed.append(sys.intern(x)) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 if rel and rel != '.': | 
					
						
							|  |  |  |                     parsed.append(sys.intern(rel)) | 
					
						
							|  |  |  |             if drv or root: | 
					
						
							|  |  |  |                 if not drv: | 
					
						
							|  |  |  |                     # If no drive is present, try to find one in the previous | 
					
						
							|  |  |  |                     # parts. This makes the result of parsing e.g. | 
					
						
							|  |  |  |                     # ("C:", "/", "a") reasonably intuitive. | 
					
						
							|  |  |  |                     for part in it: | 
					
						
							| 
									
										
										
										
											2015-02-15 18:03:59 +01:00
										 |  |  |                         if not part: | 
					
						
							|  |  |  |                             continue | 
					
						
							|  |  |  |                         if altsep: | 
					
						
							|  |  |  |                             part = part.replace(altsep, sep) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                         drv = self.splitroot(part)[0] | 
					
						
							|  |  |  |                         if drv: | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         if drv or root: | 
					
						
							|  |  |  |             parsed.append(drv + root) | 
					
						
							|  |  |  |         parsed.reverse() | 
					
						
							|  |  |  |         return drv, root, parsed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Join the two paths represented by the respective | 
					
						
							|  |  |  |         (drive, root, parts) tuples.  Return a new (drive, root, parts) tuple. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if root2: | 
					
						
							| 
									
										
										
										
											2013-12-06 17:14:12 +02:00
										 |  |  |             if not drv2 and drv: | 
					
						
							|  |  |  |                 return drv, root2, [drv + root2] + parts2[1:] | 
					
						
							|  |  |  |         elif drv2: | 
					
						
							|  |  |  |             if drv2 == drv or self.casefold(drv2) == self.casefold(drv): | 
					
						
							|  |  |  |                 # Same drive => second path is relative to the first | 
					
						
							|  |  |  |                 return drv, root, parts + parts2[1:] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2013-12-06 17:14:12 +02:00
										 |  |  |             # Second path is non-anchored (common case) | 
					
						
							|  |  |  |             return drv, root, parts + parts2 | 
					
						
							|  |  |  |         return drv2, root2, parts2 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _WindowsFlavour(_Flavour): | 
					
						
							|  |  |  |     # Reference for Windows paths can be found at | 
					
						
							|  |  |  |     # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sep = '\\' | 
					
						
							|  |  |  |     altsep = '/' | 
					
						
							|  |  |  |     has_drv = True | 
					
						
							|  |  |  |     pathmod = ntpath | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-19 00:32:08 +01:00
										 |  |  |     is_supported = (os.name == 'nt') | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-18 07:35:54 -07:00
										 |  |  |     drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     ext_namespace_prefix = '\\\\?\\' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     reserved_names = ( | 
					
						
							|  |  |  |         {'CON', 'PRN', 'AUX', 'NUL'} | | 
					
						
							|  |  |  |         {'COM%d' % i for i in range(1, 10)} | | 
					
						
							|  |  |  |         {'LPT%d' % i for i in range(1, 10)} | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Interesting findings about extended paths: | 
					
						
							|  |  |  |     # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported | 
					
						
							|  |  |  |     #   but '\\?\c:/a' is not | 
					
						
							|  |  |  |     # - extended paths are always absolute; "relative" extended paths will | 
					
						
							|  |  |  |     #   fail. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def splitroot(self, part, sep=sep): | 
					
						
							|  |  |  |         first = part[0:1] | 
					
						
							|  |  |  |         second = part[1:2] | 
					
						
							|  |  |  |         if (second == sep and first == sep): | 
					
						
							|  |  |  |             # XXX extended paths should also disable the collapsing of "." | 
					
						
							|  |  |  |             # components (according to MSDN docs). | 
					
						
							|  |  |  |             prefix, part = self._split_extended_path(part) | 
					
						
							|  |  |  |             first = part[0:1] | 
					
						
							|  |  |  |             second = part[1:2] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             prefix = '' | 
					
						
							|  |  |  |         third = part[2:3] | 
					
						
							|  |  |  |         if (second == sep and first == sep and third != sep): | 
					
						
							|  |  |  |             # is a UNC path: | 
					
						
							|  |  |  |             # vvvvvvvvvvvvvvvvvvvvv root | 
					
						
							|  |  |  |             # \\machine\mountpoint\directory\etc\... | 
					
						
							|  |  |  |             #            directory ^^^^^^^^^^^^^^ | 
					
						
							|  |  |  |             index = part.find(sep, 2) | 
					
						
							|  |  |  |             if index != -1: | 
					
						
							|  |  |  |                 index2 = part.find(sep, index + 1) | 
					
						
							|  |  |  |                 # a UNC path can't have two slashes in a row | 
					
						
							|  |  |  |                 # (after the initial two) | 
					
						
							|  |  |  |                 if index2 != index + 1: | 
					
						
							|  |  |  |                     if index2 == -1: | 
					
						
							|  |  |  |                         index2 = len(part) | 
					
						
							|  |  |  |                     if prefix: | 
					
						
							|  |  |  |                         return prefix + part[1:index2], sep, part[index2+1:] | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         return part[:index2], sep, part[index2+1:] | 
					
						
							|  |  |  |         drv = root = '' | 
					
						
							|  |  |  |         if second == ':' and first in self.drive_letters: | 
					
						
							|  |  |  |             drv = part[:2] | 
					
						
							|  |  |  |             part = part[2:] | 
					
						
							|  |  |  |             first = third | 
					
						
							|  |  |  |         if first == sep: | 
					
						
							|  |  |  |             root = first | 
					
						
							|  |  |  |             part = part.lstrip(sep) | 
					
						
							|  |  |  |         return prefix + drv, root, part | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def casefold(self, s): | 
					
						
							|  |  |  |         return s.lower() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def casefold_parts(self, parts): | 
					
						
							|  |  |  |         return [p.lower() for p in parts] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def compile_pattern(self, pattern): | 
					
						
							|  |  |  |         return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |     def resolve(self, path, strict=False): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         s = str(path) | 
					
						
							|  |  |  |         if not s: | 
					
						
							|  |  |  |             return os.getcwd() | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |         previous_s = None | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         if _getfinalpathname is not None: | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |             if strict: | 
					
						
							|  |  |  |                 return self._ext_to_normal(_getfinalpathname(s)) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2017-06-07 17:29:17 +02:00
										 |  |  |                 tail_parts = []  # End of the path after the first one not found | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |                 while True: | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         s = self._ext_to_normal(_getfinalpathname(s)) | 
					
						
							|  |  |  |                     except FileNotFoundError: | 
					
						
							|  |  |  |                         previous_s = s | 
					
						
							| 
									
										
										
										
											2017-06-07 17:29:17 +02:00
										 |  |  |                         s, tail = os.path.split(s) | 
					
						
							|  |  |  |                         tail_parts.append(tail) | 
					
						
							| 
									
										
										
										
											2016-12-28 16:02:59 -08:00
										 |  |  |                         if previous_s == s: | 
					
						
							|  |  |  |                             return path | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2017-06-07 17:29:17 +02:00
										 |  |  |                         return os.path.join(s, *reversed(tail_parts)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         # Means fallback on absolute | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): | 
					
						
							|  |  |  |         prefix = '' | 
					
						
							|  |  |  |         if s.startswith(ext_prefix): | 
					
						
							|  |  |  |             prefix = s[:4] | 
					
						
							|  |  |  |             s = s[4:] | 
					
						
							|  |  |  |             if s.startswith('UNC\\'): | 
					
						
							|  |  |  |                 prefix += s[:3] | 
					
						
							|  |  |  |                 s = '\\' + s[3:] | 
					
						
							|  |  |  |         return prefix, s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _ext_to_normal(self, s): | 
					
						
							|  |  |  |         # Turn back an extended path into a normal DOS-like path | 
					
						
							|  |  |  |         return self._split_extended_path(s)[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_reserved(self, parts): | 
					
						
							|  |  |  |         # NOTE: the rules for reserved names seem somewhat complicated | 
					
						
							|  |  |  |         # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). | 
					
						
							|  |  |  |         # We err on the side of caution and return True for paths which are | 
					
						
							|  |  |  |         # not considered reserved by Windows. | 
					
						
							|  |  |  |         if not parts: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         if parts[0].startswith('\\\\'): | 
					
						
							|  |  |  |             # UNC paths are never reserved | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         return parts[-1].partition('.')[0].upper() in self.reserved_names | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def make_uri(self, path): | 
					
						
							|  |  |  |         # Under Windows, file URIs use the UTF-8 encoding. | 
					
						
							|  |  |  |         drive = path.drive | 
					
						
							|  |  |  |         if len(drive) == 2 and drive[1] == ':': | 
					
						
							|  |  |  |             # It's a path on a local drive => 'file:///c:/a/b' | 
					
						
							|  |  |  |             rest = path.as_posix()[2:].lstrip('/') | 
					
						
							|  |  |  |             return 'file:///%s/%s' % ( | 
					
						
							|  |  |  |                 drive, urlquote_from_bytes(rest.encode('utf-8'))) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # It's a path on a network drive => 'file://host/share/a/b' | 
					
						
							|  |  |  |             return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 20:54:45 +01:00
										 |  |  |     def gethomedir(self, username): | 
					
						
							| 
									
										
										
										
											2020-01-28 10:41:50 +01:00
										 |  |  |         if 'USERPROFILE' in os.environ: | 
					
						
							| 
									
										
										
										
											2014-12-30 20:54:45 +01:00
										 |  |  |             userhome = os.environ['USERPROFILE'] | 
					
						
							|  |  |  |         elif 'HOMEPATH' in os.environ: | 
					
						
							| 
									
										
										
										
											2014-12-30 22:09:42 +01:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 drv = os.environ['HOMEDRIVE'] | 
					
						
							|  |  |  |             except KeyError: | 
					
						
							|  |  |  |                 drv = '' | 
					
						
							|  |  |  |             userhome = drv + os.environ['HOMEPATH'] | 
					
						
							| 
									
										
										
										
											2014-12-30 20:54:45 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             raise RuntimeError("Can't determine home directory") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if username: | 
					
						
							|  |  |  |             # Try to guess user home directory.  By default all users | 
					
						
							|  |  |  |             # directories are located in the same place and are named by | 
					
						
							|  |  |  |             # corresponding usernames.  If current user home directory points | 
					
						
							|  |  |  |             # to nonstandard place, this guess is likely wrong. | 
					
						
							|  |  |  |             if os.environ['USERNAME'] != username: | 
					
						
							|  |  |  |                 drv, root, parts = self.parse_parts((userhome,)) | 
					
						
							|  |  |  |                 if parts[-1] != os.environ['USERNAME']: | 
					
						
							|  |  |  |                     raise RuntimeError("Can't determine home directory " | 
					
						
							|  |  |  |                                        "for %r" % username) | 
					
						
							|  |  |  |                 parts[-1] = username | 
					
						
							|  |  |  |                 if drv or root: | 
					
						
							|  |  |  |                     userhome = drv + root + self.join(parts[1:]) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     userhome = self.join(parts) | 
					
						
							|  |  |  |         return userhome | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _PosixFlavour(_Flavour): | 
					
						
							|  |  |  |     sep = '/' | 
					
						
							|  |  |  |     altsep = '' | 
					
						
							|  |  |  |     has_drv = False | 
					
						
							|  |  |  |     pathmod = posixpath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     is_supported = (os.name != 'nt') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def splitroot(self, part, sep=sep): | 
					
						
							|  |  |  |         if part and part[0] == sep: | 
					
						
							|  |  |  |             stripped_part = part.lstrip(sep) | 
					
						
							|  |  |  |             # According to POSIX path resolution: | 
					
						
							|  |  |  |             # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 | 
					
						
							|  |  |  |             # "A pathname that begins with two successive slashes may be | 
					
						
							|  |  |  |             # interpreted in an implementation-defined manner, although more | 
					
						
							|  |  |  |             # than two leading slashes shall be treated as a single slash". | 
					
						
							|  |  |  |             if len(part) - len(stripped_part) == 2: | 
					
						
							|  |  |  |                 return '', sep * 2, stripped_part | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 return '', sep, stripped_part | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return '', '', part | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def casefold(self, s): | 
					
						
							|  |  |  |         return s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def casefold_parts(self, parts): | 
					
						
							|  |  |  |         return parts | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def compile_pattern(self, pattern): | 
					
						
							|  |  |  |         return re.compile(fnmatch.translate(pattern)).fullmatch | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |     def resolve(self, path, strict=False): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         sep = self.sep | 
					
						
							|  |  |  |         accessor = path._accessor | 
					
						
							| 
									
										
										
										
											2013-12-16 19:57:41 +01:00
										 |  |  |         seen = {} | 
					
						
							|  |  |  |         def _resolve(path, rest): | 
					
						
							|  |  |  |             if rest.startswith(sep): | 
					
						
							|  |  |  |                 path = '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for name in rest.split(sep): | 
					
						
							|  |  |  |                 if not name or name == '.': | 
					
						
							|  |  |  |                     # current dir | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if name == '..': | 
					
						
							|  |  |  |                     # parent dir | 
					
						
							|  |  |  |                     path, _, _ = path.rpartition(sep) | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2020-08-26 17:51:44 -07:00
										 |  |  |                 if path.endswith(sep): | 
					
						
							|  |  |  |                     newpath = path + name | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     newpath = path + sep + name | 
					
						
							| 
									
										
										
										
											2013-12-16 19:57:41 +01:00
										 |  |  |                 if newpath in seen: | 
					
						
							|  |  |  |                     # Already seen this path | 
					
						
							|  |  |  |                     path = seen[newpath] | 
					
						
							|  |  |  |                     if path is not None: | 
					
						
							|  |  |  |                         # use cached value | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     # The symlink is not resolved, so we must have a symlink loop. | 
					
						
							|  |  |  |                     raise RuntimeError("Symlink loop from %r" % newpath) | 
					
						
							|  |  |  |                 # Resolve the symbolic link | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     target = accessor.readlink(newpath) | 
					
						
							|  |  |  |                 except OSError as e: | 
					
						
							| 
									
										
										
										
											2017-06-07 17:29:17 +02:00
										 |  |  |                     if e.errno != EINVAL and strict: | 
					
						
							|  |  |  |                         raise | 
					
						
							|  |  |  |                     # Not a symlink, or non-strict mode. We just leave the path | 
					
						
							|  |  |  |                     # untouched. | 
					
						
							| 
									
										
										
										
											2013-12-16 19:57:41 +01:00
										 |  |  |                     path = newpath | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     seen[newpath] = None # not resolved symlink | 
					
						
							|  |  |  |                     path = _resolve(path, target) | 
					
						
							|  |  |  |                     seen[newpath] = path # resolved symlink | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return path | 
					
						
							|  |  |  |         # NOTE: according to POSIX, getcwd() cannot contain path components | 
					
						
							|  |  |  |         # which are symlinks. | 
					
						
							|  |  |  |         base = '' if path.is_absolute() else os.getcwd() | 
					
						
							|  |  |  |         return _resolve(base, str(path)) or sep | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_reserved(self, parts): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def make_uri(self, path): | 
					
						
							|  |  |  |         # We represent the path using the local filesystem encoding, | 
					
						
							|  |  |  |         # for portability to other applications. | 
					
						
							|  |  |  |         bpath = bytes(path) | 
					
						
							|  |  |  |         return 'file://' + urlquote_from_bytes(bpath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 20:54:45 +01:00
										 |  |  |     def gethomedir(self, username): | 
					
						
							|  |  |  |         if not username: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return os.environ['HOME'] | 
					
						
							|  |  |  |             except KeyError: | 
					
						
							|  |  |  |                 import pwd | 
					
						
							|  |  |  |                 return pwd.getpwuid(os.getuid()).pw_dir | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             import pwd | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return pwd.getpwnam(username).pw_dir | 
					
						
							|  |  |  |             except KeyError: | 
					
						
							|  |  |  |                 raise RuntimeError("Can't determine home directory " | 
					
						
							|  |  |  |                                    "for %r" % username) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | _windows_flavour = _WindowsFlavour() | 
					
						
							|  |  |  | _posix_flavour = _PosixFlavour() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Accessor: | 
					
						
							|  |  |  |     """An accessor implements a particular (system-specific or not) way of
 | 
					
						
							|  |  |  |     accessing paths on the filesystem."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _NormalAccessor(_Accessor): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     stat = os.stat | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     lstat = os.lstat | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     open = os.open | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     listdir = os.listdir | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     scandir = os.scandir | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     chmod = os.chmod | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if hasattr(os, "lchmod"): | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |         lchmod = os.lchmod | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     else: | 
					
						
							|  |  |  |         def lchmod(self, pathobj, mode): | 
					
						
							|  |  |  |             raise NotImplementedError("lchmod() not available on this system") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     mkdir = os.mkdir | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     unlink = os.unlink | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 13:23:55 +01:00
										 |  |  |     if hasattr(os, "link"): | 
					
						
							|  |  |  |         link_to = os.link | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         @staticmethod | 
					
						
							|  |  |  |         def link_to(self, target): | 
					
						
							|  |  |  |             raise NotImplementedError("os.link() not available on this system") | 
					
						
							| 
									
										
										
										
											2019-05-04 11:27:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     rmdir = os.rmdir | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     rename = os.rename | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     replace = os.replace | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if nt: | 
					
						
							|  |  |  |         if supports_symlinks: | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |             symlink = os.symlink | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             def symlink(a, b, target_is_directory): | 
					
						
							|  |  |  |                 raise NotImplementedError("symlink() not available on this system") | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # Under POSIX, os.symlink() takes two args | 
					
						
							|  |  |  |         @staticmethod | 
					
						
							|  |  |  |         def symlink(a, b, target_is_directory): | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |             return os.symlink(a, b) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |     utime = os.utime | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Helper for resolve() | 
					
						
							|  |  |  |     def readlink(self, path): | 
					
						
							|  |  |  |         return os.readlink(path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:41:07 +01:00
										 |  |  |     def owner(self, path): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             import pwd | 
					
						
							|  |  |  |             return pwd.getpwuid(self.stat(path).st_uid).pw_name | 
					
						
							|  |  |  |         except ImportError: | 
					
						
							|  |  |  |             raise NotImplementedError("Path.owner() is unsupported on this system") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def group(self, path): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             import grp | 
					
						
							|  |  |  |             return grp.getgrgid(self.stat(path).st_gid).gr_name | 
					
						
							|  |  |  |         except ImportError: | 
					
						
							|  |  |  |             raise NotImplementedError("Path.group() is unsupported on this system") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | _normal_accessor = _NormalAccessor() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Globbing helpers | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  | def _make_selector(pattern_parts, flavour): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     pat = pattern_parts[0] | 
					
						
							|  |  |  |     child_parts = pattern_parts[1:] | 
					
						
							|  |  |  |     if pat == '**': | 
					
						
							|  |  |  |         cls = _RecursiveWildcardSelector | 
					
						
							|  |  |  |     elif '**' in pat: | 
					
						
							|  |  |  |         raise ValueError("Invalid pattern: '**' can only be an entire path component") | 
					
						
							|  |  |  |     elif _is_wildcard_pattern(pat): | 
					
						
							|  |  |  |         cls = _WildcardSelector | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         cls = _PreciseSelector | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     return cls(pat, child_parts, flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | if hasattr(functools, "lru_cache"): | 
					
						
							|  |  |  |     _make_selector = functools.lru_cache()(_make_selector) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Selector: | 
					
						
							|  |  |  |     """A selector matches a specific glob pattern part against the children
 | 
					
						
							|  |  |  |     of a given path."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def __init__(self, child_parts, flavour): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         self.child_parts = child_parts | 
					
						
							|  |  |  |         if child_parts: | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |             self.successor = _make_selector(child_parts, flavour) | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             self.dironly = True | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             self.successor = _TerminatingSelector() | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             self.dironly = False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def select_from(self, parent_path): | 
					
						
							|  |  |  |         """Iterate over all child paths of `parent_path` matched by this
 | 
					
						
							|  |  |  |         selector.  This can contain parent_path itself."""
 | 
					
						
							|  |  |  |         path_cls = type(parent_path) | 
					
						
							|  |  |  |         is_dir = path_cls.is_dir | 
					
						
							|  |  |  |         exists = path_cls.exists | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |         scandir = parent_path._accessor.scandir | 
					
						
							|  |  |  |         if not is_dir(parent_path): | 
					
						
							|  |  |  |             return iter([]) | 
					
						
							|  |  |  |         return self._select_from(parent_path, is_dir, exists, scandir) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _TerminatingSelector: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |     def _select_from(self, parent_path, is_dir, exists, scandir): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         yield parent_path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PreciseSelector(_Selector): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def __init__(self, name, child_parts, flavour): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         self.name = name | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |         _Selector.__init__(self, child_parts, flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |     def _select_from(self, parent_path, is_dir, exists, scandir): | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |         try: | 
					
						
							|  |  |  |             path = parent_path._make_child_relpath(self.name) | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             if (is_dir if self.dironly else exists)(path): | 
					
						
							|  |  |  |                 for p in self.successor._select_from(path, is_dir, exists, scandir): | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |                     yield p | 
					
						
							|  |  |  |         except PermissionError: | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _WildcardSelector(_Selector): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def __init__(self, pat, child_parts, flavour): | 
					
						
							|  |  |  |         self.match = flavour.compile_pattern(pat) | 
					
						
							|  |  |  |         _Selector.__init__(self, child_parts, flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |     def _select_from(self, parent_path, is_dir, exists, scandir): | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-03-11 18:42:03 +02:00
										 |  |  |             with scandir(parent_path) as scandir_it: | 
					
						
							|  |  |  |                 entries = list(scandir_it) | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             for entry in entries: | 
					
						
							| 
									
										
										
										
											2020-03-07 17:53:20 +00:00
										 |  |  |                 if self.dironly: | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         # "entry.is_dir()" can raise PermissionError | 
					
						
							|  |  |  |                         # in some cases (see bpo-38894), which is not | 
					
						
							|  |  |  |                         # among the errors ignored by _ignore_error() | 
					
						
							|  |  |  |                         if not entry.is_dir(): | 
					
						
							|  |  |  |                             continue | 
					
						
							|  |  |  |                     except OSError as e: | 
					
						
							|  |  |  |                         if not _ignore_error(e): | 
					
						
							|  |  |  |                             raise | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                 name = entry.name | 
					
						
							|  |  |  |                 if self.match(name): | 
					
						
							|  |  |  |                     path = parent_path._make_child_relpath(name) | 
					
						
							|  |  |  |                     for p in self.successor._select_from(path, is_dir, exists, scandir): | 
					
						
							|  |  |  |                         yield p | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |         except PermissionError: | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _RecursiveWildcardSelector(_Selector): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |     def __init__(self, pat, child_parts, flavour): | 
					
						
							|  |  |  |         _Selector.__init__(self, child_parts, flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |     def _iterate_directories(self, parent_path, is_dir, scandir): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         yield parent_path | 
					
						
							| 
									
										
										
										
											2016-01-07 10:56:36 -08:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-03-11 18:42:03 +02:00
										 |  |  |             with scandir(parent_path) as scandir_it: | 
					
						
							|  |  |  |                 entries = list(scandir_it) | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             for entry in entries: | 
					
						
							| 
									
										
										
										
											2018-08-27 23:33:45 +02:00
										 |  |  |                 entry_is_dir = False | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     entry_is_dir = entry.is_dir() | 
					
						
							|  |  |  |                 except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |                     if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2018-08-27 23:33:45 +02:00
										 |  |  |                         raise | 
					
						
							|  |  |  |                 if entry_is_dir and not entry.is_symlink(): | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |                     path = parent_path._make_child_relpath(entry.name) | 
					
						
							|  |  |  |                     for p in self._iterate_directories(path, is_dir, scandir): | 
					
						
							| 
									
										
										
										
											2016-01-07 10:56:36 -08:00
										 |  |  |                         yield p | 
					
						
							|  |  |  |         except PermissionError: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |     def _select_from(self, parent_path, is_dir, exists, scandir): | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2016-09-07 10:58:05 +03:00
										 |  |  |             yielded = set() | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 successor_select = self.successor._select_from | 
					
						
							|  |  |  |                 for starting_point in self._iterate_directories(parent_path, is_dir, scandir): | 
					
						
							|  |  |  |                     for p in successor_select(starting_point, is_dir, exists, scandir): | 
					
						
							|  |  |  |                         if p not in yielded: | 
					
						
							|  |  |  |                             yield p | 
					
						
							|  |  |  |                             yielded.add(p) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 yielded.clear() | 
					
						
							| 
									
										
										
										
											2016-01-06 09:42:07 -08:00
										 |  |  |         except PermissionError: | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Public API | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _PathParents(Sequence): | 
					
						
							|  |  |  |     """This object provides sequence-like access to the logical ancestors
 | 
					
						
							|  |  |  |     of a path.  Don't try to construct it yourself.""" | 
					
						
							|  |  |  |     __slots__ = ('_pathcls', '_drv', '_root', '_parts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, path): | 
					
						
							|  |  |  |         # We don't store the instance to avoid reference cycles | 
					
						
							|  |  |  |         self._pathcls = type(path) | 
					
						
							|  |  |  |         self._drv = path._drv | 
					
						
							|  |  |  |         self._root = path._root | 
					
						
							|  |  |  |         self._parts = path._parts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __len__(self): | 
					
						
							|  |  |  |         if self._drv or self._root: | 
					
						
							|  |  |  |             return len(self._parts) - 1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return len(self._parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __getitem__(self, idx): | 
					
						
							|  |  |  |         if idx < 0 or idx >= len(self): | 
					
						
							|  |  |  |             raise IndexError(idx) | 
					
						
							|  |  |  |         return self._pathcls._from_parsed_parts(self._drv, self._root, | 
					
						
							|  |  |  |                                                 self._parts[:-idx - 1]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return "<{}.parents>".format(self._pathcls.__name__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PurePath(object): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """Base class for manipulating paths without I/O.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PurePath represents a filesystem path and offers operations which | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     don't imply any actual filesystem I/O.  Depending on your system, | 
					
						
							|  |  |  |     instantiating a PurePath will return either a PurePosixPath or a | 
					
						
							|  |  |  |     PureWindowsPath object.  You can also instantiate either of these classes | 
					
						
							|  |  |  |     directly, regardless of your system. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     __slots__ = ( | 
					
						
							|  |  |  |         '_drv', '_root', '_parts', | 
					
						
							|  |  |  |         '_str', '_hash', '_pparts', '_cached_cparts', | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __new__(cls, *args): | 
					
						
							|  |  |  |         """Construct a PurePath from one or several strings and or existing
 | 
					
						
							|  |  |  |         PurePath objects.  The strings and path objects are combined so as | 
					
						
							|  |  |  |         to yield a canonicalized path, which is incorporated into the | 
					
						
							|  |  |  |         new PurePath object. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if cls is PurePath: | 
					
						
							|  |  |  |             cls = PureWindowsPath if os.name == 'nt' else PurePosixPath | 
					
						
							|  |  |  |         return cls._from_parts(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __reduce__(self): | 
					
						
							|  |  |  |         # Using the parts tuple helps share interned path parts | 
					
						
							|  |  |  |         # when pickling related paths. | 
					
						
							|  |  |  |         return (self.__class__, tuple(self._parts)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def _parse_args(cls, args): | 
					
						
							|  |  |  |         # This is useful when you don't want to create an instance, just | 
					
						
							|  |  |  |         # canonicalize some constructor arguments. | 
					
						
							|  |  |  |         parts = [] | 
					
						
							|  |  |  |         for a in args: | 
					
						
							|  |  |  |             if isinstance(a, PurePath): | 
					
						
							|  |  |  |                 parts += a._parts | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2016-06-10 12:20:49 -07:00
										 |  |  |                 a = os.fspath(a) | 
					
						
							|  |  |  |                 if isinstance(a, str): | 
					
						
							|  |  |  |                     # Force-cast str subclasses to str (issue #21127) | 
					
						
							|  |  |  |                     parts.append(str(a)) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise TypeError( | 
					
						
							|  |  |  |                         "argument should be a str object or an os.PathLike " | 
					
						
							|  |  |  |                         "object returning str, not %r" | 
					
						
							|  |  |  |                         % type(a)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         return cls._flavour.parse_parts(parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def _from_parts(cls, args, init=True): | 
					
						
							|  |  |  |         # We need to call _parse_args on the instance, so as to get the | 
					
						
							|  |  |  |         # right flavour. | 
					
						
							|  |  |  |         self = object.__new__(cls) | 
					
						
							|  |  |  |         drv, root, parts = self._parse_args(args) | 
					
						
							|  |  |  |         self._drv = drv | 
					
						
							|  |  |  |         self._root = root | 
					
						
							|  |  |  |         self._parts = parts | 
					
						
							|  |  |  |         if init: | 
					
						
							|  |  |  |             self._init() | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def _from_parsed_parts(cls, drv, root, parts, init=True): | 
					
						
							|  |  |  |         self = object.__new__(cls) | 
					
						
							|  |  |  |         self._drv = drv | 
					
						
							|  |  |  |         self._root = root | 
					
						
							|  |  |  |         self._parts = parts | 
					
						
							|  |  |  |         if init: | 
					
						
							|  |  |  |             self._init() | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def _format_parsed_parts(cls, drv, root, parts): | 
					
						
							|  |  |  |         if drv or root: | 
					
						
							|  |  |  |             return drv + root + cls._flavour.join(parts[1:]) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return cls._flavour.join(parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _init(self): | 
					
						
							| 
									
										
										
										
											2016-06-02 10:07:09 +00:00
										 |  |  |         # Overridden in concrete Path | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_child(self, args): | 
					
						
							|  |  |  |         drv, root, parts = self._parse_args(args) | 
					
						
							|  |  |  |         drv, root, parts = self._flavour.join_parsed_parts( | 
					
						
							|  |  |  |             self._drv, self._root, self._parts, drv, root, parts) | 
					
						
							|  |  |  |         return self._from_parsed_parts(drv, root, parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							|  |  |  |         """Return the string representation of the path, suitable for
 | 
					
						
							|  |  |  |         passing to system calls."""
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return self._str | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             self._str = self._format_parsed_parts(self._drv, self._root, | 
					
						
							|  |  |  |                                                   self._parts) or '.' | 
					
						
							|  |  |  |             return self._str | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 12:20:49 -07:00
										 |  |  |     def __fspath__(self): | 
					
						
							|  |  |  |         return str(self) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def as_posix(self): | 
					
						
							|  |  |  |         """Return the string representation of the path with forward (/)
 | 
					
						
							|  |  |  |         slashes."""
 | 
					
						
							|  |  |  |         f = self._flavour | 
					
						
							|  |  |  |         return str(self).replace(f.sep, '/') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __bytes__(self): | 
					
						
							|  |  |  |         """Return the bytes representation of the path.  This is only
 | 
					
						
							|  |  |  |         recommended to use under Unix."""
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |         return os.fsencode(self) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return "{}({!r})".format(self.__class__.__name__, self.as_posix()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def as_uri(self): | 
					
						
							|  |  |  |         """Return the path as a 'file' URI.""" | 
					
						
							|  |  |  |         if not self.is_absolute(): | 
					
						
							|  |  |  |             raise ValueError("relative path can't be expressed as a file URI") | 
					
						
							|  |  |  |         return self._flavour.make_uri(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def _cparts(self): | 
					
						
							|  |  |  |         # Cached casefolded parts, for hashing and comparison | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return self._cached_cparts | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             self._cached_cparts = self._flavour.casefold_parts(self._parts) | 
					
						
							|  |  |  |             return self._cached_cparts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, PurePath): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return self._cparts == other._cparts and self._flavour is other._flavour | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return self._hash | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             self._hash = hash(tuple(self._cparts)) | 
					
						
							|  |  |  |             return self._hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __lt__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return self._cparts < other._cparts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __le__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return self._cparts <= other._cparts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __gt__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return self._cparts > other._cparts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __ge__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, PurePath) or self._flavour is not other._flavour: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return self._cparts >= other._cparts | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-08 23:31:15 +03:00
										 |  |  |     def __class_getitem__(cls, type): | 
					
						
							|  |  |  |         return cls | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     drive = property(attrgetter('_drv'), | 
					
						
							|  |  |  |                      doc="""The drive prefix (letter or UNC path), if any.""") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     root = property(attrgetter('_root'), | 
					
						
							|  |  |  |                     doc="""The root of the path, if any.""") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def anchor(self): | 
					
						
							|  |  |  |         """The concatenation of the drive and root, or ''.""" | 
					
						
							|  |  |  |         anchor = self._drv + self._root | 
					
						
							|  |  |  |         return anchor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def name(self): | 
					
						
							|  |  |  |         """The final path component, if any.""" | 
					
						
							|  |  |  |         parts = self._parts | 
					
						
							|  |  |  |         if len(parts) == (1 if (self._drv or self._root) else 0): | 
					
						
							|  |  |  |             return '' | 
					
						
							|  |  |  |         return parts[-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def suffix(self): | 
					
						
							| 
									
										
										
										
											2019-11-02 18:46:24 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         The final component's last suffix, if any. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This includes the leading period. For example: '.txt' | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         name = self.name | 
					
						
							|  |  |  |         i = name.rfind('.') | 
					
						
							|  |  |  |         if 0 < i < len(name) - 1: | 
					
						
							|  |  |  |             return name[i:] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def suffixes(self): | 
					
						
							| 
									
										
										
										
											2019-11-02 18:46:24 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         A list of the final component's suffixes, if any. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These include the leading periods. For example: ['.tar', '.gz'] | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         name = self.name | 
					
						
							|  |  |  |         if name.endswith('.'): | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |         name = name.lstrip('.') | 
					
						
							|  |  |  |         return ['.' + suffix for suffix in name.split('.')[1:]] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def stem(self): | 
					
						
							|  |  |  |         """The final path component, minus its last suffix.""" | 
					
						
							|  |  |  |         name = self.name | 
					
						
							|  |  |  |         i = name.rfind('.') | 
					
						
							|  |  |  |         if 0 < i < len(name) - 1: | 
					
						
							|  |  |  |             return name[:i] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def with_name(self, name): | 
					
						
							|  |  |  |         """Return a new path with the file name changed.""" | 
					
						
							|  |  |  |         if not self.name: | 
					
						
							|  |  |  |             raise ValueError("%r has an empty name" % (self,)) | 
					
						
							| 
									
										
										
										
											2014-07-06 21:31:12 -04:00
										 |  |  |         drv, root, parts = self._flavour.parse_parts((name,)) | 
					
						
							|  |  |  |         if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] | 
					
						
							|  |  |  |             or drv or root or len(parts) != 1): | 
					
						
							|  |  |  |             raise ValueError("Invalid name %r" % (name)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         return self._from_parsed_parts(self._drv, self._root, | 
					
						
							|  |  |  |                                        self._parts[:-1] + [name]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-19 17:29:49 +02:00
										 |  |  |     def with_stem(self, stem): | 
					
						
							|  |  |  |         """Return a new path with the stem changed.""" | 
					
						
							|  |  |  |         return self.with_name(stem + self.suffix) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def with_suffix(self, suffix): | 
					
						
							| 
									
										
										
										
											2018-08-03 22:49:42 +02:00
										 |  |  |         """Return a new path with the file suffix changed.  If the path
 | 
					
						
							|  |  |  |         has no suffix, add given suffix.  If the given suffix is an empty | 
					
						
							|  |  |  |         string, remove the suffix from the path. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2014-07-06 21:37:15 -04:00
										 |  |  |         f = self._flavour | 
					
						
							|  |  |  |         if f.sep in suffix or f.altsep and f.altsep in suffix: | 
					
						
							| 
									
										
										
										
											2018-08-11 08:45:06 +03:00
										 |  |  |             raise ValueError("Invalid suffix %r" % (suffix,)) | 
					
						
							| 
									
										
										
										
											2014-07-06 21:37:15 -04:00
										 |  |  |         if suffix and not suffix.startswith('.') or suffix == '.': | 
					
						
							| 
									
										
										
										
											2014-01-03 00:07:17 +01:00
										 |  |  |             raise ValueError("Invalid suffix %r" % (suffix)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         name = self.name | 
					
						
							|  |  |  |         if not name: | 
					
						
							|  |  |  |             raise ValueError("%r has an empty name" % (self,)) | 
					
						
							|  |  |  |         old_suffix = self.suffix | 
					
						
							|  |  |  |         if not old_suffix: | 
					
						
							|  |  |  |             name = name + suffix | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             name = name[:-len(old_suffix)] + suffix | 
					
						
							|  |  |  |         return self._from_parsed_parts(self._drv, self._root, | 
					
						
							|  |  |  |                                        self._parts[:-1] + [name]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def relative_to(self, *other): | 
					
						
							|  |  |  |         """Return the relative path to another path identified by the passed
 | 
					
						
							|  |  |  |         arguments.  If the operation is not possible (because this is not | 
					
						
							|  |  |  |         a subpath of the other path), raise ValueError. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # For the purpose of this method, drive and root are considered | 
					
						
							|  |  |  |         # separate parts, i.e.: | 
					
						
							|  |  |  |         #   Path('c:/').relative_to('c:')  gives Path('/') | 
					
						
							|  |  |  |         #   Path('c:/').relative_to('/')   raise ValueError | 
					
						
							|  |  |  |         if not other: | 
					
						
							|  |  |  |             raise TypeError("need at least one argument") | 
					
						
							|  |  |  |         parts = self._parts | 
					
						
							|  |  |  |         drv = self._drv | 
					
						
							|  |  |  |         root = self._root | 
					
						
							| 
									
										
										
										
											2013-12-28 19:49:04 +01:00
										 |  |  |         if root: | 
					
						
							|  |  |  |             abs_parts = [drv, root] + parts[1:] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             abs_parts = parts | 
					
						
							|  |  |  |         to_drv, to_root, to_parts = self._parse_args(other) | 
					
						
							| 
									
										
										
										
											2013-12-28 19:49:04 +01:00
										 |  |  |         if to_root: | 
					
						
							|  |  |  |             to_abs_parts = [to_drv, to_root] + to_parts[1:] | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         else: | 
					
						
							|  |  |  |             to_abs_parts = to_parts | 
					
						
							|  |  |  |         n = len(to_abs_parts) | 
					
						
							| 
									
										
										
										
											2013-12-28 19:49:04 +01:00
										 |  |  |         cf = self._flavour.casefold_parts | 
					
						
							|  |  |  |         if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |             formatted = self._format_parsed_parts(to_drv, to_root, to_parts) | 
					
						
							| 
									
										
										
										
											2020-05-25 13:01:20 -07:00
										 |  |  |             raise ValueError("{!r} is not in the subpath of {!r}" | 
					
						
							|  |  |  |                     " OR one path is relative and the other is absolute." | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                              .format(str(self), str(formatted))) | 
					
						
							| 
									
										
										
										
											2013-12-28 19:49:04 +01:00
										 |  |  |         return self._from_parsed_parts('', root if n == 1 else '', | 
					
						
							|  |  |  |                                        abs_parts[n:]) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 14:54:02 -05:00
										 |  |  |     def is_relative_to(self, *other): | 
					
						
							|  |  |  |         """Return True if the path is relative to another path or False.
 | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.relative_to(*other) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def parts(self): | 
					
						
							|  |  |  |         """An object providing sequence-like access to the
 | 
					
						
							|  |  |  |         components in the filesystem path."""
 | 
					
						
							|  |  |  |         # We cache the tuple to avoid building a new one each time .parts | 
					
						
							|  |  |  |         # is accessed.  XXX is this necessary? | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return self._pparts | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             self._pparts = tuple(self._parts) | 
					
						
							|  |  |  |             return self._pparts | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def joinpath(self, *args): | 
					
						
							|  |  |  |         """Combine this path with one or several arguments, and return a
 | 
					
						
							|  |  |  |         new path representing either a subpath (if all arguments are relative | 
					
						
							|  |  |  |         paths) or a totally different path (if one of the arguments is | 
					
						
							|  |  |  |         anchored). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._make_child(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __truediv__(self, key): | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         try: | 
					
						
							|  |  |  |             return self._make_child((key,)) | 
					
						
							|  |  |  |         except TypeError: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __rtruediv__(self, key): | 
					
						
							| 
									
										
										
										
											2019-08-08 01:41:10 -04:00
										 |  |  |         try: | 
					
						
							|  |  |  |             return self._from_parts([key] + self._parts) | 
					
						
							|  |  |  |         except TypeError: | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def parent(self): | 
					
						
							|  |  |  |         """The logical parent of the path.""" | 
					
						
							|  |  |  |         drv = self._drv | 
					
						
							|  |  |  |         root = self._root | 
					
						
							|  |  |  |         parts = self._parts | 
					
						
							|  |  |  |         if len(parts) == 1 and (drv or root): | 
					
						
							|  |  |  |             return self | 
					
						
							|  |  |  |         return self._from_parsed_parts(drv, root, parts[:-1]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def parents(self): | 
					
						
							|  |  |  |         """A sequence of this path's logical parents.""" | 
					
						
							|  |  |  |         return _PathParents(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_absolute(self): | 
					
						
							|  |  |  |         """True if the path is absolute (has both a root and, if applicable,
 | 
					
						
							|  |  |  |         a drive)."""
 | 
					
						
							|  |  |  |         if not self._root: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         return not self._flavour.has_drv or bool(self._drv) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_reserved(self): | 
					
						
							|  |  |  |         """Return True if the path contains one of the special names reserved
 | 
					
						
							|  |  |  |         by the system, if any."""
 | 
					
						
							|  |  |  |         return self._flavour.is_reserved(self._parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def match(self, path_pattern): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return True if this path matches the given pattern. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         cf = self._flavour.casefold | 
					
						
							|  |  |  |         path_pattern = cf(path_pattern) | 
					
						
							|  |  |  |         drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) | 
					
						
							|  |  |  |         if not pat_parts: | 
					
						
							|  |  |  |             raise ValueError("empty pattern") | 
					
						
							|  |  |  |         if drv and drv != cf(self._drv): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         if root and root != cf(self._root): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         parts = self._cparts | 
					
						
							|  |  |  |         if drv or root: | 
					
						
							|  |  |  |             if len(pat_parts) != len(parts): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |             pat_parts = pat_parts[1:] | 
					
						
							|  |  |  |         elif len(pat_parts) > len(parts): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         for part, pat in zip(reversed(parts), reversed(pat_parts)): | 
					
						
							|  |  |  |             if not fnmatch.fnmatchcase(part, pat): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 12:20:49 -07:00
										 |  |  | # Can't subclass os.PathLike from PurePath and keep the constructor | 
					
						
							|  |  |  | # optimizations in PurePath._parse_args(). | 
					
						
							|  |  |  | os.PathLike.register(PurePath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class PurePosixPath(PurePath): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """PurePath subclass for non-Windows systems.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     On a POSIX system, instantiating a PurePath should return this object. | 
					
						
							|  |  |  |     However, you can also instantiate it directly on any system. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     _flavour = _posix_flavour | 
					
						
							|  |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PureWindowsPath(PurePath): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """PurePath subclass for Windows systems.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     On a Windows system, instantiating a PurePath should return this object. | 
					
						
							|  |  |  |     However, you can also instantiate it directly on any system. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     _flavour = _windows_flavour | 
					
						
							|  |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Filesystem-accessing classes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Path(PurePath): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """PurePath subclass that can make system calls.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Path represents a filesystem path but unlike PurePath, also offers | 
					
						
							|  |  |  |     methods to do system calls on path objects. Depending on your system, | 
					
						
							|  |  |  |     instantiating a Path will return either a PosixPath or a WindowsPath | 
					
						
							|  |  |  |     object. You can also instantiate a PosixPath or WindowsPath directly, | 
					
						
							|  |  |  |     but cannot instantiate a WindowsPath on a POSIX system or vice versa. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     __slots__ = ( | 
					
						
							|  |  |  |         '_accessor', | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __new__(cls, *args, **kwargs): | 
					
						
							|  |  |  |         if cls is Path: | 
					
						
							|  |  |  |             cls = WindowsPath if os.name == 'nt' else PosixPath | 
					
						
							|  |  |  |         self = cls._from_parts(args, init=False) | 
					
						
							|  |  |  |         if not self._flavour.is_supported: | 
					
						
							|  |  |  |             raise NotImplementedError("cannot instantiate %r on your system" | 
					
						
							|  |  |  |                                       % (cls.__name__,)) | 
					
						
							|  |  |  |         self._init() | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _init(self, | 
					
						
							|  |  |  |               # Private non-constructor arguments | 
					
						
							|  |  |  |               template=None, | 
					
						
							|  |  |  |               ): | 
					
						
							|  |  |  |         if template is not None: | 
					
						
							|  |  |  |             self._accessor = template._accessor | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._accessor = _normal_accessor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_child_relpath(self, part): | 
					
						
							|  |  |  |         # This is an optimization used for dir walking.  `part` must be | 
					
						
							|  |  |  |         # a single part relative to this path. | 
					
						
							|  |  |  |         parts = self._parts + [part] | 
					
						
							|  |  |  |         return self._from_parsed_parts(self._drv, self._root, parts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, t, v, tb): | 
					
						
							| 
									
										
										
										
											2020-04-01 15:10:51 +01:00
										 |  |  |         # https://bugs.python.org/issue39682 | 
					
						
							|  |  |  |         # In previous versions of pathlib, this method marked this path as | 
					
						
							|  |  |  |         # closed; subsequent attempts to perform I/O would raise an IOError. | 
					
						
							|  |  |  |         # This functionality was never documented, and had the effect of | 
					
						
							|  |  |  |         # making Path objects mutable, contrary to PEP 428. In Python 3.9 the | 
					
						
							|  |  |  |         # _closed attribute was removed, and this method made a no-op. | 
					
						
							|  |  |  |         # This method and __enter__()/__exit__() should be deprecated and | 
					
						
							|  |  |  |         # removed in the future. | 
					
						
							|  |  |  |         pass | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _opener(self, name, flags, mode=0o666): | 
					
						
							|  |  |  |         # A stub for the opener argument to built-in open() | 
					
						
							|  |  |  |         return self._accessor.open(self, flags, mode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-02 21:25:18 +01:00
										 |  |  |     def _raw_open(self, flags, mode=0o777): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file pointed by this path and return a file descriptor, | 
					
						
							|  |  |  |         as os.open() does. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._accessor.open(self, flags, mode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     # Public API | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def cwd(cls): | 
					
						
							|  |  |  |         """Return a new path pointing to the current working directory
 | 
					
						
							|  |  |  |         (as returned by os.getcwd()). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return cls(os.getcwd()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-12 21:03:41 +01:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def home(cls): | 
					
						
							|  |  |  |         """Return a new path pointing to the user's home directory (as
 | 
					
						
							|  |  |  |         returned by os.path.expanduser('~')). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return cls(cls()._flavour.gethomedir(None)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-13 10:50:15 +02:00
										 |  |  |     def samefile(self, other_path): | 
					
						
							| 
									
										
										
										
											2015-10-22 03:34:16 +03:00
										 |  |  |         """Return whether other_path is the same or not as this file
 | 
					
						
							| 
									
										
										
										
											2015-10-21 20:10:24 +03:00
										 |  |  |         (as returned by os.path.samefile()). | 
					
						
							| 
									
										
										
										
											2014-05-13 10:50:15 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         st = self.stat() | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             other_st = other_path.stat() | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							| 
									
										
										
										
											2020-04-17 18:47:27 +01:00
										 |  |  |             other_st = self._accessor.stat(other_path) | 
					
						
							| 
									
										
										
										
											2014-05-13 10:50:15 +02:00
										 |  |  |         return os.path.samestat(st, other_st) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def iterdir(self): | 
					
						
							|  |  |  |         """Iterate over the files in this directory.  Does not yield any
 | 
					
						
							|  |  |  |         result for the special paths '.' and '..'. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         for name in self._accessor.listdir(self): | 
					
						
							|  |  |  |             if name in {'.', '..'}: | 
					
						
							|  |  |  |                 # Yielding a path object for these makes little sense | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             yield self._make_child_relpath(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def glob(self, pattern): | 
					
						
							|  |  |  |         """Iterate over this subtree and yield all existing files (of any
 | 
					
						
							| 
									
										
										
										
											2019-02-11 11:47:09 +01:00
										 |  |  |         kind, including directories) matching the given relative pattern. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-02-12 12:11:34 +02:00
										 |  |  |         sys.audit("pathlib.Path.glob", self, pattern) | 
					
						
							| 
									
										
										
										
											2016-01-30 17:50:48 +02:00
										 |  |  |         if not pattern: | 
					
						
							|  |  |  |             raise ValueError("Unacceptable pattern: {!r}".format(pattern)) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | 
					
						
							|  |  |  |         if drv or root: | 
					
						
							|  |  |  |             raise NotImplementedError("Non-relative patterns are unsupported") | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |         selector = _make_selector(tuple(pattern_parts), self._flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         for p in selector.select_from(self): | 
					
						
							|  |  |  |             yield p | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def rglob(self, pattern): | 
					
						
							|  |  |  |         """Recursively yield all existing files (of any kind, including
 | 
					
						
							| 
									
										
										
										
											2019-02-11 11:47:09 +01:00
										 |  |  |         directories) matching the given relative pattern, anywhere in | 
					
						
							|  |  |  |         this subtree. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-02-12 12:11:34 +02:00
										 |  |  |         sys.audit("pathlib.Path.rglob", self, pattern) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) | 
					
						
							|  |  |  |         if drv or root: | 
					
						
							|  |  |  |             raise NotImplementedError("Non-relative patterns are unsupported") | 
					
						
							| 
									
										
										
										
											2019-10-21 20:37:15 +03:00
										 |  |  |         selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         for p in selector.select_from(self): | 
					
						
							|  |  |  |             yield p | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def absolute(self): | 
					
						
							|  |  |  |         """Return an absolute version of this path.  This function works
 | 
					
						
							|  |  |  |         even if the path doesn't point to anything. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         No normalization is done, i.e. all '.' and '..' will be kept along. | 
					
						
							|  |  |  |         Use resolve() to get the canonical path to a file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # XXX untested yet! | 
					
						
							|  |  |  |         if self.is_absolute(): | 
					
						
							|  |  |  |             return self | 
					
						
							|  |  |  |         # FIXME this must defer to the specific flavour (and, under Windows, | 
					
						
							|  |  |  |         # use nt._getfullpathname()) | 
					
						
							|  |  |  |         obj = self._from_parts([os.getcwd()] + self._parts, init=False) | 
					
						
							|  |  |  |         obj._init(template=self) | 
					
						
							|  |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |     def resolve(self, strict=False): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Make the path absolute, resolving all symlinks on the way and also | 
					
						
							|  |  |  |         normalizing it (for example turning slashes into backslashes under | 
					
						
							|  |  |  |         Windows). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2016-11-09 12:58:17 -08:00
										 |  |  |         s = self._flavour.resolve(self, strict=strict) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         if s is None: | 
					
						
							|  |  |  |             # No symlink resolution => for consistency, raise an error if | 
					
						
							|  |  |  |             # the path doesn't exist or is forbidden | 
					
						
							|  |  |  |             self.stat() | 
					
						
							|  |  |  |             s = str(self.absolute()) | 
					
						
							|  |  |  |         # Now we have no symlinks in the path, it's safe to normalize it. | 
					
						
							|  |  |  |         normed = self._flavour.pathmod.normpath(s) | 
					
						
							|  |  |  |         obj = self._from_parts((normed,), init=False) | 
					
						
							|  |  |  |         obj._init(template=self) | 
					
						
							|  |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def stat(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return the result of the stat() system call on this path, like | 
					
						
							|  |  |  |         os.stat() does. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._accessor.stat(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def owner(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return the login name of the file owner. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:41:07 +01:00
										 |  |  |         return self._accessor.owner(self) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def group(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return the group name of the file gid. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:41:07 +01:00
										 |  |  |         return self._accessor.group(self) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def open(self, mode='r', buffering=-1, encoding=None, | 
					
						
							|  |  |  |              errors=None, newline=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file pointed by this path and return a file object, as | 
					
						
							|  |  |  |         the built-in open() function does. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-03-25 13:42:11 +02:00
										 |  |  |         return io.open(self, mode, buffering, encoding, errors, newline, | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                        opener=self._opener) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-01 19:12:33 +02:00
										 |  |  |     def read_bytes(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in bytes mode, read it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         with self.open(mode='rb') as f: | 
					
						
							|  |  |  |             return f.read() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def read_text(self, encoding=None, errors=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in text mode, read it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         with self.open(mode='r', encoding=encoding, errors=errors) as f: | 
					
						
							|  |  |  |             return f.read() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_bytes(self, data): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in bytes mode, write to it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # type-check for the buffer interface before truncating the file | 
					
						
							|  |  |  |         view = memoryview(data) | 
					
						
							|  |  |  |         with self.open(mode='wb') as f: | 
					
						
							|  |  |  |             return f.write(view) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_text(self, data, encoding=None, errors=None): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Open the file in text mode, write to it, and close the file. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if not isinstance(data, str): | 
					
						
							|  |  |  |             raise TypeError('data must be str, not %s' % | 
					
						
							|  |  |  |                             data.__class__.__name__) | 
					
						
							|  |  |  |         with self.open(mode='w', encoding=encoding, errors=errors) as f: | 
					
						
							|  |  |  |             return f.write(data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 14:18:40 -07:00
										 |  |  |     def readlink(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Return the path to which the symbolic link points. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         path = self._accessor.readlink(self) | 
					
						
							|  |  |  |         obj = self._from_parts((path,), init=False) | 
					
						
							|  |  |  |         obj._init(template=self) | 
					
						
							|  |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def touch(self, mode=0o666, exist_ok=True): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Create this file with the given access mode, if it doesn't exist. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if exist_ok: | 
					
						
							|  |  |  |             # First try to bump modification time | 
					
						
							|  |  |  |             # Implementation note: GNU touch uses the UTIME_NOW option of | 
					
						
							|  |  |  |             # the utimensat() / futimens() functions. | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2013-11-23 15:25:59 +01:00
										 |  |  |                 self._accessor.utime(self, None) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |             except OSError: | 
					
						
							|  |  |  |                 # Avoid exception chaining | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |         flags = os.O_CREAT | os.O_WRONLY | 
					
						
							|  |  |  |         if not exist_ok: | 
					
						
							|  |  |  |             flags |= os.O_EXCL | 
					
						
							|  |  |  |         fd = self._raw_open(flags, mode) | 
					
						
							|  |  |  |         os.close(fd) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-05 11:28:12 -04:00
										 |  |  |     def mkdir(self, mode=0o777, parents=False, exist_ok=False): | 
					
						
							| 
									
										
										
										
											2017-03-24 20:51:53 +02:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Create a new directory at this given path. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._accessor.mkdir(self, mode) | 
					
						
							|  |  |  |         except FileNotFoundError: | 
					
						
							|  |  |  |             if not parents or self.parent == self: | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2017-04-13 20:08:15 +02:00
										 |  |  |             self.parent.mkdir(parents=True, exist_ok=True) | 
					
						
							|  |  |  |             self.mkdir(mode, parents=False, exist_ok=exist_ok) | 
					
						
							| 
									
										
										
										
											2017-03-24 20:51:53 +02:00
										 |  |  |         except OSError: | 
					
						
							|  |  |  |             # Cannot rely on checking for EEXIST, since the operating system | 
					
						
							|  |  |  |             # could give priority to other errors like EACCES or EROFS | 
					
						
							|  |  |  |             if not exist_ok or not self.is_dir(): | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def chmod(self, mode): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Change the permissions of the path, like os.chmod(). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.chmod(self, mode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def lchmod(self, mode): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Like chmod(), except if the path points to a symlink, the symlink's | 
					
						
							|  |  |  |         permissions are changed, rather than its target's. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.lchmod(self, mode) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 00:02:11 +02:00
										 |  |  |     def unlink(self, missing_ok=False): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         Remove this file or link. | 
					
						
							|  |  |  |         If the path is a directory, use rmdir() instead. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2019-05-16 00:02:11 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._accessor.unlink(self) | 
					
						
							|  |  |  |         except FileNotFoundError: | 
					
						
							|  |  |  |             if not missing_ok: | 
					
						
							|  |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def rmdir(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Remove this directory.  The directory must be empty. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.rmdir(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def lstat(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Like stat(), except if the path points to a symlink, the symlink's | 
					
						
							|  |  |  |         status information is returned, rather than its target's. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._accessor.lstat(self) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 11:27:10 -04:00
										 |  |  |     def link_to(self, target): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Create a hard link pointing to a path named target. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.link_to(self, target) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def rename(self, target): | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-10-03 03:44:14 -07:00
										 |  |  |         Rename this path to the target path. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The target path may be absolute or relative. Relative paths are | 
					
						
							|  |  |  |         interpreted relative to the current working directory, *not* the | 
					
						
							|  |  |  |         directory of the Path object. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns the new Path instance pointing to the target path. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.rename(self, target) | 
					
						
							| 
									
										
										
										
											2019-09-11 21:26:49 +08:00
										 |  |  |         return self.__class__(target) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def replace(self, target): | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2020-10-03 03:44:14 -07:00
										 |  |  |         Rename this path to the target path, overwriting if that path exists. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The target path may be absolute or relative. Relative paths are | 
					
						
							|  |  |  |         interpreted relative to the current working directory, *not* the | 
					
						
							|  |  |  |         directory of the Path object. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns the new Path instance pointing to the target path. | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.replace(self, target) | 
					
						
							| 
									
										
										
										
											2019-09-11 21:26:49 +08:00
										 |  |  |         return self.__class__(target) | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def symlink_to(self, target, target_is_directory=False): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Make this path a symlink pointing to the given path. | 
					
						
							|  |  |  |         Note the order of arguments (self, target) is the reverse of os.symlink's. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._accessor.symlink(target, self, target_is_directory) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Convenience functions for querying the stat results | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def exists(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path exists. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.stat() | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_dir(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a directory. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISDIR(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_file(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a regular file (also True for symlinks pointing | 
					
						
							|  |  |  |         to regular files). | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISREG(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-01 15:35:45 -07:00
										 |  |  |     def is_mount(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Check if this path is a POSIX mount point | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # Need to exist and be a dir | 
					
						
							|  |  |  |         if not self.exists() or not self.is_dir(): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-04-17 18:42:06 +01:00
										 |  |  |             parent_dev = self.parent.stat().st_dev | 
					
						
							| 
									
										
										
										
											2017-08-01 15:35:45 -07:00
										 |  |  |         except OSError: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dev = self.stat().st_dev | 
					
						
							|  |  |  |         if dev != parent_dev: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         ino = self.stat().st_ino | 
					
						
							| 
									
										
										
										
											2020-04-17 18:42:06 +01:00
										 |  |  |         parent_ino = self.parent.stat().st_ino | 
					
						
							| 
									
										
										
										
											2017-08-01 15:35:45 -07:00
										 |  |  |         return ino == parent_ino | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     def is_symlink(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a symbolic link. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISLNK(self.lstat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_block_device(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a block device. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISBLK(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_char_device(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a character device. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISCHR(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_fifo(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a FIFO. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISFIFO(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def is_socket(self): | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         Whether this path is a socket. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return S_ISSOCK(self.stat().st_mode) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							| 
									
										
										
										
											2019-02-03 23:08:18 -08:00
										 |  |  |             if not _ignore_error(e): | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |                 raise | 
					
						
							|  |  |  |             # Path doesn't exist or is a broken symlink | 
					
						
							|  |  |  |             # (see https://bitbucket.org/pitrou/pathlib/issue/12/) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2018-09-18 11:28:51 +03:00
										 |  |  |         except ValueError: | 
					
						
							|  |  |  |             # Non-encodable path | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-30 20:54:45 +01:00
										 |  |  |     def expanduser(self): | 
					
						
							|  |  |  |         """ Return a new path with expanded ~ and ~user constructs
 | 
					
						
							|  |  |  |         (as returned by os.path.expanduser) | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if (not (self._drv or self._root) and | 
					
						
							|  |  |  |             self._parts and self._parts[0][:1] == '~'): | 
					
						
							|  |  |  |             homedir = self._flavour.gethomedir(self._parts[0][1:]) | 
					
						
							|  |  |  |             return self._from_parts([homedir] + self._parts[1:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class PosixPath(Path, PurePosixPath): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """Path subclass for non-Windows systems.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     On a POSIX system, instantiating a Path should return this object. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WindowsPath(Path, PureWindowsPath): | 
					
						
							| 
									
										
										
										
											2018-02-19 08:36:32 +09:00
										 |  |  |     """Path subclass for Windows systems.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     On a Windows system, instantiating a Path should return this object. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2013-11-22 17:38:12 +01:00
										 |  |  |     __slots__ = () | 
					
						
							| 
									
										
										
										
											2016-03-11 23:07:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-01 15:35:45 -07:00
										 |  |  |     def is_mount(self): | 
					
						
							|  |  |  |         raise NotImplementedError("Path.is_mount() is unsupported on this system") |