1import fnmatch 2import functools 3import io 4import ntpath 5import os 6import posixpath 7import re 8import sys 9from _collections_abc import Sequence 10from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP 11from operator import attrgetter 12from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO 13from urllib.parse import quote_from_bytes as urlquote_from_bytes 14 15 16supports_symlinks = True 17if os.name == 'nt': 18 import nt 19 if sys.getwindowsversion()[:2] >= (6, 0): 20 from nt import _getfinalpathname 21 else: 22 supports_symlinks = False 23 _getfinalpathname = None 24else: 25 nt = None 26 27 28__all__ = [ 29 "PurePath", "PurePosixPath", "PureWindowsPath", 30 "Path", "PosixPath", "WindowsPath", 31 ] 32 33# 34# Internals 35# 36 37# EBADF - guard against macOS `stat` throwing EBADF 38_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP) 39 40_IGNORED_WINERRORS = ( 41 21, # ERROR_NOT_READY - drive exists but is not accessible 42 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself 43) 44 45def _ignore_error(exception): 46 return (getattr(exception, 'errno', None) in _IGNORED_ERROS or 47 getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) 48 49 50def _is_wildcard_pattern(pat): 51 # Whether this pattern needs actual matching using fnmatch, or can 52 # be looked up directly as a file. 53 return "*" in pat or "?" in pat or "[" in pat 54 55 56class _Flavour(object): 57 """A flavour implements a particular (platform-specific) set of path 58 semantics.""" 59 60 def __init__(self): 61 self.join = self.sep.join 62 63 def parse_parts(self, parts): 64 parsed = [] 65 sep = self.sep 66 altsep = self.altsep 67 drv = root = '' 68 it = reversed(parts) 69 for part in it: 70 if not part: 71 continue 72 if altsep: 73 part = part.replace(altsep, sep) 74 drv, root, rel = self.splitroot(part) 75 if sep in rel: 76 for x in reversed(rel.split(sep)): 77 if x and x != '.': 78 parsed.append(sys.intern(x)) 79 else: 80 if rel and rel != '.': 81 parsed.append(sys.intern(rel)) 82 if drv or root: 83 if not drv: 84 # If no drive is present, try to find one in the previous 85 # parts. This makes the result of parsing e.g. 86 # ("C:", "/", "a") reasonably intuitive. 87 for part in it: 88 if not part: 89 continue 90 if altsep: 91 part = part.replace(altsep, sep) 92 drv = self.splitroot(part)[0] 93 if drv: 94 break 95 break 96 if drv or root: 97 parsed.append(drv + root) 98 parsed.reverse() 99 return drv, root, parsed 100 101 def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): 102 """ 103 Join the two paths represented by the respective 104 (drive, root, parts) tuples. Return a new (drive, root, parts) tuple. 105 """ 106 if root2: 107 if not drv2 and drv: 108 return drv, root2, [drv + root2] + parts2[1:] 109 elif drv2: 110 if drv2 == drv or self.casefold(drv2) == self.casefold(drv): 111 # Same drive => second path is relative to the first 112 return drv, root, parts + parts2[1:] 113 else: 114 # Second path is non-anchored (common case) 115 return drv, root, parts + parts2 116 return drv2, root2, parts2 117 118 119class _WindowsFlavour(_Flavour): 120 # Reference for Windows paths can be found at 121 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx 122 123 sep = '\\' 124 altsep = '/' 125 has_drv = True 126 pathmod = ntpath 127 128 is_supported = (os.name == 'nt') 129 130 drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') 131 ext_namespace_prefix = '\\\\?\\' 132 133 reserved_names = ( 134 {'CON', 'PRN', 'AUX', 'NUL'} | 135 {'COM%d' % i for i in range(1, 10)} | 136 {'LPT%d' % i for i in range(1, 10)} 137 ) 138 139 # Interesting findings about extended paths: 140 # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported 141 # but '\\?\c:/a' is not 142 # - extended paths are always absolute; "relative" extended paths will 143 # fail. 144 145 def splitroot(self, part, sep=sep): 146 first = part[0:1] 147 second = part[1:2] 148 if (second == sep and first == sep): 149 # XXX extended paths should also disable the collapsing of "." 150 # components (according to MSDN docs). 151 prefix, part = self._split_extended_path(part) 152 first = part[0:1] 153 second = part[1:2] 154 else: 155 prefix = '' 156 third = part[2:3] 157 if (second == sep and first == sep and third != sep): 158 # is a UNC path: 159 # vvvvvvvvvvvvvvvvvvvvv root 160 # \\machine\mountpoint\directory\etc\... 161 # directory ^^^^^^^^^^^^^^ 162 index = part.find(sep, 2) 163 if index != -1: 164 index2 = part.find(sep, index + 1) 165 # a UNC path can't have two slashes in a row 166 # (after the initial two) 167 if index2 != index + 1: 168 if index2 == -1: 169 index2 = len(part) 170 if prefix: 171 return prefix + part[1:index2], sep, part[index2+1:] 172 else: 173 return part[:index2], sep, part[index2+1:] 174 drv = root = '' 175 if second == ':' and first in self.drive_letters: 176 drv = part[:2] 177 part = part[2:] 178 first = third 179 if first == sep: 180 root = first 181 part = part.lstrip(sep) 182 return prefix + drv, root, part 183 184 def casefold(self, s): 185 return s.lower() 186 187 def casefold_parts(self, parts): 188 return [p.lower() for p in parts] 189 190 def compile_pattern(self, pattern): 191 return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch 192 193 def resolve(self, path, strict=False): 194 s = str(path) 195 if not s: 196 return os.getcwd() 197 previous_s = None 198 if _getfinalpathname is not None: 199 if strict: 200 return self._ext_to_normal(_getfinalpathname(s)) 201 else: 202 tail_parts = [] # End of the path after the first one not found 203 while True: 204 try: 205 s = self._ext_to_normal(_getfinalpathname(s)) 206 except FileNotFoundError: 207 previous_s = s 208 s, tail = os.path.split(s) 209 tail_parts.append(tail) 210 if previous_s == s: 211 return path 212 else: 213 return os.path.join(s, *reversed(tail_parts)) 214 # Means fallback on absolute 215 return None 216 217 def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): 218 prefix = '' 219 if s.startswith(ext_prefix): 220 prefix = s[:4] 221 s = s[4:] 222 if s.startswith('UNC\\'): 223 prefix += s[:3] 224 s = '\\' + s[3:] 225 return prefix, s 226 227 def _ext_to_normal(self, s): 228 # Turn back an extended path into a normal DOS-like path 229 return self._split_extended_path(s)[1] 230 231 def is_reserved(self, parts): 232 # NOTE: the rules for reserved names seem somewhat complicated 233 # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). 234 # We err on the side of caution and return True for paths which are 235 # not considered reserved by Windows. 236 if not parts: 237 return False 238 if parts[0].startswith('\\\\'): 239 # UNC paths are never reserved 240 return False 241 return parts[-1].partition('.')[0].upper() in self.reserved_names 242 243 def make_uri(self, path): 244 # Under Windows, file URIs use the UTF-8 encoding. 245 drive = path.drive 246 if len(drive) == 2 and drive[1] == ':': 247 # It's a path on a local drive => 'file:///c:/a/b' 248 rest = path.as_posix()[2:].lstrip('/') 249 return 'file:///%s/%s' % ( 250 drive, urlquote_from_bytes(rest.encode('utf-8'))) 251 else: 252 # It's a path on a network drive => 'file://host/share/a/b' 253 return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8')) 254 255 def gethomedir(self, username): 256 if 'USERPROFILE' in os.environ: 257 userhome = os.environ['USERPROFILE'] 258 elif 'HOMEPATH' in os.environ: 259 try: 260 drv = os.environ['HOMEDRIVE'] 261 except KeyError: 262 drv = '' 263 userhome = drv + os.environ['HOMEPATH'] 264 else: 265 raise RuntimeError("Can't determine home directory") 266 267 if username: 268 # Try to guess user home directory. By default all users 269 # directories are located in the same place and are named by 270 # corresponding usernames. If current user home directory points 271 # to nonstandard place, this guess is likely wrong. 272 if os.environ['USERNAME'] != username: 273 drv, root, parts = self.parse_parts((userhome,)) 274 if parts[-1] != os.environ['USERNAME']: 275 raise RuntimeError("Can't determine home directory " 276 "for %r" % username) 277 parts[-1] = username 278 if drv or root: 279 userhome = drv + root + self.join(parts[1:]) 280 else: 281 userhome = self.join(parts) 282 return userhome 283 284class _PosixFlavour(_Flavour): 285 sep = '/' 286 altsep = '' 287 has_drv = False 288 pathmod = posixpath 289 290 is_supported = (os.name != 'nt') 291 292 def splitroot(self, part, sep=sep): 293 if part and part[0] == sep: 294 stripped_part = part.lstrip(sep) 295 # According to POSIX path resolution: 296 # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 297 # "A pathname that begins with two successive slashes may be 298 # interpreted in an implementation-defined manner, although more 299 # than two leading slashes shall be treated as a single slash". 300 if len(part) - len(stripped_part) == 2: 301 return '', sep * 2, stripped_part 302 else: 303 return '', sep, stripped_part 304 else: 305 return '', '', part 306 307 def casefold(self, s): 308 return s 309 310 def casefold_parts(self, parts): 311 return parts 312 313 def compile_pattern(self, pattern): 314 return re.compile(fnmatch.translate(pattern)).fullmatch 315 316 def resolve(self, path, strict=False): 317 sep = self.sep 318 accessor = path._accessor 319 seen = {} 320 def _resolve(path, rest): 321 if rest.startswith(sep): 322 path = '' 323 324 for name in rest.split(sep): 325 if not name or name == '.': 326 # current dir 327 continue 328 if name == '..': 329 # parent dir 330 path, _, _ = path.rpartition(sep) 331 continue 332 if path.endswith(sep): 333 newpath = path + name 334 else: 335 newpath = path + sep + name 336 if newpath in seen: 337 # Already seen this path 338 path = seen[newpath] 339 if path is not None: 340 # use cached value 341 continue 342 # The symlink is not resolved, so we must have a symlink loop. 343 raise RuntimeError("Symlink loop from %r" % newpath) 344 # Resolve the symbolic link 345 try: 346 target = accessor.readlink(newpath) 347 except OSError as e: 348 if e.errno != EINVAL and strict: 349 raise 350 # Not a symlink, or non-strict mode. We just leave the path 351 # untouched. 352 path = newpath 353 else: 354 seen[newpath] = None # not resolved symlink 355 path = _resolve(path, target) 356 seen[newpath] = path # resolved symlink 357 358 return path 359 # NOTE: according to POSIX, getcwd() cannot contain path components 360 # which are symlinks. 361 base = '' if path.is_absolute() else os.getcwd() 362 return _resolve(base, str(path)) or sep 363 364 def is_reserved(self, parts): 365 return False 366 367 def make_uri(self, path): 368 # We represent the path using the local filesystem encoding, 369 # for portability to other applications. 370 bpath = bytes(path) 371 return 'file://' + urlquote_from_bytes(bpath) 372 373 def gethomedir(self, username): 374 if not username: 375 try: 376 return os.environ['HOME'] 377 except KeyError: 378 import pwd 379 return pwd.getpwuid(os.getuid()).pw_dir 380 else: 381 import pwd 382 try: 383 return pwd.getpwnam(username).pw_dir 384 except KeyError: 385 raise RuntimeError("Can't determine home directory " 386 "for %r" % username) 387 388 389_windows_flavour = _WindowsFlavour() 390_posix_flavour = _PosixFlavour() 391 392 393class _Accessor: 394 """An accessor implements a particular (system-specific or not) way of 395 accessing paths on the filesystem.""" 396 397 398class _NormalAccessor(_Accessor): 399 400 stat = os.stat 401 402 lstat = os.lstat 403 404 open = os.open 405 406 listdir = os.listdir 407 408 scandir = os.scandir 409 410 chmod = os.chmod 411 412 if hasattr(os, "lchmod"): 413 lchmod = os.lchmod 414 else: 415 def lchmod(self, pathobj, mode): 416 raise NotImplementedError("lchmod() not available on this system") 417 418 mkdir = os.mkdir 419 420 unlink = os.unlink 421 422 if hasattr(os, "link"): 423 link_to = os.link 424 else: 425 @staticmethod 426 def link_to(self, target): 427 raise NotImplementedError("os.link() not available on this system") 428 429 rmdir = os.rmdir 430 431 rename = os.rename 432 433 replace = os.replace 434 435 if nt: 436 if supports_symlinks: 437 symlink = os.symlink 438 else: 439 def symlink(a, b, target_is_directory): 440 raise NotImplementedError("symlink() not available on this system") 441 else: 442 # Under POSIX, os.symlink() takes two args 443 @staticmethod 444 def symlink(a, b, target_is_directory): 445 return os.symlink(a, b) 446 447 utime = os.utime 448 449 # Helper for resolve() 450 def readlink(self, path): 451 return os.readlink(path) 452 453 def owner(self, path): 454 try: 455 import pwd 456 return pwd.getpwuid(self.stat(path).st_uid).pw_name 457 except ImportError: 458 raise NotImplementedError("Path.owner() is unsupported on this system") 459 460 def group(self, path): 461 try: 462 import grp 463 return grp.getgrgid(self.stat(path).st_gid).gr_name 464 except ImportError: 465 raise NotImplementedError("Path.group() is unsupported on this system") 466 467 468_normal_accessor = _NormalAccessor() 469 470 471# 472# Globbing helpers 473# 474 475def _make_selector(pattern_parts, flavour): 476 pat = pattern_parts[0] 477 child_parts = pattern_parts[1:] 478 if pat == '**': 479 cls = _RecursiveWildcardSelector 480 elif '**' in pat: 481 raise ValueError("Invalid pattern: '**' can only be an entire path component") 482 elif _is_wildcard_pattern(pat): 483 cls = _WildcardSelector 484 else: 485 cls = _PreciseSelector 486 return cls(pat, child_parts, flavour) 487 488if hasattr(functools, "lru_cache"): 489 _make_selector = functools.lru_cache()(_make_selector) 490 491 492class _Selector: 493 """A selector matches a specific glob pattern part against the children 494 of a given path.""" 495 496 def __init__(self, child_parts, flavour): 497 self.child_parts = child_parts 498 if child_parts: 499 self.successor = _make_selector(child_parts, flavour) 500 self.dironly = True 501 else: 502 self.successor = _TerminatingSelector() 503 self.dironly = False 504 505 def select_from(self, parent_path): 506 """Iterate over all child paths of `parent_path` matched by this 507 selector. This can contain parent_path itself.""" 508 path_cls = type(parent_path) 509 is_dir = path_cls.is_dir 510 exists = path_cls.exists 511 scandir = parent_path._accessor.scandir 512 if not is_dir(parent_path): 513 return iter([]) 514 return self._select_from(parent_path, is_dir, exists, scandir) 515 516 517class _TerminatingSelector: 518 519 def _select_from(self, parent_path, is_dir, exists, scandir): 520 yield parent_path 521 522 523class _PreciseSelector(_Selector): 524 525 def __init__(self, name, child_parts, flavour): 526 self.name = name 527 _Selector.__init__(self, child_parts, flavour) 528 529 def _select_from(self, parent_path, is_dir, exists, scandir): 530 try: 531 path = parent_path._make_child_relpath(self.name) 532 if (is_dir if self.dironly else exists)(path): 533 for p in self.successor._select_from(path, is_dir, exists, scandir): 534 yield p 535 except PermissionError: 536 return 537 538 539class _WildcardSelector(_Selector): 540 541 def __init__(self, pat, child_parts, flavour): 542 self.match = flavour.compile_pattern(pat) 543 _Selector.__init__(self, child_parts, flavour) 544 545 def _select_from(self, parent_path, is_dir, exists, scandir): 546 try: 547 with scandir(parent_path) as scandir_it: 548 entries = list(scandir_it) 549 for entry in entries: 550 if self.dironly: 551 try: 552 # "entry.is_dir()" can raise PermissionError 553 # in some cases (see bpo-38894), which is not 554 # among the errors ignored by _ignore_error() 555 if not entry.is_dir(): 556 continue 557 except OSError as e: 558 if not _ignore_error(e): 559 raise 560 continue 561 name = entry.name 562 if self.match(name): 563 path = parent_path._make_child_relpath(name) 564 for p in self.successor._select_from(path, is_dir, exists, scandir): 565 yield p 566 except PermissionError: 567 return 568 569 570class _RecursiveWildcardSelector(_Selector): 571 572 def __init__(self, pat, child_parts, flavour): 573 _Selector.__init__(self, child_parts, flavour) 574 575 def _iterate_directories(self, parent_path, is_dir, scandir): 576 yield parent_path 577 try: 578 with scandir(parent_path) as scandir_it: 579 entries = list(scandir_it) 580 for entry in entries: 581 entry_is_dir = False 582 try: 583 entry_is_dir = entry.is_dir() 584 except OSError as e: 585 if not _ignore_error(e): 586 raise 587 if entry_is_dir and not entry.is_symlink(): 588 path = parent_path._make_child_relpath(entry.name) 589 for p in self._iterate_directories(path, is_dir, scandir): 590 yield p 591 except PermissionError: 592 return 593 594 def _select_from(self, parent_path, is_dir, exists, scandir): 595 try: 596 yielded = set() 597 try: 598 successor_select = self.successor._select_from 599 for starting_point in self._iterate_directories(parent_path, is_dir, scandir): 600 for p in successor_select(starting_point, is_dir, exists, scandir): 601 if p not in yielded: 602 yield p 603 yielded.add(p) 604 finally: 605 yielded.clear() 606 except PermissionError: 607 return 608 609 610# 611# Public API 612# 613 614class _PathParents(Sequence): 615 """This object provides sequence-like access to the logical ancestors 616 of a path. Don't try to construct it yourself.""" 617 __slots__ = ('_pathcls', '_drv', '_root', '_parts') 618 619 def __init__(self, path): 620 # We don't store the instance to avoid reference cycles 621 self._pathcls = type(path) 622 self._drv = path._drv 623 self._root = path._root 624 self._parts = path._parts 625 626 def __len__(self): 627 if self._drv or self._root: 628 return len(self._parts) - 1 629 else: 630 return len(self._parts) 631 632 def __getitem__(self, idx): 633 if idx < 0 or idx >= len(self): 634 raise IndexError(idx) 635 return self._pathcls._from_parsed_parts(self._drv, self._root, 636 self._parts[:-idx - 1]) 637 638 def __repr__(self): 639 return "<{}.parents>".format(self._pathcls.__name__) 640 641 642class PurePath(object): 643 """Base class for manipulating paths without I/O. 644 645 PurePath represents a filesystem path and offers operations which 646 don't imply any actual filesystem I/O. Depending on your system, 647 instantiating a PurePath will return either a PurePosixPath or a 648 PureWindowsPath object. You can also instantiate either of these classes 649 directly, regardless of your system. 650 """ 651 __slots__ = ( 652 '_drv', '_root', '_parts', 653 '_str', '_hash', '_pparts', '_cached_cparts', 654 ) 655 656 def __new__(cls, *args): 657 """Construct a PurePath from one or several strings and or existing 658 PurePath objects. The strings and path objects are combined so as 659 to yield a canonicalized path, which is incorporated into the 660 new PurePath object. 661 """ 662 if cls is PurePath: 663 cls = PureWindowsPath if os.name == 'nt' else PurePosixPath 664 return cls._from_parts(args) 665 666 def __reduce__(self): 667 # Using the parts tuple helps share interned path parts 668 # when pickling related paths. 669 return (self.__class__, tuple(self._parts)) 670 671 @classmethod 672 def _parse_args(cls, args): 673 # This is useful when you don't want to create an instance, just 674 # canonicalize some constructor arguments. 675 parts = [] 676 for a in args: 677 if isinstance(a, PurePath): 678 parts += a._parts 679 else: 680 a = os.fspath(a) 681 if isinstance(a, str): 682 # Force-cast str subclasses to str (issue #21127) 683 parts.append(str(a)) 684 else: 685 raise TypeError( 686 "argument should be a str object or an os.PathLike " 687 "object returning str, not %r" 688 % type(a)) 689 return cls._flavour.parse_parts(parts) 690 691 @classmethod 692 def _from_parts(cls, args, init=True): 693 # We need to call _parse_args on the instance, so as to get the 694 # right flavour. 695 self = object.__new__(cls) 696 drv, root, parts = self._parse_args(args) 697 self._drv = drv 698 self._root = root 699 self._parts = parts 700 if init: 701 self._init() 702 return self 703 704 @classmethod 705 def _from_parsed_parts(cls, drv, root, parts, init=True): 706 self = object.__new__(cls) 707 self._drv = drv 708 self._root = root 709 self._parts = parts 710 if init: 711 self._init() 712 return self 713 714 @classmethod 715 def _format_parsed_parts(cls, drv, root, parts): 716 if drv or root: 717 return drv + root + cls._flavour.join(parts[1:]) 718 else: 719 return cls._flavour.join(parts) 720 721 def _init(self): 722 # Overridden in concrete Path 723 pass 724 725 def _make_child(self, args): 726 drv, root, parts = self._parse_args(args) 727 drv, root, parts = self._flavour.join_parsed_parts( 728 self._drv, self._root, self._parts, drv, root, parts) 729 return self._from_parsed_parts(drv, root, parts) 730 731 def __str__(self): 732 """Return the string representation of the path, suitable for 733 passing to system calls.""" 734 try: 735 return self._str 736 except AttributeError: 737 self._str = self._format_parsed_parts(self._drv, self._root, 738 self._parts) or '.' 739 return self._str 740 741 def __fspath__(self): 742 return str(self) 743 744 def as_posix(self): 745 """Return the string representation of the path with forward (/) 746 slashes.""" 747 f = self._flavour 748 return str(self).replace(f.sep, '/') 749 750 def __bytes__(self): 751 """Return the bytes representation of the path. This is only 752 recommended to use under Unix.""" 753 return os.fsencode(self) 754 755 def __repr__(self): 756 return "{}({!r})".format(self.__class__.__name__, self.as_posix()) 757 758 def as_uri(self): 759 """Return the path as a 'file' URI.""" 760 if not self.is_absolute(): 761 raise ValueError("relative path can't be expressed as a file URI") 762 return self._flavour.make_uri(self) 763 764 @property 765 def _cparts(self): 766 # Cached casefolded parts, for hashing and comparison 767 try: 768 return self._cached_cparts 769 except AttributeError: 770 self._cached_cparts = self._flavour.casefold_parts(self._parts) 771 return self._cached_cparts 772 773 def __eq__(self, other): 774 if not isinstance(other, PurePath): 775 return NotImplemented 776 return self._cparts == other._cparts and self._flavour is other._flavour 777 778 def __hash__(self): 779 try: 780 return self._hash 781 except AttributeError: 782 self._hash = hash(tuple(self._cparts)) 783 return self._hash 784 785 def __lt__(self, other): 786 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 787 return NotImplemented 788 return self._cparts < other._cparts 789 790 def __le__(self, other): 791 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 792 return NotImplemented 793 return self._cparts <= other._cparts 794 795 def __gt__(self, other): 796 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 797 return NotImplemented 798 return self._cparts > other._cparts 799 800 def __ge__(self, other): 801 if not isinstance(other, PurePath) or self._flavour is not other._flavour: 802 return NotImplemented 803 return self._cparts >= other._cparts 804 805 def __class_getitem__(cls, type): 806 return cls 807 808 drive = property(attrgetter('_drv'), 809 doc="""The drive prefix (letter or UNC path), if any.""") 810 811 root = property(attrgetter('_root'), 812 doc="""The root of the path, if any.""") 813 814 @property 815 def anchor(self): 816 """The concatenation of the drive and root, or ''.""" 817 anchor = self._drv + self._root 818 return anchor 819 820 @property 821 def name(self): 822 """The final path component, if any.""" 823 parts = self._parts 824 if len(parts) == (1 if (self._drv or self._root) else 0): 825 return '' 826 return parts[-1] 827 828 @property 829 def suffix(self): 830 """ 831 The final component's last suffix, if any. 832 833 This includes the leading period. For example: '.txt' 834 """ 835 name = self.name 836 i = name.rfind('.') 837 if 0 < i < len(name) - 1: 838 return name[i:] 839 else: 840 return '' 841 842 @property 843 def suffixes(self): 844 """ 845 A list of the final component's suffixes, if any. 846 847 These include the leading periods. For example: ['.tar', '.gz'] 848 """ 849 name = self.name 850 if name.endswith('.'): 851 return [] 852 name = name.lstrip('.') 853 return ['.' + suffix for suffix in name.split('.')[1:]] 854 855 @property 856 def stem(self): 857 """The final path component, minus its last suffix.""" 858 name = self.name 859 i = name.rfind('.') 860 if 0 < i < len(name) - 1: 861 return name[:i] 862 else: 863 return name 864 865 def with_name(self, name): 866 """Return a new path with the file name changed.""" 867 if not self.name: 868 raise ValueError("%r has an empty name" % (self,)) 869 drv, root, parts = self._flavour.parse_parts((name,)) 870 if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] 871 or drv or root or len(parts) != 1): 872 raise ValueError("Invalid name %r" % (name)) 873 return self._from_parsed_parts(self._drv, self._root, 874 self._parts[:-1] + [name]) 875 876 def with_stem(self, stem): 877 """Return a new path with the stem changed.""" 878 return self.with_name(stem + self.suffix) 879 880 def with_suffix(self, suffix): 881 """Return a new path with the file suffix changed. If the path 882 has no suffix, add given suffix. If the given suffix is an empty 883 string, remove the suffix from the path. 884 """ 885 f = self._flavour 886 if f.sep in suffix or f.altsep and f.altsep in suffix: 887 raise ValueError("Invalid suffix %r" % (suffix,)) 888 if suffix and not suffix.startswith('.') or suffix == '.': 889 raise ValueError("Invalid suffix %r" % (suffix)) 890 name = self.name 891 if not name: 892 raise ValueError("%r has an empty name" % (self,)) 893 old_suffix = self.suffix 894 if not old_suffix: 895 name = name + suffix 896 else: 897 name = name[:-len(old_suffix)] + suffix 898 return self._from_parsed_parts(self._drv, self._root, 899 self._parts[:-1] + [name]) 900 901 def relative_to(self, *other): 902 """Return the relative path to another path identified by the passed 903 arguments. If the operation is not possible (because this is not 904 a subpath of the other path), raise ValueError. 905 """ 906 # For the purpose of this method, drive and root are considered 907 # separate parts, i.e.: 908 # Path('c:/').relative_to('c:') gives Path('/') 909 # Path('c:/').relative_to('/') raise ValueError 910 if not other: 911 raise TypeError("need at least one argument") 912 parts = self._parts 913 drv = self._drv 914 root = self._root 915 if root: 916 abs_parts = [drv, root] + parts[1:] 917 else: 918 abs_parts = parts 919 to_drv, to_root, to_parts = self._parse_args(other) 920 if to_root: 921 to_abs_parts = [to_drv, to_root] + to_parts[1:] 922 else: 923 to_abs_parts = to_parts 924 n = len(to_abs_parts) 925 cf = self._flavour.casefold_parts 926 if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): 927 formatted = self._format_parsed_parts(to_drv, to_root, to_parts) 928 raise ValueError("{!r} is not in the subpath of {!r}" 929 " OR one path is relative and the other is absolute." 930 .format(str(self), str(formatted))) 931 return self._from_parsed_parts('', root if n == 1 else '', 932 abs_parts[n:]) 933 934 def is_relative_to(self, *other): 935 """Return True if the path is relative to another path or False. 936 """ 937 try: 938 self.relative_to(*other) 939 return True 940 except ValueError: 941 return False 942 943 @property 944 def parts(self): 945 """An object providing sequence-like access to the 946 components in the filesystem path.""" 947 # We cache the tuple to avoid building a new one each time .parts 948 # is accessed. XXX is this necessary? 949 try: 950 return self._pparts 951 except AttributeError: 952 self._pparts = tuple(self._parts) 953 return self._pparts 954 955 def joinpath(self, *args): 956 """Combine this path with one or several arguments, and return a 957 new path representing either a subpath (if all arguments are relative 958 paths) or a totally different path (if one of the arguments is 959 anchored). 960 """ 961 return self._make_child(args) 962 963 def __truediv__(self, key): 964 try: 965 return self._make_child((key,)) 966 except TypeError: 967 return NotImplemented 968 969 def __rtruediv__(self, key): 970 try: 971 return self._from_parts([key] + self._parts) 972 except TypeError: 973 return NotImplemented 974 975 @property 976 def parent(self): 977 """The logical parent of the path.""" 978 drv = self._drv 979 root = self._root 980 parts = self._parts 981 if len(parts) == 1 and (drv or root): 982 return self 983 return self._from_parsed_parts(drv, root, parts[:-1]) 984 985 @property 986 def parents(self): 987 """A sequence of this path's logical parents.""" 988 return _PathParents(self) 989 990 def is_absolute(self): 991 """True if the path is absolute (has both a root and, if applicable, 992 a drive).""" 993 if not self._root: 994 return False 995 return not self._flavour.has_drv or bool(self._drv) 996 997 def is_reserved(self): 998 """Return True if the path contains one of the special names reserved 999 by the system, if any.""" 1000 return self._flavour.is_reserved(self._parts) 1001 1002 def match(self, path_pattern): 1003 """ 1004 Return True if this path matches the given pattern. 1005 """ 1006 cf = self._flavour.casefold 1007 path_pattern = cf(path_pattern) 1008 drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) 1009 if not pat_parts: 1010 raise ValueError("empty pattern") 1011 if drv and drv != cf(self._drv): 1012 return False 1013 if root and root != cf(self._root): 1014 return False 1015 parts = self._cparts 1016 if drv or root: 1017 if len(pat_parts) != len(parts): 1018 return False 1019 pat_parts = pat_parts[1:] 1020 elif len(pat_parts) > len(parts): 1021 return False 1022 for part, pat in zip(reversed(parts), reversed(pat_parts)): 1023 if not fnmatch.fnmatchcase(part, pat): 1024 return False 1025 return True 1026 1027# Can't subclass os.PathLike from PurePath and keep the constructor 1028# optimizations in PurePath._parse_args(). 1029os.PathLike.register(PurePath) 1030 1031 1032class PurePosixPath(PurePath): 1033 """PurePath subclass for non-Windows systems. 1034 1035 On a POSIX system, instantiating a PurePath should return this object. 1036 However, you can also instantiate it directly on any system. 1037 """ 1038 _flavour = _posix_flavour 1039 __slots__ = () 1040 1041 1042class PureWindowsPath(PurePath): 1043 """PurePath subclass for Windows systems. 1044 1045 On a Windows system, instantiating a PurePath should return this object. 1046 However, you can also instantiate it directly on any system. 1047 """ 1048 _flavour = _windows_flavour 1049 __slots__ = () 1050 1051 1052# Filesystem-accessing classes 1053 1054 1055class Path(PurePath): 1056 """PurePath subclass that can make system calls. 1057 1058 Path represents a filesystem path but unlike PurePath, also offers 1059 methods to do system calls on path objects. Depending on your system, 1060 instantiating a Path will return either a PosixPath or a WindowsPath 1061 object. You can also instantiate a PosixPath or WindowsPath directly, 1062 but cannot instantiate a WindowsPath on a POSIX system or vice versa. 1063 """ 1064 __slots__ = ( 1065 '_accessor', 1066 ) 1067 1068 def __new__(cls, *args, **kwargs): 1069 if cls is Path: 1070 cls = WindowsPath if os.name == 'nt' else PosixPath 1071 self = cls._from_parts(args, init=False) 1072 if not self._flavour.is_supported: 1073 raise NotImplementedError("cannot instantiate %r on your system" 1074 % (cls.__name__,)) 1075 self._init() 1076 return self 1077 1078 def _init(self, 1079 # Private non-constructor arguments 1080 template=None, 1081 ): 1082 if template is not None: 1083 self._accessor = template._accessor 1084 else: 1085 self._accessor = _normal_accessor 1086 1087 def _make_child_relpath(self, part): 1088 # This is an optimization used for dir walking. `part` must be 1089 # a single part relative to this path. 1090 parts = self._parts + [part] 1091 return self._from_parsed_parts(self._drv, self._root, parts) 1092 1093 def __enter__(self): 1094 return self 1095 1096 def __exit__(self, t, v, tb): 1097 # https://bugs.python.org/issue39682 1098 # In previous versions of pathlib, this method marked this path as 1099 # closed; subsequent attempts to perform I/O would raise an IOError. 1100 # This functionality was never documented, and had the effect of 1101 # making Path objects mutable, contrary to PEP 428. In Python 3.9 the 1102 # _closed attribute was removed, and this method made a no-op. 1103 # This method and __enter__()/__exit__() should be deprecated and 1104 # removed in the future. 1105 pass 1106 1107 def _opener(self, name, flags, mode=0o666): 1108 # A stub for the opener argument to built-in open() 1109 return self._accessor.open(self, flags, mode) 1110 1111 def _raw_open(self, flags, mode=0o777): 1112 """ 1113 Open the file pointed by this path and return a file descriptor, 1114 as os.open() does. 1115 """ 1116 return self._accessor.open(self, flags, mode) 1117 1118 # Public API 1119 1120 @classmethod 1121 def cwd(cls): 1122 """Return a new path pointing to the current working directory 1123 (as returned by os.getcwd()). 1124 """ 1125 return cls(os.getcwd()) 1126 1127 @classmethod 1128 def home(cls): 1129 """Return a new path pointing to the user's home directory (as 1130 returned by os.path.expanduser('~')). 1131 """ 1132 return cls(cls()._flavour.gethomedir(None)) 1133 1134 def samefile(self, other_path): 1135 """Return whether other_path is the same or not as this file 1136 (as returned by os.path.samefile()). 1137 """ 1138 st = self.stat() 1139 try: 1140 other_st = other_path.stat() 1141 except AttributeError: 1142 other_st = self._accessor.stat(other_path) 1143 return os.path.samestat(st, other_st) 1144 1145 def iterdir(self): 1146 """Iterate over the files in this directory. Does not yield any 1147 result for the special paths '.' and '..'. 1148 """ 1149 for name in self._accessor.listdir(self): 1150 if name in {'.', '..'}: 1151 # Yielding a path object for these makes little sense 1152 continue 1153 yield self._make_child_relpath(name) 1154 1155 def glob(self, pattern): 1156 """Iterate over this subtree and yield all existing files (of any 1157 kind, including directories) matching the given relative pattern. 1158 """ 1159 sys.audit("pathlib.Path.glob", self, pattern) 1160 if not pattern: 1161 raise ValueError("Unacceptable pattern: {!r}".format(pattern)) 1162 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) 1163 if drv or root: 1164 raise NotImplementedError("Non-relative patterns are unsupported") 1165 selector = _make_selector(tuple(pattern_parts), self._flavour) 1166 for p in selector.select_from(self): 1167 yield p 1168 1169 def rglob(self, pattern): 1170 """Recursively yield all existing files (of any kind, including 1171 directories) matching the given relative pattern, anywhere in 1172 this subtree. 1173 """ 1174 sys.audit("pathlib.Path.rglob", self, pattern) 1175 drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) 1176 if drv or root: 1177 raise NotImplementedError("Non-relative patterns are unsupported") 1178 selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour) 1179 for p in selector.select_from(self): 1180 yield p 1181 1182 def absolute(self): 1183 """Return an absolute version of this path. This function works 1184 even if the path doesn't point to anything. 1185 1186 No normalization is done, i.e. all '.' and '..' will be kept along. 1187 Use resolve() to get the canonical path to a file. 1188 """ 1189 # XXX untested yet! 1190 if self.is_absolute(): 1191 return self 1192 # FIXME this must defer to the specific flavour (and, under Windows, 1193 # use nt._getfullpathname()) 1194 obj = self._from_parts([os.getcwd()] + self._parts, init=False) 1195 obj._init(template=self) 1196 return obj 1197 1198 def resolve(self, strict=False): 1199 """ 1200 Make the path absolute, resolving all symlinks on the way and also 1201 normalizing it (for example turning slashes into backslashes under 1202 Windows). 1203 """ 1204 s = self._flavour.resolve(self, strict=strict) 1205 if s is None: 1206 # No symlink resolution => for consistency, raise an error if 1207 # the path doesn't exist or is forbidden 1208 self.stat() 1209 s = str(self.absolute()) 1210 # Now we have no symlinks in the path, it's safe to normalize it. 1211 normed = self._flavour.pathmod.normpath(s) 1212 obj = self._from_parts((normed,), init=False) 1213 obj._init(template=self) 1214 return obj 1215 1216 def stat(self): 1217 """ 1218 Return the result of the stat() system call on this path, like 1219 os.stat() does. 1220 """ 1221 return self._accessor.stat(self) 1222 1223 def owner(self): 1224 """ 1225 Return the login name of the file owner. 1226 """ 1227 return self._accessor.owner(self) 1228 1229 def group(self): 1230 """ 1231 Return the group name of the file gid. 1232 """ 1233 return self._accessor.group(self) 1234 1235 def open(self, mode='r', buffering=-1, encoding=None, 1236 errors=None, newline=None): 1237 """ 1238 Open the file pointed by this path and return a file object, as 1239 the built-in open() function does. 1240 """ 1241 return io.open(self, mode, buffering, encoding, errors, newline, 1242 opener=self._opener) 1243 1244 def read_bytes(self): 1245 """ 1246 Open the file in bytes mode, read it, and close the file. 1247 """ 1248 with self.open(mode='rb') as f: 1249 return f.read() 1250 1251 def read_text(self, encoding=None, errors=None): 1252 """ 1253 Open the file in text mode, read it, and close the file. 1254 """ 1255 with self.open(mode='r', encoding=encoding, errors=errors) as f: 1256 return f.read() 1257 1258 def write_bytes(self, data): 1259 """ 1260 Open the file in bytes mode, write to it, and close the file. 1261 """ 1262 # type-check for the buffer interface before truncating the file 1263 view = memoryview(data) 1264 with self.open(mode='wb') as f: 1265 return f.write(view) 1266 1267 def write_text(self, data, encoding=None, errors=None): 1268 """ 1269 Open the file in text mode, write to it, and close the file. 1270 """ 1271 if not isinstance(data, str): 1272 raise TypeError('data must be str, not %s' % 1273 data.__class__.__name__) 1274 with self.open(mode='w', encoding=encoding, errors=errors) as f: 1275 return f.write(data) 1276 1277 def readlink(self): 1278 """ 1279 Return the path to which the symbolic link points. 1280 """ 1281 path = self._accessor.readlink(self) 1282 obj = self._from_parts((path,), init=False) 1283 obj._init(template=self) 1284 return obj 1285 1286 def touch(self, mode=0o666, exist_ok=True): 1287 """ 1288 Create this file with the given access mode, if it doesn't exist. 1289 """ 1290 if exist_ok: 1291 # First try to bump modification time 1292 # Implementation note: GNU touch uses the UTIME_NOW option of 1293 # the utimensat() / futimens() functions. 1294 try: 1295 self._accessor.utime(self, None) 1296 except OSError: 1297 # Avoid exception chaining 1298 pass 1299 else: 1300 return 1301 flags = os.O_CREAT | os.O_WRONLY 1302 if not exist_ok: 1303 flags |= os.O_EXCL 1304 fd = self._raw_open(flags, mode) 1305 os.close(fd) 1306 1307 def mkdir(self, mode=0o777, parents=False, exist_ok=False): 1308 """ 1309 Create a new directory at this given path. 1310 """ 1311 try: 1312 self._accessor.mkdir(self, mode) 1313 except FileNotFoundError: 1314 if not parents or self.parent == self: 1315 raise 1316 self.parent.mkdir(parents=True, exist_ok=True) 1317 self.mkdir(mode, parents=False, exist_ok=exist_ok) 1318 except OSError: 1319 # Cannot rely on checking for EEXIST, since the operating system 1320 # could give priority to other errors like EACCES or EROFS 1321 if not exist_ok or not self.is_dir(): 1322 raise 1323 1324 def chmod(self, mode): 1325 """ 1326 Change the permissions of the path, like os.chmod(). 1327 """ 1328 self._accessor.chmod(self, mode) 1329 1330 def lchmod(self, mode): 1331 """ 1332 Like chmod(), except if the path points to a symlink, the symlink's 1333 permissions are changed, rather than its target's. 1334 """ 1335 self._accessor.lchmod(self, mode) 1336 1337 def unlink(self, missing_ok=False): 1338 """ 1339 Remove this file or link. 1340 If the path is a directory, use rmdir() instead. 1341 """ 1342 try: 1343 self._accessor.unlink(self) 1344 except FileNotFoundError: 1345 if not missing_ok: 1346 raise 1347 1348 def rmdir(self): 1349 """ 1350 Remove this directory. The directory must be empty. 1351 """ 1352 self._accessor.rmdir(self) 1353 1354 def lstat(self): 1355 """ 1356 Like stat(), except if the path points to a symlink, the symlink's 1357 status information is returned, rather than its target's. 1358 """ 1359 return self._accessor.lstat(self) 1360 1361 def link_to(self, target): 1362 """ 1363 Create a hard link pointing to a path named target. 1364 """ 1365 self._accessor.link_to(self, target) 1366 1367 def rename(self, target): 1368 """ 1369 Rename this path to the target path. 1370 1371 The target path may be absolute or relative. Relative paths are 1372 interpreted relative to the current working directory, *not* the 1373 directory of the Path object. 1374 1375 Returns the new Path instance pointing to the target path. 1376 """ 1377 self._accessor.rename(self, target) 1378 return self.__class__(target) 1379 1380 def replace(self, target): 1381 """ 1382 Rename this path to the target path, overwriting if that path exists. 1383 1384 The target path may be absolute or relative. Relative paths are 1385 interpreted relative to the current working directory, *not* the 1386 directory of the Path object. 1387 1388 Returns the new Path instance pointing to the target path. 1389 """ 1390 self._accessor.replace(self, target) 1391 return self.__class__(target) 1392 1393 def symlink_to(self, target, target_is_directory=False): 1394 """ 1395 Make this path a symlink pointing to the given path. 1396 Note the order of arguments (self, target) is the reverse of os.symlink's. 1397 """ 1398 self._accessor.symlink(target, self, target_is_directory) 1399 1400 # Convenience functions for querying the stat results 1401 1402 def exists(self): 1403 """ 1404 Whether this path exists. 1405 """ 1406 try: 1407 self.stat() 1408 except OSError as e: 1409 if not _ignore_error(e): 1410 raise 1411 return False 1412 except ValueError: 1413 # Non-encodable path 1414 return False 1415 return True 1416 1417 def is_dir(self): 1418 """ 1419 Whether this path is a directory. 1420 """ 1421 try: 1422 return S_ISDIR(self.stat().st_mode) 1423 except OSError as e: 1424 if not _ignore_error(e): 1425 raise 1426 # Path doesn't exist or is a broken symlink 1427 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1428 return False 1429 except ValueError: 1430 # Non-encodable path 1431 return False 1432 1433 def is_file(self): 1434 """ 1435 Whether this path is a regular file (also True for symlinks pointing 1436 to regular files). 1437 """ 1438 try: 1439 return S_ISREG(self.stat().st_mode) 1440 except OSError as e: 1441 if not _ignore_error(e): 1442 raise 1443 # Path doesn't exist or is a broken symlink 1444 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1445 return False 1446 except ValueError: 1447 # Non-encodable path 1448 return False 1449 1450 def is_mount(self): 1451 """ 1452 Check if this path is a POSIX mount point 1453 """ 1454 # Need to exist and be a dir 1455 if not self.exists() or not self.is_dir(): 1456 return False 1457 1458 try: 1459 parent_dev = self.parent.stat().st_dev 1460 except OSError: 1461 return False 1462 1463 dev = self.stat().st_dev 1464 if dev != parent_dev: 1465 return True 1466 ino = self.stat().st_ino 1467 parent_ino = self.parent.stat().st_ino 1468 return ino == parent_ino 1469 1470 def is_symlink(self): 1471 """ 1472 Whether this path is a symbolic link. 1473 """ 1474 try: 1475 return S_ISLNK(self.lstat().st_mode) 1476 except OSError as e: 1477 if not _ignore_error(e): 1478 raise 1479 # Path doesn't exist 1480 return False 1481 except ValueError: 1482 # Non-encodable path 1483 return False 1484 1485 def is_block_device(self): 1486 """ 1487 Whether this path is a block device. 1488 """ 1489 try: 1490 return S_ISBLK(self.stat().st_mode) 1491 except OSError as e: 1492 if not _ignore_error(e): 1493 raise 1494 # Path doesn't exist or is a broken symlink 1495 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1496 return False 1497 except ValueError: 1498 # Non-encodable path 1499 return False 1500 1501 def is_char_device(self): 1502 """ 1503 Whether this path is a character device. 1504 """ 1505 try: 1506 return S_ISCHR(self.stat().st_mode) 1507 except OSError as e: 1508 if not _ignore_error(e): 1509 raise 1510 # Path doesn't exist or is a broken symlink 1511 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1512 return False 1513 except ValueError: 1514 # Non-encodable path 1515 return False 1516 1517 def is_fifo(self): 1518 """ 1519 Whether this path is a FIFO. 1520 """ 1521 try: 1522 return S_ISFIFO(self.stat().st_mode) 1523 except OSError as e: 1524 if not _ignore_error(e): 1525 raise 1526 # Path doesn't exist or is a broken symlink 1527 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1528 return False 1529 except ValueError: 1530 # Non-encodable path 1531 return False 1532 1533 def is_socket(self): 1534 """ 1535 Whether this path is a socket. 1536 """ 1537 try: 1538 return S_ISSOCK(self.stat().st_mode) 1539 except OSError as e: 1540 if not _ignore_error(e): 1541 raise 1542 # Path doesn't exist or is a broken symlink 1543 # (see https://bitbucket.org/pitrou/pathlib/issue/12/) 1544 return False 1545 except ValueError: 1546 # Non-encodable path 1547 return False 1548 1549 def expanduser(self): 1550 """ Return a new path with expanded ~ and ~user constructs 1551 (as returned by os.path.expanduser) 1552 """ 1553 if (not (self._drv or self._root) and 1554 self._parts and self._parts[0][:1] == '~'): 1555 homedir = self._flavour.gethomedir(self._parts[0][1:]) 1556 return self._from_parts([homedir] + self._parts[1:]) 1557 1558 return self 1559 1560 1561class PosixPath(Path, PurePosixPath): 1562 """Path subclass for non-Windows systems. 1563 1564 On a POSIX system, instantiating a Path should return this object. 1565 """ 1566 __slots__ = () 1567 1568class WindowsPath(Path, PureWindowsPath): 1569 """Path subclass for Windows systems. 1570 1571 On a Windows system, instantiating a Path should return this object. 1572 """ 1573 __slots__ = () 1574 1575 def is_mount(self): 1576 raise NotImplementedError("Path.is_mount() is unsupported on this system") 1577