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