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