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